16ac391b3SStephan Aßmus /* 2657983b8SStephan Aßmus * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de> 36ac391b3SStephan Aßmus * All rights reserved. Distributed under the terms of the GNU L-GPL license. 46ac391b3SStephan Aßmus */ 56ac391b3SStephan Aßmus 66ac391b3SStephan Aßmus #include "AVFormatWriter.h" 76ac391b3SStephan Aßmus 86ac391b3SStephan Aßmus #include <stdio.h> 96ac391b3SStephan Aßmus #include <string.h> 106ac391b3SStephan Aßmus #include <stdlib.h> 116ac391b3SStephan Aßmus 126ac391b3SStephan Aßmus #include <new> 136ac391b3SStephan Aßmus 14657983b8SStephan Aßmus #include <Application.h> 156ac391b3SStephan Aßmus #include <AutoDeleter.h> 166ac391b3SStephan Aßmus #include <Autolock.h> 176ac391b3SStephan Aßmus #include <ByteOrder.h> 186ac391b3SStephan Aßmus #include <DataIO.h> 196ac391b3SStephan Aßmus #include <MediaDefs.h> 206ac391b3SStephan Aßmus #include <MediaFormats.h> 21657983b8SStephan Aßmus #include <Roster.h> 226ac391b3SStephan Aßmus 236ac391b3SStephan Aßmus extern "C" { 246ac391b3SStephan Aßmus #include "avformat.h" 256ac391b3SStephan Aßmus } 266ac391b3SStephan Aßmus 276ac391b3SStephan Aßmus #include "DemuxerTable.h" 28eb01f516SStephan Aßmus #include "EncoderTable.h" 296ac391b3SStephan Aßmus #include "gfx_util.h" 306ac391b3SStephan Aßmus 316ac391b3SStephan Aßmus 32856d0bbfSStephan Aßmus //#define TRACE_AVFORMAT_WRITER 336ac391b3SStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 346ac391b3SStephan Aßmus # define TRACE printf 356ac391b3SStephan Aßmus # define TRACE_IO(a...) 363ca4a7b1SStephan Aßmus # define TRACE_PACKET printf 376ac391b3SStephan Aßmus #else 386ac391b3SStephan Aßmus # define TRACE(a...) 396ac391b3SStephan Aßmus # define TRACE_IO(a...) 406ac391b3SStephan Aßmus # define TRACE_PACKET(a...) 416ac391b3SStephan Aßmus #endif 426ac391b3SStephan Aßmus 436ac391b3SStephan Aßmus #define ERROR(a...) fprintf(stderr, a) 446ac391b3SStephan Aßmus 456ac391b3SStephan Aßmus 464384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024; 474384acf6SStephan Aßmus // TODO: This could depend on the BMediaFile creation flags, IIRC, 484384acf6SStephan Aßmus // they allow to specify a buffering mode. 494384acf6SStephan Aßmus 500c72a8aeSStephan Aßmus // NOTE: The following works around some weird bug in libavformat. We 510c72a8aeSStephan Aßmus // have to open the AVFormatContext->AVStream->AVCodecContext, even though 520c72a8aeSStephan Aßmus // we are not interested in donig any encoding here!! 530c72a8aeSStephan Aßmus #define OPEN_CODEC_CONTEXT 1 540c72a8aeSStephan Aßmus #define GET_CONTEXT_DEFAULTS 0 554384acf6SStephan Aßmus 56*9e5c6946SAdrien Destugues #if __GNUC__ > 2 57*9e5c6946SAdrien Destugues typedef AVCodecID CodecID; 58*9e5c6946SAdrien Destugues #endif 594384acf6SStephan Aßmus 606ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter::StreamCookie 616ac391b3SStephan Aßmus 626ac391b3SStephan Aßmus 636ac391b3SStephan Aßmus class AVFormatWriter::StreamCookie { 646ac391b3SStephan Aßmus public: 654384acf6SStephan Aßmus StreamCookie(AVFormatContext* context, 666ac391b3SStephan Aßmus BLocker* streamLock); 676ac391b3SStephan Aßmus virtual ~StreamCookie(); 686ac391b3SStephan Aßmus 6969372b55SStephan Aßmus status_t Init(media_format* format, 7054897d5cSStephan Aßmus const media_codec_info* codecInfo); 714384acf6SStephan Aßmus 724384acf6SStephan Aßmus status_t WriteChunk(const void* chunkBuffer, 734384acf6SStephan Aßmus size_t chunkSize, 744384acf6SStephan Aßmus media_encode_info* encodeInfo); 754384acf6SStephan Aßmus 764384acf6SStephan Aßmus status_t AddTrackInfo(uint32 code, const void* data, 774384acf6SStephan Aßmus size_t size, uint32 flags); 784384acf6SStephan Aßmus 796ac391b3SStephan Aßmus private: 804384acf6SStephan Aßmus AVFormatContext* fContext; 814384acf6SStephan Aßmus AVStream* fStream; 8254897d5cSStephan Aßmus AVPacket fPacket; 8354897d5cSStephan Aßmus // Since different threads may write to the target, 846ac391b3SStephan Aßmus // we need to protect the file position and I/O by a lock. 856ac391b3SStephan Aßmus BLocker* fStreamLock; 866ac391b3SStephan Aßmus }; 876ac391b3SStephan Aßmus 886ac391b3SStephan Aßmus 896ac391b3SStephan Aßmus 904384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context, 916ac391b3SStephan Aßmus BLocker* streamLock) 926ac391b3SStephan Aßmus : 934384acf6SStephan Aßmus fContext(context), 944384acf6SStephan Aßmus fStream(NULL), 950c72a8aeSStephan Aßmus fStreamLock(streamLock) 966ac391b3SStephan Aßmus { 970c72a8aeSStephan Aßmus av_init_packet(&fPacket); 986ac391b3SStephan Aßmus } 996ac391b3SStephan Aßmus 1006ac391b3SStephan Aßmus 1016ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie() 1026ac391b3SStephan Aßmus { 1036ac391b3SStephan Aßmus } 1046ac391b3SStephan Aßmus 1056ac391b3SStephan Aßmus 1064384acf6SStephan Aßmus status_t 10769372b55SStephan Aßmus AVFormatWriter::StreamCookie::Init(media_format* format, 10854897d5cSStephan Aßmus const media_codec_info* codecInfo) 1094384acf6SStephan Aßmus { 1106e567425SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::Init()\n"); 1114384acf6SStephan Aßmus 1124384acf6SStephan Aßmus BAutolock _(fStreamLock); 1134384acf6SStephan Aßmus 11454897d5cSStephan Aßmus fPacket.stream_index = fContext->nb_streams; 115*9e5c6946SAdrien Destugues fStream = avformat_new_stream(fContext, NULL); 116*9e5c6946SAdrien Destugues fStream->id = fPacket.stream_index; 1174384acf6SStephan Aßmus 1184384acf6SStephan Aßmus if (fStream == NULL) { 1194384acf6SStephan Aßmus TRACE(" failed to add new stream\n"); 1204384acf6SStephan Aßmus return B_ERROR; 1214384acf6SStephan Aßmus } 1224384acf6SStephan Aßmus 1230c72a8aeSStephan Aßmus // TRACE(" fStream->codec: %p\n", fStream->codec); 1240c72a8aeSStephan Aßmus // TODO: This is a hack for now! Use avcodec_find_encoder_by_name() 1250c72a8aeSStephan Aßmus // or something similar... 1260c72a8aeSStephan Aßmus fStream->codec->codec_id = (CodecID)codecInfo->sub_id; 127eb01f516SStephan Aßmus if (fStream->codec->codec_id == CODEC_ID_NONE) 128eb01f516SStephan Aßmus fStream->codec->codec_id = raw_audio_codec_id_for(*format); 1290c72a8aeSStephan Aßmus 13054897d5cSStephan Aßmus // Setup the stream according to the media format... 13154897d5cSStephan Aßmus if (format->type == B_MEDIA_RAW_VIDEO) { 13269372b55SStephan Aßmus fStream->codec->codec_type = AVMEDIA_TYPE_VIDEO; 1330c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS 1340c72a8aeSStephan Aßmus // NOTE: API example does not do this: 1350c72a8aeSStephan Aßmus avcodec_get_context_defaults(fStream->codec); 1360c72a8aeSStephan Aßmus #endif 13754897d5cSStephan Aßmus // frame rate 13854897d5cSStephan Aßmus fStream->codec->time_base.den = (int)format->u.raw_video.field_rate; 13954897d5cSStephan Aßmus fStream->codec->time_base.num = 1; 14054897d5cSStephan Aßmus // video size 14154897d5cSStephan Aßmus fStream->codec->width = format->u.raw_video.display.line_width; 14254897d5cSStephan Aßmus fStream->codec->height = format->u.raw_video.display.line_count; 14354897d5cSStephan Aßmus // pixel aspect ratio 14454897d5cSStephan Aßmus fStream->sample_aspect_ratio.num 14554897d5cSStephan Aßmus = format->u.raw_video.pixel_width_aspect; 14654897d5cSStephan Aßmus fStream->sample_aspect_ratio.den 14754897d5cSStephan Aßmus = format->u.raw_video.pixel_height_aspect; 14854897d5cSStephan Aßmus if (fStream->sample_aspect_ratio.num == 0 14954897d5cSStephan Aßmus || fStream->sample_aspect_ratio.den == 0) { 15054897d5cSStephan Aßmus av_reduce(&fStream->sample_aspect_ratio.num, 15154897d5cSStephan Aßmus &fStream->sample_aspect_ratio.den, fStream->codec->width, 15254897d5cSStephan Aßmus fStream->codec->height, 255); 15354897d5cSStephan Aßmus } 15454897d5cSStephan Aßmus 15569372b55SStephan Aßmus fStream->codec->gop_size = 12; 1560c72a8aeSStephan Aßmus 15769372b55SStephan Aßmus fStream->codec->sample_aspect_ratio = fStream->sample_aspect_ratio; 15869372b55SStephan Aßmus 15969372b55SStephan Aßmus // Use the last supported pixel format of the AVCodec, which we hope 16069372b55SStephan Aßmus // is the one with the best quality (true for all currently supported 16169372b55SStephan Aßmus // encoders). 162bb188e4dSStephan Aßmus // AVCodec* codec = fStream->codec->codec; 163bb188e4dSStephan Aßmus // for (int i = 0; codec->pix_fmts[i] != PIX_FMT_NONE; i++) 164bb188e4dSStephan Aßmus // fStream->codec->pix_fmt = codec->pix_fmts[i]; 165bb188e4dSStephan Aßmus fStream->codec->pix_fmt = PIX_FMT_YUV420P; 16669372b55SStephan Aßmus 16754897d5cSStephan Aßmus } else if (format->type == B_MEDIA_RAW_AUDIO) { 16869372b55SStephan Aßmus fStream->codec->codec_type = AVMEDIA_TYPE_AUDIO; 1690c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS 1700c72a8aeSStephan Aßmus // NOTE: API example does not do this: 1710c72a8aeSStephan Aßmus avcodec_get_context_defaults(fStream->codec); 1720c72a8aeSStephan Aßmus #endif 1730c72a8aeSStephan Aßmus // frame rate 1740c72a8aeSStephan Aßmus fStream->codec->sample_rate = (int)format->u.raw_audio.frame_rate; 1750c72a8aeSStephan Aßmus 1763ca4a7b1SStephan Aßmus // channels 1773ca4a7b1SStephan Aßmus fStream->codec->channels = format->u.raw_audio.channel_count; 1785e0e1689SAdrien Destugues 1795e0e1689SAdrien Destugues // set fStream to the audio format we want to use. This is only a hint 1805e0e1689SAdrien Destugues // (each encoder has a different set of accepted formats) 1813ca4a7b1SStephan Aßmus switch (format->u.raw_audio.format) { 1823ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_FLOAT: 18384e70401SAdrien Destugues fStream->codec->sample_fmt = AV_SAMPLE_FMT_FLT; 1843ca4a7b1SStephan Aßmus break; 1853ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_DOUBLE: 18684e70401SAdrien Destugues fStream->codec->sample_fmt = AV_SAMPLE_FMT_DBL; 1873ca4a7b1SStephan Aßmus break; 1883ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_INT: 18984e70401SAdrien Destugues fStream->codec->sample_fmt = AV_SAMPLE_FMT_S32; 1903ca4a7b1SStephan Aßmus break; 1913ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_SHORT: 19284e70401SAdrien Destugues fStream->codec->sample_fmt = AV_SAMPLE_FMT_S16; 1933ca4a7b1SStephan Aßmus break; 1943ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_UCHAR: 19584e70401SAdrien Destugues fStream->codec->sample_fmt = AV_SAMPLE_FMT_U8; 1963ca4a7b1SStephan Aßmus break; 1973ca4a7b1SStephan Aßmus 1983ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_CHAR: 1993ca4a7b1SStephan Aßmus default: 2003ca4a7b1SStephan Aßmus return B_MEDIA_BAD_FORMAT; 2013ca4a7b1SStephan Aßmus break; 20254897d5cSStephan Aßmus } 2035e0e1689SAdrien Destugues 2045e0e1689SAdrien Destugues // Now negociate the actual format with the encoder 2055e0e1689SAdrien Destugues // First check if the requested format is acceptable 2065e0e1689SAdrien Destugues AVCodec* codec = avcodec_find_encoder(fStream->codec->codec_id); 207*9e5c6946SAdrien Destugues 208*9e5c6946SAdrien Destugues if (codec == NULL) 209*9e5c6946SAdrien Destugues return B_MEDIA_BAD_FORMAT; 210*9e5c6946SAdrien Destugues 2115e0e1689SAdrien Destugues const enum AVSampleFormat *p = codec->sample_fmts; 2125e0e1689SAdrien Destugues for (; *p != -1; p++) { 2135e0e1689SAdrien Destugues if (*p == fStream->codec->sample_fmt) 2145e0e1689SAdrien Destugues break; 2155e0e1689SAdrien Destugues } 2165e0e1689SAdrien Destugues // If not, force one of the acceptable ones 2175e0e1689SAdrien Destugues if (*p == -1) { 2185e0e1689SAdrien Destugues fStream->codec->sample_fmt = codec->sample_fmts[0]; 2195e0e1689SAdrien Destugues 2205e0e1689SAdrien Destugues // And finally set the format struct to the accepted format. It is 2215e0e1689SAdrien Destugues // then up to the caller to make sure we get data matching that 2225e0e1689SAdrien Destugues // format. 2235e0e1689SAdrien Destugues switch (fStream->codec->sample_fmt) { 2245e0e1689SAdrien Destugues case AV_SAMPLE_FMT_FLT: 2255e0e1689SAdrien Destugues format->u.raw_audio.format 2265e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_FLOAT; 2275e0e1689SAdrien Destugues break; 2285e0e1689SAdrien Destugues case AV_SAMPLE_FMT_DBL: 2295e0e1689SAdrien Destugues format->u.raw_audio.format 2305e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_DOUBLE; 2315e0e1689SAdrien Destugues break; 2325e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S32: 2335e0e1689SAdrien Destugues format->u.raw_audio.format 2345e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_INT; 2355e0e1689SAdrien Destugues break; 2365e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S16: 2375e0e1689SAdrien Destugues format->u.raw_audio.format 2385e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_SHORT; 2395e0e1689SAdrien Destugues break; 2405e0e1689SAdrien Destugues case AV_SAMPLE_FMT_U8: 2415e0e1689SAdrien Destugues format->u.raw_audio.format 2425e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_UCHAR; 2435e0e1689SAdrien Destugues break; 2445e0e1689SAdrien Destugues default: 2455e0e1689SAdrien Destugues return B_MEDIA_BAD_FORMAT; 2465e0e1689SAdrien Destugues break; 2475e0e1689SAdrien Destugues } 2485e0e1689SAdrien Destugues } 2495e0e1689SAdrien Destugues 2503ca4a7b1SStephan Aßmus if (format->u.raw_audio.channel_mask == 0) { 2513ca4a7b1SStephan Aßmus // guess the channel mask... 2523ca4a7b1SStephan Aßmus switch (format->u.raw_audio.channel_count) { 2533ca4a7b1SStephan Aßmus default: 2543ca4a7b1SStephan Aßmus case 2: 25584e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_STEREO; 2563ca4a7b1SStephan Aßmus break; 2573ca4a7b1SStephan Aßmus case 1: 25884e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_MONO; 2593ca4a7b1SStephan Aßmus break; 2603ca4a7b1SStephan Aßmus case 3: 26184e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_SURROUND; 2623ca4a7b1SStephan Aßmus break; 2633ca4a7b1SStephan Aßmus case 4: 26484e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_QUAD; 2653ca4a7b1SStephan Aßmus break; 2663ca4a7b1SStephan Aßmus case 5: 26784e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_5POINT0; 2683ca4a7b1SStephan Aßmus break; 2693ca4a7b1SStephan Aßmus case 6: 27084e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_5POINT1; 2713ca4a7b1SStephan Aßmus break; 2723ca4a7b1SStephan Aßmus case 8: 27384e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_7POINT1; 2743ca4a7b1SStephan Aßmus break; 2753ca4a7b1SStephan Aßmus case 10: 27684e70401SAdrien Destugues fStream->codec->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE; 2773ca4a7b1SStephan Aßmus break; 2783ca4a7b1SStephan Aßmus } 2793ca4a7b1SStephan Aßmus } else { 2803ca4a7b1SStephan Aßmus // The bits match 1:1 for media_multi_channels and FFmpeg defines. 2813ca4a7b1SStephan Aßmus fStream->codec->channel_layout = format->u.raw_audio.channel_mask; 2823ca4a7b1SStephan Aßmus } 2833ca4a7b1SStephan Aßmus } 2843ca4a7b1SStephan Aßmus 28569372b55SStephan Aßmus // Some formats want stream headers to be separate 28669372b55SStephan Aßmus if ((fContext->oformat->flags & AVFMT_GLOBALHEADER) != 0) 28769372b55SStephan Aßmus fStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 28869372b55SStephan Aßmus 2893ca4a7b1SStephan Aßmus TRACE(" stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n", 2903ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 2913ca4a7b1SStephan Aßmus fStream->codec->time_base.num, fStream->codec->time_base.den); 29254897d5cSStephan Aßmus 293657983b8SStephan Aßmus #if 0 294657983b8SStephan Aßmus // Write the AVCodecContext pointer to the user data section of the 295657983b8SStephan Aßmus // media_format. For some encoders, it seems to be necessary to use 296657983b8SStephan Aßmus // the AVCodecContext of the AVStream in order to successfully encode 297657983b8SStephan Aßmus // anything and write valid media files. For example some codecs need 298657983b8SStephan Aßmus // to store meta data or global data in the container. 299657983b8SStephan Aßmus app_info appInfo; 300657983b8SStephan Aßmus if (be_app->GetAppInfo(&appInfo) == B_OK) { 301657983b8SStephan Aßmus uchar* userData = format->user_data; 302657983b8SStephan Aßmus *(uint32*)userData = 'ffmp'; 303657983b8SStephan Aßmus userData += sizeof(uint32); 304657983b8SStephan Aßmus *(team_id*)userData = appInfo.team; 305657983b8SStephan Aßmus userData += sizeof(team_id); 306657983b8SStephan Aßmus *(AVCodecContext**)userData = fStream->codec; 307657983b8SStephan Aßmus } 308657983b8SStephan Aßmus #endif 309657983b8SStephan Aßmus 3104384acf6SStephan Aßmus return B_OK; 3114384acf6SStephan Aßmus } 3124384acf6SStephan Aßmus 3134384acf6SStephan Aßmus 3144384acf6SStephan Aßmus status_t 3154384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer, 3164384acf6SStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 3174384acf6SStephan Aßmus { 31896590b5bSStephan Aßmus TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, " 31996590b5bSStephan Aßmus "start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize, 32096590b5bSStephan Aßmus encodeInfo->start_time); 3214384acf6SStephan Aßmus 3224384acf6SStephan Aßmus BAutolock _(fStreamLock); 3234384acf6SStephan Aßmus 32454897d5cSStephan Aßmus fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer); 32554897d5cSStephan Aßmus fPacket.size = chunkSize; 32654897d5cSStephan Aßmus 32796590b5bSStephan Aßmus fPacket.pts = int64_t((double)encodeInfo->start_time 32896590b5bSStephan Aßmus * fStream->time_base.den / (1000000.0 * fStream->time_base.num) 32996590b5bSStephan Aßmus + 0.5); 33069372b55SStephan Aßmus 33169372b55SStephan Aßmus fPacket.flags = 0; 33269372b55SStephan Aßmus if ((encodeInfo->flags & B_MEDIA_KEY_FRAME) != 0) 33369372b55SStephan Aßmus fPacket.flags |= AV_PKT_FLAG_KEY; 33469372b55SStephan Aßmus 3353ca4a7b1SStephan Aßmus TRACE_PACKET(" PTS: %lld (stream->time_base: (%d/%d), " 3363ca4a7b1SStephan Aßmus "codec->time_base: (%d/%d))\n", fPacket.pts, 3373ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 3383ca4a7b1SStephan Aßmus fStream->codec->time_base.num, fStream->codec->time_base.den); 3393ca4a7b1SStephan Aßmus 34054897d5cSStephan Aßmus #if 0 34154897d5cSStephan Aßmus // TODO: Eventually, we need to write interleaved packets, but 34254897d5cSStephan Aßmus // maybe we are only supposed to use this if we have actually 34354897d5cSStephan Aßmus // more than one stream. For the moment, this crashes in AVPacket 34454897d5cSStephan Aßmus // shuffling inside libavformat. Maybe if we want to use this, we 34554897d5cSStephan Aßmus // need to allocate a separate AVPacket and copy the chunk buffer. 34654897d5cSStephan Aßmus int result = av_interleaved_write_frame(fContext, &fPacket); 34754897d5cSStephan Aßmus if (result < 0) 34854897d5cSStephan Aßmus TRACE(" av_interleaved_write_frame(): %d\n", result); 34954897d5cSStephan Aßmus #else 35054897d5cSStephan Aßmus int result = av_write_frame(fContext, &fPacket); 35154897d5cSStephan Aßmus if (result < 0) 35254897d5cSStephan Aßmus TRACE(" av_write_frame(): %d\n", result); 35354897d5cSStephan Aßmus #endif 35454897d5cSStephan Aßmus 35554897d5cSStephan Aßmus return result == 0 ? B_OK : B_ERROR; 3564384acf6SStephan Aßmus } 3574384acf6SStephan Aßmus 3584384acf6SStephan Aßmus 3594384acf6SStephan Aßmus status_t 3604384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code, 3614384acf6SStephan Aßmus const void* data, size_t size, uint32 flags) 3624384acf6SStephan Aßmus { 3634384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n", 3644384acf6SStephan Aßmus code, data, size, flags); 3654384acf6SStephan Aßmus 3664384acf6SStephan Aßmus BAutolock _(fStreamLock); 3674384acf6SStephan Aßmus 3684384acf6SStephan Aßmus return B_NOT_SUPPORTED; 3694384acf6SStephan Aßmus } 3704384acf6SStephan Aßmus 3714384acf6SStephan Aßmus 3726ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter 3736ac391b3SStephan Aßmus 3746ac391b3SStephan Aßmus 3756ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter() 3766ac391b3SStephan Aßmus : 3774384acf6SStephan Aßmus fContext(avformat_alloc_context()), 3784384acf6SStephan Aßmus fHeaderWritten(false), 379b95fa248SJérôme Duval fIOContext(NULL), 3806ac391b3SStephan Aßmus fStreamLock("stream lock") 3816ac391b3SStephan Aßmus { 3826ac391b3SStephan Aßmus TRACE("AVFormatWriter::AVFormatWriter\n"); 3836ac391b3SStephan Aßmus } 3846ac391b3SStephan Aßmus 3856ac391b3SStephan Aßmus 3866ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter() 3876ac391b3SStephan Aßmus { 3886ac391b3SStephan Aßmus TRACE("AVFormatWriter::~AVFormatWriter\n"); 3894384acf6SStephan Aßmus 3900c72a8aeSStephan Aßmus // Free the streams and close the AVCodecContexts 3910c72a8aeSStephan Aßmus for(unsigned i = 0; i < fContext->nb_streams; i++) { 3920c72a8aeSStephan Aßmus #if OPEN_CODEC_CONTEXT 3930c72a8aeSStephan Aßmus // We only need to close the AVCodecContext when we opened it. 394fac8f830SStefano Ceccherini // This is experimental, see CommitHeader(). 395fac8f830SStefano Ceccherini if (fHeaderWritten) 3960c72a8aeSStephan Aßmus avcodec_close(fContext->streams[i]->codec); 3970c72a8aeSStephan Aßmus #endif 3980c72a8aeSStephan Aßmus av_freep(&fContext->streams[i]->codec); 3990c72a8aeSStephan Aßmus av_freep(&fContext->streams[i]); 4000c72a8aeSStephan Aßmus } 4010c72a8aeSStephan Aßmus 4024384acf6SStephan Aßmus av_free(fContext); 403b95fa248SJérôme Duval av_free(fIOContext->buffer); 404b95fa248SJérôme Duval av_free(fIOContext); 4056ac391b3SStephan Aßmus } 4066ac391b3SStephan Aßmus 4076ac391b3SStephan Aßmus 4086ac391b3SStephan Aßmus // #pragma mark - 4096ac391b3SStephan Aßmus 4106ac391b3SStephan Aßmus 4116ac391b3SStephan Aßmus status_t 4124384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat) 4134384acf6SStephan Aßmus { 4144384acf6SStephan Aßmus TRACE("AVFormatWriter::Init()\n"); 4154384acf6SStephan Aßmus 41696590b5bSStephan Aßmus uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize)); 41796590b5bSStephan Aßmus if (buffer == NULL) 4184384acf6SStephan Aßmus return B_NO_MEMORY; 4194384acf6SStephan Aßmus 4207e75e564SStefano Ceccherini // Allocate I/O context and initialize it with buffer 4217e75e564SStefano Ceccherini // and hook functions, pass ourself as cookie. 42284e70401SAdrien Destugues fIOContext = avio_alloc_context(buffer, kIOBufferSize, 1, this, 4237e75e564SStefano Ceccherini 0, _Write, _Seek); 4247e75e564SStefano Ceccherini if (fIOContext == NULL) { 4257e75e564SStefano Ceccherini TRACE("av_alloc_put_byte() failed!\n"); 4264384acf6SStephan Aßmus return B_ERROR; 4274384acf6SStephan Aßmus } 4284384acf6SStephan Aßmus 42954897d5cSStephan Aßmus // Setup I/O hooks. This seems to be enough. 430b95fa248SJérôme Duval fContext->pb = fIOContext; 4314384acf6SStephan Aßmus 43254897d5cSStephan Aßmus // Set the AVOutputFormat according to fileFormat... 43396590b5bSStephan Aßmus fContext->oformat = av_guess_format(fileFormat->short_name, 4344384acf6SStephan Aßmus fileFormat->file_extension, fileFormat->mime_type); 4354384acf6SStephan Aßmus if (fContext->oformat == NULL) { 4364384acf6SStephan Aßmus TRACE(" failed to find AVOuputFormat for %s\n", 4374384acf6SStephan Aßmus fileFormat->short_name); 4384384acf6SStephan Aßmus return B_NOT_SUPPORTED; 4394384acf6SStephan Aßmus } 4404384acf6SStephan Aßmus 4414384acf6SStephan Aßmus TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name, 4424384acf6SStephan Aßmus fContext->oformat->name); 4434384acf6SStephan Aßmus 4444384acf6SStephan Aßmus return B_OK; 4454384acf6SStephan Aßmus } 4464384acf6SStephan Aßmus 4474384acf6SStephan Aßmus 4484384acf6SStephan Aßmus status_t 4496ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright) 4506ac391b3SStephan Aßmus { 4516ac391b3SStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright); 4526ac391b3SStephan Aßmus 4536ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 4546ac391b3SStephan Aßmus } 4556ac391b3SStephan Aßmus 4566ac391b3SStephan Aßmus 4576ac391b3SStephan Aßmus status_t 4586ac391b3SStephan Aßmus AVFormatWriter::CommitHeader() 4596ac391b3SStephan Aßmus { 4606ac391b3SStephan Aßmus TRACE("AVFormatWriter::CommitHeader\n"); 4616ac391b3SStephan Aßmus 4624384acf6SStephan Aßmus if (fContext == NULL) 4634384acf6SStephan Aßmus return B_NO_INIT; 4644384acf6SStephan Aßmus 4654384acf6SStephan Aßmus if (fHeaderWritten) 4664384acf6SStephan Aßmus return B_NOT_ALLOWED; 4674384acf6SStephan Aßmus 46869372b55SStephan Aßmus #if OPEN_CODEC_CONTEXT 4693ca4a7b1SStephan Aßmus for (unsigned i = 0; i < fContext->nb_streams; i++) { 4703ca4a7b1SStephan Aßmus AVStream* stream = fContext->streams[i]; 4710c72a8aeSStephan Aßmus // NOTE: Experimental, this should not be needed. Especially, since 4720c72a8aeSStephan Aßmus // we have no idea (in the future) what CodecID some encoder uses, 4730c72a8aeSStephan Aßmus // it may be an encoder from a different plugin. 4740c72a8aeSStephan Aßmus AVCodecContext* codecContext = stream->codec; 4755e0e1689SAdrien Destugues codecContext->strict_std_compliance = -2; 4760c72a8aeSStephan Aßmus AVCodec* codec = avcodec_find_encoder(codecContext->codec_id); 477*9e5c6946SAdrien Destugues if (codec == NULL || avcodec_open2(codecContext, codec, NULL) < 0) { 4780c72a8aeSStephan Aßmus TRACE(" stream[%u] - failed to open AVCodecContext\n", i); 4790c72a8aeSStephan Aßmus } 4803ca4a7b1SStephan Aßmus TRACE(" stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n", 4813ca4a7b1SStephan Aßmus i, stream->time_base.num, stream->time_base.den, 4823ca4a7b1SStephan Aßmus stream->codec->time_base.num, stream->codec->time_base.den); 4833ca4a7b1SStephan Aßmus } 48469372b55SStephan Aßmus #endif 4853ca4a7b1SStephan Aßmus 48684e70401SAdrien Destugues int result = avformat_write_header(fContext, NULL); 4874384acf6SStephan Aßmus if (result < 0) 48884e70401SAdrien Destugues TRACE(" avformat_write_header(): %d\n", result); 48996590b5bSStephan Aßmus 49096590b5bSStephan Aßmus // We need to close the codecs we opened, even in case of failure. 4914384acf6SStephan Aßmus fHeaderWritten = true; 4924384acf6SStephan Aßmus 49396590b5bSStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 4943ca4a7b1SStephan Aßmus TRACE(" wrote header\n"); 4953ca4a7b1SStephan Aßmus for (unsigned i = 0; i < fContext->nb_streams; i++) { 4963ca4a7b1SStephan Aßmus AVStream* stream = fContext->streams[i]; 4973ca4a7b1SStephan Aßmus TRACE(" stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n", 4983ca4a7b1SStephan Aßmus i, stream->time_base.num, stream->time_base.den, 4993ca4a7b1SStephan Aßmus stream->codec->time_base.num, stream->codec->time_base.den); 5003ca4a7b1SStephan Aßmus } 501eb01f516SStephan Aßmus #endif // TRACE_AVFORMAT_WRITER 5023ca4a7b1SStephan Aßmus 5034384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 5046ac391b3SStephan Aßmus } 5056ac391b3SStephan Aßmus 5066ac391b3SStephan Aßmus 5076ac391b3SStephan Aßmus status_t 5086ac391b3SStephan Aßmus AVFormatWriter::Flush() 5096ac391b3SStephan Aßmus { 5106ac391b3SStephan Aßmus TRACE("AVFormatWriter::Flush\n"); 5116ac391b3SStephan Aßmus 5126ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 5136ac391b3SStephan Aßmus } 5146ac391b3SStephan Aßmus 5156ac391b3SStephan Aßmus 5166ac391b3SStephan Aßmus status_t 5176ac391b3SStephan Aßmus AVFormatWriter::Close() 5186ac391b3SStephan Aßmus { 5196ac391b3SStephan Aßmus TRACE("AVFormatWriter::Close\n"); 5206ac391b3SStephan Aßmus 5214384acf6SStephan Aßmus if (fContext == NULL) 5224384acf6SStephan Aßmus return B_NO_INIT; 5234384acf6SStephan Aßmus 5244384acf6SStephan Aßmus if (!fHeaderWritten) 5254384acf6SStephan Aßmus return B_NOT_ALLOWED; 5264384acf6SStephan Aßmus 5274384acf6SStephan Aßmus int result = av_write_trailer(fContext); 5284384acf6SStephan Aßmus if (result < 0) 5294384acf6SStephan Aßmus TRACE(" av_write_trailer(): %d\n", result); 5304384acf6SStephan Aßmus 5314384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 5326ac391b3SStephan Aßmus } 5336ac391b3SStephan Aßmus 5346ac391b3SStephan Aßmus 5356ac391b3SStephan Aßmus status_t 53669372b55SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, media_format* format, 53754897d5cSStephan Aßmus const media_codec_info* codecInfo) 5386ac391b3SStephan Aßmus { 5396ac391b3SStephan Aßmus TRACE("AVFormatWriter::AllocateCookie()\n"); 5406ac391b3SStephan Aßmus 5410c72a8aeSStephan Aßmus if (fHeaderWritten) 5420c72a8aeSStephan Aßmus return B_NOT_ALLOWED; 5430c72a8aeSStephan Aßmus 5446ac391b3SStephan Aßmus BAutolock _(fStreamLock); 5456ac391b3SStephan Aßmus 5466ac391b3SStephan Aßmus if (_cookie == NULL) 5476ac391b3SStephan Aßmus return B_BAD_VALUE; 5486ac391b3SStephan Aßmus 5494384acf6SStephan Aßmus StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext, 5504384acf6SStephan Aßmus &fStreamLock); 5514384acf6SStephan Aßmus 55254897d5cSStephan Aßmus status_t ret = cookie->Init(format, codecInfo); 55354897d5cSStephan Aßmus if (ret != B_OK) { 55454897d5cSStephan Aßmus delete cookie; 55554897d5cSStephan Aßmus return ret; 55654897d5cSStephan Aßmus } 55754897d5cSStephan Aßmus 55854897d5cSStephan Aßmus *_cookie = cookie; 55954897d5cSStephan Aßmus return B_OK; 5606ac391b3SStephan Aßmus } 5616ac391b3SStephan Aßmus 5626ac391b3SStephan Aßmus 5636ac391b3SStephan Aßmus status_t 5646ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie) 5656ac391b3SStephan Aßmus { 5666ac391b3SStephan Aßmus BAutolock _(fStreamLock); 5676ac391b3SStephan Aßmus 5686ac391b3SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5696ac391b3SStephan Aßmus delete cookie; 5706ac391b3SStephan Aßmus 5716ac391b3SStephan Aßmus return B_OK; 5726ac391b3SStephan Aßmus } 5736ac391b3SStephan Aßmus 5746ac391b3SStephan Aßmus 5756ac391b3SStephan Aßmus // #pragma mark - 5766ac391b3SStephan Aßmus 5776ac391b3SStephan Aßmus 5786ac391b3SStephan Aßmus status_t 579fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright) 580fa770e4cSStephan Aßmus { 581fa770e4cSStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright); 582fa770e4cSStephan Aßmus 583fa770e4cSStephan Aßmus return B_NOT_SUPPORTED; 584fa770e4cSStephan Aßmus } 585fa770e4cSStephan Aßmus 586fa770e4cSStephan Aßmus 587fa770e4cSStephan Aßmus status_t 5884384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code, 5896ac391b3SStephan Aßmus const void* data, size_t size, uint32 flags) 5906ac391b3SStephan Aßmus { 5916ac391b3SStephan Aßmus TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n", 5926ac391b3SStephan Aßmus code, data, size, flags); 5936ac391b3SStephan Aßmus 5944384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5954384acf6SStephan Aßmus return cookie->AddTrackInfo(code, data, size, flags); 5966ac391b3SStephan Aßmus } 5976ac391b3SStephan Aßmus 5986ac391b3SStephan Aßmus 5996ac391b3SStephan Aßmus status_t 6004384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer, 601fa770e4cSStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 6026ac391b3SStephan Aßmus { 6033ca4a7b1SStephan Aßmus TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer, 6043ca4a7b1SStephan Aßmus chunkSize, encodeInfo); 6056ac391b3SStephan Aßmus 6064384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 6074384acf6SStephan Aßmus return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo); 6086ac391b3SStephan Aßmus } 6096ac391b3SStephan Aßmus 6106ac391b3SStephan Aßmus 61154897d5cSStephan Aßmus // #pragma mark - I/O hooks 6122e9d65abSStephan Aßmus 6132e9d65abSStephan Aßmus 6142e9d65abSStephan Aßmus /*static*/ int 6157a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize) 6162e9d65abSStephan Aßmus { 6172e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n", 6182e9d65abSStephan Aßmus cookie, buffer, bufferSize); 6192e9d65abSStephan Aßmus 6202e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 6212e9d65abSStephan Aßmus 6222e9d65abSStephan Aßmus ssize_t written = writer->fTarget->Write(buffer, bufferSize); 6232e9d65abSStephan Aßmus 6242e9d65abSStephan Aßmus TRACE_IO(" written: %ld\n", written); 6252e9d65abSStephan Aßmus return (int)written; 6262e9d65abSStephan Aßmus 6272e9d65abSStephan Aßmus } 6282e9d65abSStephan Aßmus 6292e9d65abSStephan Aßmus 6302e9d65abSStephan Aßmus /*static*/ off_t 6312e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) 6322e9d65abSStephan Aßmus { 6332e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n", 6342e9d65abSStephan Aßmus cookie, offset, whence); 6352e9d65abSStephan Aßmus 6362e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 6372e9d65abSStephan Aßmus 6382e9d65abSStephan Aßmus BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget); 6392e9d65abSStephan Aßmus if (positionIO == NULL) 6402e9d65abSStephan Aßmus return -1; 6412e9d65abSStephan Aßmus 6424384acf6SStephan Aßmus // Support for special file size retrieval API without seeking anywhere: 6432e9d65abSStephan Aßmus if (whence == AVSEEK_SIZE) { 6442e9d65abSStephan Aßmus off_t size; 6452e9d65abSStephan Aßmus if (positionIO->GetSize(&size) == B_OK) 6462e9d65abSStephan Aßmus return size; 6472e9d65abSStephan Aßmus return -1; 6482e9d65abSStephan Aßmus } 6492e9d65abSStephan Aßmus 6502e9d65abSStephan Aßmus off_t position = positionIO->Seek(offset, whence); 6512e9d65abSStephan Aßmus TRACE_IO(" position: %lld\n", position); 6522e9d65abSStephan Aßmus if (position < 0) 6532e9d65abSStephan Aßmus return -1; 6542e9d65abSStephan Aßmus 6552e9d65abSStephan Aßmus return position; 6562e9d65abSStephan Aßmus } 6572e9d65abSStephan Aßmus 6582e9d65abSStephan Aßmus 659