16ac391b3SStephan Aßmus /* 2657983b8SStephan Aßmus * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de> 3428d87c5SBarrett17 * Copyright 2018, Dario Casalinuovo 46ac391b3SStephan Aßmus * All rights reserved. Distributed under the terms of the GNU L-GPL license. 56ac391b3SStephan Aßmus */ 66ac391b3SStephan Aßmus 76ac391b3SStephan Aßmus #include "AVFormatWriter.h" 86ac391b3SStephan Aßmus 96ac391b3SStephan Aßmus #include <stdio.h> 106ac391b3SStephan Aßmus #include <string.h> 116ac391b3SStephan Aßmus #include <stdlib.h> 126ac391b3SStephan Aßmus 136ac391b3SStephan Aßmus #include <new> 146ac391b3SStephan Aßmus 15657983b8SStephan Aßmus #include <Application.h> 166ac391b3SStephan Aßmus #include <AutoDeleter.h> 176ac391b3SStephan Aßmus #include <Autolock.h> 186ac391b3SStephan Aßmus #include <ByteOrder.h> 198c767985SDario Casalinuovo #include <MediaIO.h> 206ac391b3SStephan Aßmus #include <MediaDefs.h> 216ac391b3SStephan Aßmus #include <MediaFormats.h> 22657983b8SStephan Aßmus #include <Roster.h> 236ac391b3SStephan Aßmus 246ac391b3SStephan Aßmus extern "C" { 256ac391b3SStephan Aßmus #include "avformat.h" 266ac391b3SStephan Aßmus } 276ac391b3SStephan Aßmus 286ac391b3SStephan Aßmus #include "DemuxerTable.h" 29eb01f516SStephan Aßmus #include "EncoderTable.h" 306ac391b3SStephan Aßmus #include "gfx_util.h" 316ac391b3SStephan Aßmus 326ac391b3SStephan Aßmus 33856d0bbfSStephan Aßmus //#define TRACE_AVFORMAT_WRITER 346ac391b3SStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 356ac391b3SStephan Aßmus # define TRACE printf 366ac391b3SStephan Aßmus # define TRACE_IO(a...) 373ca4a7b1SStephan Aßmus # define TRACE_PACKET printf 386ac391b3SStephan Aßmus #else 396ac391b3SStephan Aßmus # define TRACE(a...) 406ac391b3SStephan Aßmus # define TRACE_IO(a...) 416ac391b3SStephan Aßmus # define TRACE_PACKET(a...) 426ac391b3SStephan Aßmus #endif 436ac391b3SStephan Aßmus 446ac391b3SStephan Aßmus #define ERROR(a...) fprintf(stderr, a) 456ac391b3SStephan Aßmus 466ac391b3SStephan Aßmus 474384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024; 484384acf6SStephan Aßmus // TODO: This could depend on the BMediaFile creation flags, IIRC, 494384acf6SStephan Aßmus // they allow to specify a buffering mode. 504384acf6SStephan Aßmus 519e5c6946SAdrien Destugues typedef AVCodecID CodecID; 524384acf6SStephan Aßmus 536ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter::StreamCookie 546ac391b3SStephan Aßmus 556ac391b3SStephan Aßmus 566ac391b3SStephan Aßmus class AVFormatWriter::StreamCookie { 576ac391b3SStephan Aßmus public: 584384acf6SStephan Aßmus StreamCookie(AVFormatContext* context, 596ac391b3SStephan Aßmus BLocker* streamLock); 606ac391b3SStephan Aßmus virtual ~StreamCookie(); 616ac391b3SStephan Aßmus 6269372b55SStephan Aßmus status_t Init(media_format* format, 6354897d5cSStephan Aßmus const media_codec_info* codecInfo); 644384acf6SStephan Aßmus 654384acf6SStephan Aßmus status_t WriteChunk(const void* chunkBuffer, 664384acf6SStephan Aßmus size_t chunkSize, 674384acf6SStephan Aßmus media_encode_info* encodeInfo); 684384acf6SStephan Aßmus 694384acf6SStephan Aßmus status_t AddTrackInfo(uint32 code, const void* data, 704384acf6SStephan Aßmus size_t size, uint32 flags); 714384acf6SStephan Aßmus 726ac391b3SStephan Aßmus private: 73ee9d0e02SBarrett17 AVFormatContext* fFormatContext; 744384acf6SStephan Aßmus AVStream* fStream; 7554897d5cSStephan Aßmus AVPacket fPacket; 7654897d5cSStephan Aßmus // Since different threads may write to the target, 776ac391b3SStephan Aßmus // we need to protect the file position and I/O by a lock. 786ac391b3SStephan Aßmus BLocker* fStreamLock; 796ac391b3SStephan Aßmus }; 806ac391b3SStephan Aßmus 816ac391b3SStephan Aßmus 826ac391b3SStephan Aßmus 834384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context, 846ac391b3SStephan Aßmus BLocker* streamLock) 856ac391b3SStephan Aßmus : 86ee9d0e02SBarrett17 fFormatContext(context), 874384acf6SStephan Aßmus fStream(NULL), 880c72a8aeSStephan Aßmus fStreamLock(streamLock) 896ac391b3SStephan Aßmus { 900c72a8aeSStephan Aßmus av_init_packet(&fPacket); 916ac391b3SStephan Aßmus } 926ac391b3SStephan Aßmus 936ac391b3SStephan Aßmus 946ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie() 956ac391b3SStephan Aßmus { 966ac391b3SStephan Aßmus } 976ac391b3SStephan Aßmus 986ac391b3SStephan Aßmus 994384acf6SStephan Aßmus status_t 10069372b55SStephan Aßmus AVFormatWriter::StreamCookie::Init(media_format* format, 10154897d5cSStephan Aßmus const media_codec_info* codecInfo) 1024384acf6SStephan Aßmus { 1036e567425SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::Init()\n"); 1044384acf6SStephan Aßmus 1054384acf6SStephan Aßmus BAutolock _(fStreamLock); 1064384acf6SStephan Aßmus 107ee9d0e02SBarrett17 fPacket.stream_index = fFormatContext->nb_streams; 108ee9d0e02SBarrett17 fStream = avformat_new_stream(fFormatContext, NULL); 1099e5c6946SAdrien Destugues fStream->id = fPacket.stream_index; 1104384acf6SStephan Aßmus 1114384acf6SStephan Aßmus if (fStream == NULL) { 1124384acf6SStephan Aßmus TRACE(" failed to add new stream\n"); 1134384acf6SStephan Aßmus return B_ERROR; 1144384acf6SStephan Aßmus } 1154384acf6SStephan Aßmus 116428d87c5SBarrett17 // TRACE(" fStream->codecpar: %p\n", fStream->codecpar); 1170c72a8aeSStephan Aßmus // TODO: This is a hack for now! Use avcodec_find_encoder_by_name() 1180c72a8aeSStephan Aßmus // or something similar... 119428d87c5SBarrett17 fStream->codecpar->codec_id = (CodecID)codecInfo->sub_id; 120428d87c5SBarrett17 if (fStream->codecpar->codec_id == AV_CODEC_ID_NONE) 121428d87c5SBarrett17 fStream->codecpar->codec_id = raw_audio_codec_id_for(*format); 1220c72a8aeSStephan Aßmus 12354897d5cSStephan Aßmus // Setup the stream according to the media format... 12454897d5cSStephan Aßmus if (format->type == B_MEDIA_RAW_VIDEO) { 125428d87c5SBarrett17 fStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; 1265ba361f8SBarrett17 fStream->time_base.den = (int)format->u.raw_video.field_rate; 1275ba361f8SBarrett17 fStream->time_base.num = 1; 1285ba361f8SBarrett17 12954897d5cSStephan Aßmus // video size 130428d87c5SBarrett17 fStream->codecpar->width = format->u.raw_video.display.line_width; 131428d87c5SBarrett17 fStream->codecpar->height = format->u.raw_video.display.line_count; 13254897d5cSStephan Aßmus // pixel aspect ratio 13354897d5cSStephan Aßmus fStream->sample_aspect_ratio.num 13454897d5cSStephan Aßmus = format->u.raw_video.pixel_width_aspect; 13554897d5cSStephan Aßmus fStream->sample_aspect_ratio.den 13654897d5cSStephan Aßmus = format->u.raw_video.pixel_height_aspect; 13754897d5cSStephan Aßmus if (fStream->sample_aspect_ratio.num == 0 13854897d5cSStephan Aßmus || fStream->sample_aspect_ratio.den == 0) { 13954897d5cSStephan Aßmus av_reduce(&fStream->sample_aspect_ratio.num, 140428d87c5SBarrett17 &fStream->sample_aspect_ratio.den, fStream->codecpar->width, 141428d87c5SBarrett17 fStream->codecpar->height, 255); 14254897d5cSStephan Aßmus } 14354897d5cSStephan Aßmus 144428d87c5SBarrett17 fStream->codecpar->sample_aspect_ratio = fStream->sample_aspect_ratio; 14569372b55SStephan Aßmus 14669372b55SStephan Aßmus // Use the last supported pixel format of the AVCodec, which we hope 14769372b55SStephan Aßmus // is the one with the best quality (true for all currently supported 14869372b55SStephan Aßmus // encoders). 149428d87c5SBarrett17 // AVCodec* codec = fStream->codecpar->codec; 150bb188e4dSStephan Aßmus // for (int i = 0; codec->pix_fmts[i] != PIX_FMT_NONE; i++) 151428d87c5SBarrett17 // fStream->codecpar->pix_fmt = codec->pix_fmts[i]; 152428d87c5SBarrett17 fStream->codecpar->format = AV_PIX_FMT_YUV420P; 15369372b55SStephan Aßmus 15454897d5cSStephan Aßmus } else if (format->type == B_MEDIA_RAW_AUDIO) { 155428d87c5SBarrett17 fStream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 156428d87c5SBarrett17 1570c72a8aeSStephan Aßmus // frame rate 158428d87c5SBarrett17 fStream->codecpar->sample_rate = (int)format->u.raw_audio.frame_rate; 1590c72a8aeSStephan Aßmus 1603ca4a7b1SStephan Aßmus // channels 161428d87c5SBarrett17 fStream->codecpar->channels = format->u.raw_audio.channel_count; 1625e0e1689SAdrien Destugues 1635e0e1689SAdrien Destugues // set fStream to the audio format we want to use. This is only a hint 1645e0e1689SAdrien Destugues // (each encoder has a different set of accepted formats) 1653ca4a7b1SStephan Aßmus switch (format->u.raw_audio.format) { 1663ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_FLOAT: 167428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_FLT; 1683ca4a7b1SStephan Aßmus break; 1693ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_DOUBLE: 170428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_DBL; 1713ca4a7b1SStephan Aßmus break; 1723ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_INT: 173428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_S32; 1743ca4a7b1SStephan Aßmus break; 1753ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_SHORT: 176428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_S16; 1773ca4a7b1SStephan Aßmus break; 1783ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_UCHAR: 179428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_U8; 1803ca4a7b1SStephan Aßmus break; 1813ca4a7b1SStephan Aßmus 1823ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_CHAR: 1833ca4a7b1SStephan Aßmus default: 1843ca4a7b1SStephan Aßmus return B_MEDIA_BAD_FORMAT; 1853ca4a7b1SStephan Aßmus break; 18654897d5cSStephan Aßmus } 1875e0e1689SAdrien Destugues 1885e0e1689SAdrien Destugues // Now negociate the actual format with the encoder 1895e0e1689SAdrien Destugues // First check if the requested format is acceptable 190428d87c5SBarrett17 AVCodec* codec = avcodec_find_encoder(fStream->codecpar->codec_id); 1919e5c6946SAdrien Destugues 1929e5c6946SAdrien Destugues if (codec == NULL) 1939e5c6946SAdrien Destugues return B_MEDIA_BAD_FORMAT; 1949e5c6946SAdrien Destugues 1955e0e1689SAdrien Destugues const enum AVSampleFormat *p = codec->sample_fmts; 1965e0e1689SAdrien Destugues for (; *p != -1; p++) { 197428d87c5SBarrett17 if (*p == fStream->codecpar->format) 1985e0e1689SAdrien Destugues break; 1995e0e1689SAdrien Destugues } 2005e0e1689SAdrien Destugues // If not, force one of the acceptable ones 2015e0e1689SAdrien Destugues if (*p == -1) { 202428d87c5SBarrett17 fStream->codecpar->format = codec->sample_fmts[0]; 2035e0e1689SAdrien Destugues 2045e0e1689SAdrien Destugues // And finally set the format struct to the accepted format. It is 2055e0e1689SAdrien Destugues // then up to the caller to make sure we get data matching that 2065e0e1689SAdrien Destugues // format. 207428d87c5SBarrett17 switch (fStream->codecpar->format) { 2085e0e1689SAdrien Destugues case AV_SAMPLE_FMT_FLT: 2095e0e1689SAdrien Destugues format->u.raw_audio.format 2105e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_FLOAT; 2115e0e1689SAdrien Destugues break; 2125e0e1689SAdrien Destugues case AV_SAMPLE_FMT_DBL: 2135e0e1689SAdrien Destugues format->u.raw_audio.format 2145e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_DOUBLE; 2155e0e1689SAdrien Destugues break; 2165e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S32: 2175e0e1689SAdrien Destugues format->u.raw_audio.format 2185e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_INT; 2195e0e1689SAdrien Destugues break; 2205e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S16: 2215e0e1689SAdrien Destugues format->u.raw_audio.format 2225e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_SHORT; 2235e0e1689SAdrien Destugues break; 2245e0e1689SAdrien Destugues case AV_SAMPLE_FMT_U8: 2255e0e1689SAdrien Destugues format->u.raw_audio.format 2265e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_UCHAR; 2275e0e1689SAdrien Destugues break; 2285e0e1689SAdrien Destugues default: 2295e0e1689SAdrien Destugues return B_MEDIA_BAD_FORMAT; 2305e0e1689SAdrien Destugues break; 2315e0e1689SAdrien Destugues } 2325e0e1689SAdrien Destugues } 2335e0e1689SAdrien Destugues 2343ca4a7b1SStephan Aßmus if (format->u.raw_audio.channel_mask == 0) { 2353ca4a7b1SStephan Aßmus // guess the channel mask... 2363ca4a7b1SStephan Aßmus switch (format->u.raw_audio.channel_count) { 2373ca4a7b1SStephan Aßmus default: 2383ca4a7b1SStephan Aßmus case 2: 239428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; 2403ca4a7b1SStephan Aßmus break; 2413ca4a7b1SStephan Aßmus case 1: 242428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_MONO; 2433ca4a7b1SStephan Aßmus break; 2443ca4a7b1SStephan Aßmus case 3: 245428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_SURROUND; 2463ca4a7b1SStephan Aßmus break; 2473ca4a7b1SStephan Aßmus case 4: 248428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_QUAD; 2493ca4a7b1SStephan Aßmus break; 2503ca4a7b1SStephan Aßmus case 5: 251428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT0; 2523ca4a7b1SStephan Aßmus break; 2533ca4a7b1SStephan Aßmus case 6: 254428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT1; 2553ca4a7b1SStephan Aßmus break; 2563ca4a7b1SStephan Aßmus case 8: 257428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1; 2583ca4a7b1SStephan Aßmus break; 2593ca4a7b1SStephan Aßmus case 10: 260428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE; 2613ca4a7b1SStephan Aßmus break; 2623ca4a7b1SStephan Aßmus } 2633ca4a7b1SStephan Aßmus } else { 2643ca4a7b1SStephan Aßmus // The bits match 1:1 for media_multi_channels and FFmpeg defines. 265428d87c5SBarrett17 fStream->codecpar->channel_layout = format->u.raw_audio.channel_mask; 2663ca4a7b1SStephan Aßmus } 2673ca4a7b1SStephan Aßmus } 2683ca4a7b1SStephan Aßmus 2693ca4a7b1SStephan Aßmus TRACE(" stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n", 2703ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 271428d87c5SBarrett17 fStream->codecpar->time_base.num, fStream->codecpar->time_base.den); 27254897d5cSStephan Aßmus 273657983b8SStephan Aßmus #if 0 274657983b8SStephan Aßmus // Write the AVCodecContext pointer to the user data section of the 275657983b8SStephan Aßmus // media_format. For some encoders, it seems to be necessary to use 276657983b8SStephan Aßmus // the AVCodecContext of the AVStream in order to successfully encode 277657983b8SStephan Aßmus // anything and write valid media files. For example some codecs need 278657983b8SStephan Aßmus // to store meta data or global data in the container. 279657983b8SStephan Aßmus app_info appInfo; 280657983b8SStephan Aßmus if (be_app->GetAppInfo(&appInfo) == B_OK) { 281657983b8SStephan Aßmus uchar* userData = format->user_data; 282657983b8SStephan Aßmus *(uint32*)userData = 'ffmp'; 283657983b8SStephan Aßmus userData += sizeof(uint32); 284657983b8SStephan Aßmus *(team_id*)userData = appInfo.team; 285657983b8SStephan Aßmus userData += sizeof(team_id); 286657983b8SStephan Aßmus *(AVCodecContext**)userData = fStream->codec; 287657983b8SStephan Aßmus } 288657983b8SStephan Aßmus #endif 289657983b8SStephan Aßmus 2904384acf6SStephan Aßmus return B_OK; 2914384acf6SStephan Aßmus } 2924384acf6SStephan Aßmus 2934384acf6SStephan Aßmus 2944384acf6SStephan Aßmus status_t 2954384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer, 2964384acf6SStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 2974384acf6SStephan Aßmus { 29896590b5bSStephan Aßmus TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, " 29996590b5bSStephan Aßmus "start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize, 30096590b5bSStephan Aßmus encodeInfo->start_time); 3014384acf6SStephan Aßmus 3024384acf6SStephan Aßmus BAutolock _(fStreamLock); 3034384acf6SStephan Aßmus 30454897d5cSStephan Aßmus fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer); 30554897d5cSStephan Aßmus fPacket.size = chunkSize; 30654897d5cSStephan Aßmus 30796590b5bSStephan Aßmus fPacket.pts = int64_t((double)encodeInfo->start_time 30896590b5bSStephan Aßmus * fStream->time_base.den / (1000000.0 * fStream->time_base.num) 30996590b5bSStephan Aßmus + 0.5); 31069372b55SStephan Aßmus 311*e93fce62SBarrett17 fPacket.dts = fPacket.pts; 312*e93fce62SBarrett17 31369372b55SStephan Aßmus fPacket.flags = 0; 31469372b55SStephan Aßmus if ((encodeInfo->flags & B_MEDIA_KEY_FRAME) != 0) 31569372b55SStephan Aßmus fPacket.flags |= AV_PKT_FLAG_KEY; 31669372b55SStephan Aßmus 3173ca4a7b1SStephan Aßmus TRACE_PACKET(" PTS: %lld (stream->time_base: (%d/%d), " 3183ca4a7b1SStephan Aßmus "codec->time_base: (%d/%d))\n", fPacket.pts, 3193ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 320428d87c5SBarrett17 fStream->codecpar->time_base.num, fStream->codecpar->time_base.den); 3213ca4a7b1SStephan Aßmus 32254897d5cSStephan Aßmus #if 0 32354897d5cSStephan Aßmus // TODO: Eventually, we need to write interleaved packets, but 32454897d5cSStephan Aßmus // maybe we are only supposed to use this if we have actually 32554897d5cSStephan Aßmus // more than one stream. For the moment, this crashes in AVPacket 32654897d5cSStephan Aßmus // shuffling inside libavformat. Maybe if we want to use this, we 32754897d5cSStephan Aßmus // need to allocate a separate AVPacket and copy the chunk buffer. 328ee9d0e02SBarrett17 int result = av_interleaved_write_frame(fFormatContext, &fPacket); 32954897d5cSStephan Aßmus if (result < 0) 33054897d5cSStephan Aßmus TRACE(" av_interleaved_write_frame(): %d\n", result); 33154897d5cSStephan Aßmus #else 332ee9d0e02SBarrett17 int result = av_write_frame(fFormatContext, &fPacket); 33354897d5cSStephan Aßmus if (result < 0) 33454897d5cSStephan Aßmus TRACE(" av_write_frame(): %d\n", result); 33554897d5cSStephan Aßmus #endif 33654897d5cSStephan Aßmus 33754897d5cSStephan Aßmus return result == 0 ? B_OK : B_ERROR; 3384384acf6SStephan Aßmus } 3394384acf6SStephan Aßmus 3404384acf6SStephan Aßmus 3414384acf6SStephan Aßmus status_t 3424384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code, 3434384acf6SStephan Aßmus const void* data, size_t size, uint32 flags) 3444384acf6SStephan Aßmus { 3454384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n", 3464384acf6SStephan Aßmus code, data, size, flags); 3474384acf6SStephan Aßmus 3484384acf6SStephan Aßmus BAutolock _(fStreamLock); 3494384acf6SStephan Aßmus 3504384acf6SStephan Aßmus return B_NOT_SUPPORTED; 3514384acf6SStephan Aßmus } 3524384acf6SStephan Aßmus 3534384acf6SStephan Aßmus 3546ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter 3556ac391b3SStephan Aßmus 3566ac391b3SStephan Aßmus 3576ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter() 3586ac391b3SStephan Aßmus : 359ee9d0e02SBarrett17 fFormatContext(avformat_alloc_context()), 3606266cf35SDario Casalinuovo fCodecOpened(false), 361cbada661SDario Casalinuovo fHeaderError(-1), 362b95fa248SJérôme Duval fIOContext(NULL), 3636ac391b3SStephan Aßmus fStreamLock("stream lock") 3646ac391b3SStephan Aßmus { 3656ac391b3SStephan Aßmus TRACE("AVFormatWriter::AVFormatWriter\n"); 3666ac391b3SStephan Aßmus } 3676ac391b3SStephan Aßmus 3686ac391b3SStephan Aßmus 3696ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter() 3706ac391b3SStephan Aßmus { 3716ac391b3SStephan Aßmus TRACE("AVFormatWriter::~AVFormatWriter\n"); 3724384acf6SStephan Aßmus 3730c72a8aeSStephan Aßmus // Free the streams and close the AVCodecContexts 374ee9d0e02SBarrett17 for(unsigned i = 0; i < fFormatContext->nb_streams; i++) { 375ee9d0e02SBarrett17 av_freep(&fFormatContext->streams[i]->codecpar); 376ee9d0e02SBarrett17 av_freep(&fFormatContext->streams[i]); 3770c72a8aeSStephan Aßmus } 3780c72a8aeSStephan Aßmus 379ee9d0e02SBarrett17 av_free(fFormatContext); 380b95fa248SJérôme Duval av_free(fIOContext->buffer); 381b95fa248SJérôme Duval av_free(fIOContext); 3826ac391b3SStephan Aßmus } 3836ac391b3SStephan Aßmus 3846ac391b3SStephan Aßmus 3856ac391b3SStephan Aßmus // #pragma mark - 3866ac391b3SStephan Aßmus 3876ac391b3SStephan Aßmus 3886ac391b3SStephan Aßmus status_t 3894384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat) 3904384acf6SStephan Aßmus { 3914384acf6SStephan Aßmus TRACE("AVFormatWriter::Init()\n"); 3924384acf6SStephan Aßmus 39396590b5bSStephan Aßmus uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize)); 39496590b5bSStephan Aßmus if (buffer == NULL) 3954384acf6SStephan Aßmus return B_NO_MEMORY; 3964384acf6SStephan Aßmus 3977e75e564SStefano Ceccherini // Allocate I/O context and initialize it with buffer 3987e75e564SStefano Ceccherini // and hook functions, pass ourself as cookie. 39984e70401SAdrien Destugues fIOContext = avio_alloc_context(buffer, kIOBufferSize, 1, this, 4007e75e564SStefano Ceccherini 0, _Write, _Seek); 4017e75e564SStefano Ceccherini if (fIOContext == NULL) { 4027e75e564SStefano Ceccherini TRACE("av_alloc_put_byte() failed!\n"); 4034384acf6SStephan Aßmus return B_ERROR; 4044384acf6SStephan Aßmus } 4054384acf6SStephan Aßmus 40654897d5cSStephan Aßmus // Setup I/O hooks. This seems to be enough. 407ee9d0e02SBarrett17 fFormatContext->pb = fIOContext; 4084384acf6SStephan Aßmus 40954897d5cSStephan Aßmus // Set the AVOutputFormat according to fileFormat... 410ee9d0e02SBarrett17 fFormatContext->oformat = av_guess_format(fileFormat->short_name, 4114384acf6SStephan Aßmus fileFormat->file_extension, fileFormat->mime_type); 412ee9d0e02SBarrett17 if (fFormatContext->oformat == NULL) { 4134384acf6SStephan Aßmus TRACE(" failed to find AVOuputFormat for %s\n", 4144384acf6SStephan Aßmus fileFormat->short_name); 4154384acf6SStephan Aßmus return B_NOT_SUPPORTED; 4164384acf6SStephan Aßmus } 4174384acf6SStephan Aßmus 4184384acf6SStephan Aßmus TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name, 419ee9d0e02SBarrett17 fFormatContext->oformat->name); 4204384acf6SStephan Aßmus 4214384acf6SStephan Aßmus return B_OK; 4224384acf6SStephan Aßmus } 4234384acf6SStephan Aßmus 4244384acf6SStephan Aßmus 4254384acf6SStephan Aßmus status_t 4266ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright) 4276ac391b3SStephan Aßmus { 4286ac391b3SStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright); 4296ac391b3SStephan Aßmus 4306ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 4316ac391b3SStephan Aßmus } 4326ac391b3SStephan Aßmus 4336ac391b3SStephan Aßmus 4346ac391b3SStephan Aßmus status_t 4356ac391b3SStephan Aßmus AVFormatWriter::CommitHeader() 4366ac391b3SStephan Aßmus { 4376ac391b3SStephan Aßmus TRACE("AVFormatWriter::CommitHeader\n"); 4386ac391b3SStephan Aßmus 439ee9d0e02SBarrett17 if (fFormatContext == NULL) 4404384acf6SStephan Aßmus return B_NO_INIT; 4414384acf6SStephan Aßmus 4426266cf35SDario Casalinuovo if (fCodecOpened) 4434384acf6SStephan Aßmus return B_NOT_ALLOWED; 4444384acf6SStephan Aßmus 4456266cf35SDario Casalinuovo // We need to close the codecs we opened, even in case of failure. 4466266cf35SDario Casalinuovo fCodecOpened = true; 4476266cf35SDario Casalinuovo 448ee9d0e02SBarrett17 fHeaderError = avformat_write_header(fFormatContext, NULL); 449cbada661SDario Casalinuovo if (fHeaderError < 0) 450cbada661SDario Casalinuovo TRACE(" avformat_write_header(): %d\n", fHeaderError); 45196590b5bSStephan Aßmus 45296590b5bSStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 4533ca4a7b1SStephan Aßmus TRACE(" wrote header\n"); 454ee9d0e02SBarrett17 for (unsigned i = 0; i < fFormatContext->nb_streams; i++) { 455ee9d0e02SBarrett17 AVStream* stream = fFormatContext->streams[i]; 4563ca4a7b1SStephan Aßmus TRACE(" stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n", 4573ca4a7b1SStephan Aßmus i, stream->time_base.num, stream->time_base.den, 4583ca4a7b1SStephan Aßmus stream->codec->time_base.num, stream->codec->time_base.den); 4593ca4a7b1SStephan Aßmus } 460eb01f516SStephan Aßmus #endif // TRACE_AVFORMAT_WRITER 4613ca4a7b1SStephan Aßmus 462cbada661SDario Casalinuovo return fHeaderError == 0 ? B_OK : B_ERROR; 4636ac391b3SStephan Aßmus } 4646ac391b3SStephan Aßmus 4656ac391b3SStephan Aßmus 4666ac391b3SStephan Aßmus status_t 4676ac391b3SStephan Aßmus AVFormatWriter::Flush() 4686ac391b3SStephan Aßmus { 4696ac391b3SStephan Aßmus TRACE("AVFormatWriter::Flush\n"); 4706ac391b3SStephan Aßmus 4716ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 4726ac391b3SStephan Aßmus } 4736ac391b3SStephan Aßmus 4746ac391b3SStephan Aßmus 4756ac391b3SStephan Aßmus status_t 4766ac391b3SStephan Aßmus AVFormatWriter::Close() 4776ac391b3SStephan Aßmus { 4786ac391b3SStephan Aßmus TRACE("AVFormatWriter::Close\n"); 4796ac391b3SStephan Aßmus 480ee9d0e02SBarrett17 if (fFormatContext == NULL) 4814384acf6SStephan Aßmus return B_NO_INIT; 4824384acf6SStephan Aßmus 4836266cf35SDario Casalinuovo if (!fCodecOpened) 4844384acf6SStephan Aßmus return B_NOT_ALLOWED; 4854384acf6SStephan Aßmus 486cbada661SDario Casalinuovo // From ffmpeg documentation: [av_write_trailer] may only be called 487cbada661SDario Casalinuovo // after a successful call to avformat_write_header. 4886266cf35SDario Casalinuovo if (fHeaderError != 0) 4896266cf35SDario Casalinuovo return B_ERROR; 4906266cf35SDario Casalinuovo 491ee9d0e02SBarrett17 int result = av_write_trailer(fFormatContext); 4924384acf6SStephan Aßmus if (result < 0) 4934384acf6SStephan Aßmus TRACE(" av_write_trailer(): %d\n", result); 4944384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 4956ac391b3SStephan Aßmus } 4966ac391b3SStephan Aßmus 4976ac391b3SStephan Aßmus 4986ac391b3SStephan Aßmus status_t 49969372b55SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, media_format* format, 50054897d5cSStephan Aßmus const media_codec_info* codecInfo) 5016ac391b3SStephan Aßmus { 5026ac391b3SStephan Aßmus TRACE("AVFormatWriter::AllocateCookie()\n"); 5036ac391b3SStephan Aßmus 5046266cf35SDario Casalinuovo if (fCodecOpened) 5050c72a8aeSStephan Aßmus return B_NOT_ALLOWED; 5060c72a8aeSStephan Aßmus 5076ac391b3SStephan Aßmus BAutolock _(fStreamLock); 5086ac391b3SStephan Aßmus 5096ac391b3SStephan Aßmus if (_cookie == NULL) 5106ac391b3SStephan Aßmus return B_BAD_VALUE; 5116ac391b3SStephan Aßmus 512ee9d0e02SBarrett17 StreamCookie* cookie = new(std::nothrow) StreamCookie(fFormatContext, 5134384acf6SStephan Aßmus &fStreamLock); 5144384acf6SStephan Aßmus 51554897d5cSStephan Aßmus status_t ret = cookie->Init(format, codecInfo); 51654897d5cSStephan Aßmus if (ret != B_OK) { 51754897d5cSStephan Aßmus delete cookie; 51854897d5cSStephan Aßmus return ret; 51954897d5cSStephan Aßmus } 52054897d5cSStephan Aßmus 52154897d5cSStephan Aßmus *_cookie = cookie; 52254897d5cSStephan Aßmus return B_OK; 5236ac391b3SStephan Aßmus } 5246ac391b3SStephan Aßmus 5256ac391b3SStephan Aßmus 5266ac391b3SStephan Aßmus status_t 5276ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie) 5286ac391b3SStephan Aßmus { 5296ac391b3SStephan Aßmus BAutolock _(fStreamLock); 5306ac391b3SStephan Aßmus 5316ac391b3SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5326ac391b3SStephan Aßmus delete cookie; 5336ac391b3SStephan Aßmus 5346ac391b3SStephan Aßmus return B_OK; 5356ac391b3SStephan Aßmus } 5366ac391b3SStephan Aßmus 5376ac391b3SStephan Aßmus 5386ac391b3SStephan Aßmus // #pragma mark - 5396ac391b3SStephan Aßmus 5406ac391b3SStephan Aßmus 5416ac391b3SStephan Aßmus status_t 542fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright) 543fa770e4cSStephan Aßmus { 544fa770e4cSStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright); 545fa770e4cSStephan Aßmus 546fa770e4cSStephan Aßmus return B_NOT_SUPPORTED; 547fa770e4cSStephan Aßmus } 548fa770e4cSStephan Aßmus 549fa770e4cSStephan Aßmus 550fa770e4cSStephan Aßmus status_t 5514384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code, 5526ac391b3SStephan Aßmus const void* data, size_t size, uint32 flags) 5536ac391b3SStephan Aßmus { 5546ac391b3SStephan Aßmus TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n", 5556ac391b3SStephan Aßmus code, data, size, flags); 5566ac391b3SStephan Aßmus 557ec2c5619SDario Casalinuovo if (fHeaderError != 0) 558ec2c5619SDario Casalinuovo return B_ERROR; 559ec2c5619SDario Casalinuovo 5604384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5614384acf6SStephan Aßmus return cookie->AddTrackInfo(code, data, size, flags); 5626ac391b3SStephan Aßmus } 5636ac391b3SStephan Aßmus 5646ac391b3SStephan Aßmus 5656ac391b3SStephan Aßmus status_t 5664384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer, 567fa770e4cSStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 5686ac391b3SStephan Aßmus { 5693ca4a7b1SStephan Aßmus TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer, 5703ca4a7b1SStephan Aßmus chunkSize, encodeInfo); 5716ac391b3SStephan Aßmus 572ec2c5619SDario Casalinuovo if (fHeaderError != 0) 573ec2c5619SDario Casalinuovo return B_ERROR; 574ec2c5619SDario Casalinuovo 5754384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5764384acf6SStephan Aßmus return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo); 5776ac391b3SStephan Aßmus } 5786ac391b3SStephan Aßmus 5796ac391b3SStephan Aßmus 58054897d5cSStephan Aßmus // #pragma mark - I/O hooks 5812e9d65abSStephan Aßmus 5822e9d65abSStephan Aßmus 5832e9d65abSStephan Aßmus /*static*/ int 5847a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize) 5852e9d65abSStephan Aßmus { 5862e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n", 5872e9d65abSStephan Aßmus cookie, buffer, bufferSize); 5882e9d65abSStephan Aßmus 5892e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 5902e9d65abSStephan Aßmus 5912e9d65abSStephan Aßmus ssize_t written = writer->fTarget->Write(buffer, bufferSize); 5922e9d65abSStephan Aßmus 5932e9d65abSStephan Aßmus TRACE_IO(" written: %ld\n", written); 5942e9d65abSStephan Aßmus return (int)written; 5952e9d65abSStephan Aßmus 5962e9d65abSStephan Aßmus } 5972e9d65abSStephan Aßmus 5982e9d65abSStephan Aßmus 5992e9d65abSStephan Aßmus /*static*/ off_t 6002e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) 6012e9d65abSStephan Aßmus { 6022e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n", 6032e9d65abSStephan Aßmus cookie, offset, whence); 6042e9d65abSStephan Aßmus 6052e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 6062e9d65abSStephan Aßmus 607a6b34a8cSDario Casalinuovo BMediaIO* mediaIO = dynamic_cast<BMediaIO*>(writer->fTarget); 608a6b34a8cSDario Casalinuovo if (mediaIO == NULL) 6092e9d65abSStephan Aßmus return -1; 6102e9d65abSStephan Aßmus 6114384acf6SStephan Aßmus // Support for special file size retrieval API without seeking anywhere: 6122e9d65abSStephan Aßmus if (whence == AVSEEK_SIZE) { 6132e9d65abSStephan Aßmus off_t size; 614a6b34a8cSDario Casalinuovo if (mediaIO->GetSize(&size) == B_OK) 6152e9d65abSStephan Aßmus return size; 616a6b34a8cSDario Casalinuovo 6172e9d65abSStephan Aßmus return -1; 6182e9d65abSStephan Aßmus } 6192e9d65abSStephan Aßmus 620a6b34a8cSDario Casalinuovo off_t position = mediaIO->Seek(offset, whence); 6212e9d65abSStephan Aßmus TRACE_IO(" position: %lld\n", position); 6222e9d65abSStephan Aßmus if (position < 0) 6232e9d65abSStephan Aßmus return -1; 6242e9d65abSStephan Aßmus 6252e9d65abSStephan Aßmus return position; 6262e9d65abSStephan Aßmus } 6272e9d65abSStephan Aßmus 6282e9d65abSStephan Aßmus 629