16ac391b3SStephan Aßmus /* 26ac391b3SStephan Aßmus * Copyright 2009, 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 146ac391b3SStephan Aßmus #include <AutoDeleter.h> 156ac391b3SStephan Aßmus #include <Autolock.h> 166ac391b3SStephan Aßmus #include <ByteOrder.h> 176ac391b3SStephan Aßmus #include <DataIO.h> 186ac391b3SStephan Aßmus #include <MediaDefs.h> 196ac391b3SStephan Aßmus #include <MediaFormats.h> 206ac391b3SStephan Aßmus 216ac391b3SStephan Aßmus extern "C" { 226ac391b3SStephan Aßmus #include "avformat.h" 236ac391b3SStephan Aßmus } 246ac391b3SStephan Aßmus 256ac391b3SStephan Aßmus #include "DemuxerTable.h" 26eb01f516SStephan Aßmus #include "EncoderTable.h" 276ac391b3SStephan Aßmus #include "gfx_util.h" 286ac391b3SStephan Aßmus 296ac391b3SStephan Aßmus 30856d0bbfSStephan Aßmus //#define TRACE_AVFORMAT_WRITER 316ac391b3SStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 326ac391b3SStephan Aßmus # define TRACE printf 336ac391b3SStephan Aßmus # define TRACE_IO(a...) 343ca4a7b1SStephan Aßmus # define TRACE_PACKET printf 356ac391b3SStephan Aßmus #else 366ac391b3SStephan Aßmus # define TRACE(a...) 376ac391b3SStephan Aßmus # define TRACE_IO(a...) 386ac391b3SStephan Aßmus # define TRACE_PACKET(a...) 396ac391b3SStephan Aßmus #endif 406ac391b3SStephan Aßmus 416ac391b3SStephan Aßmus #define ERROR(a...) fprintf(stderr, a) 426ac391b3SStephan Aßmus 436ac391b3SStephan Aßmus 444384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024; 454384acf6SStephan Aßmus // TODO: This could depend on the BMediaFile creation flags, IIRC, 464384acf6SStephan Aßmus // they allow to specify a buffering mode. 474384acf6SStephan Aßmus 480c72a8aeSStephan Aßmus // NOTE: The following works around some weird bug in libavformat. We 490c72a8aeSStephan Aßmus // have to open the AVFormatContext->AVStream->AVCodecContext, even though 500c72a8aeSStephan Aßmus // we are not interested in donig any encoding here!! 510c72a8aeSStephan Aßmus #define OPEN_CODEC_CONTEXT 1 520c72a8aeSStephan Aßmus #define GET_CONTEXT_DEFAULTS 0 534384acf6SStephan Aßmus 544384acf6SStephan Aßmus 556ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter::StreamCookie 566ac391b3SStephan Aßmus 576ac391b3SStephan Aßmus 586ac391b3SStephan Aßmus class AVFormatWriter::StreamCookie { 596ac391b3SStephan Aßmus public: 604384acf6SStephan Aßmus StreamCookie(AVFormatContext* context, 616ac391b3SStephan Aßmus BLocker* streamLock); 626ac391b3SStephan Aßmus virtual ~StreamCookie(); 636ac391b3SStephan Aßmus 6454897d5cSStephan Aßmus status_t Init(const media_format* format, 6554897d5cSStephan Aßmus const media_codec_info* codecInfo); 664384acf6SStephan Aßmus 674384acf6SStephan Aßmus status_t WriteChunk(const void* chunkBuffer, 684384acf6SStephan Aßmus size_t chunkSize, 694384acf6SStephan Aßmus media_encode_info* encodeInfo); 704384acf6SStephan Aßmus 714384acf6SStephan Aßmus status_t AddTrackInfo(uint32 code, const void* data, 724384acf6SStephan Aßmus size_t size, uint32 flags); 734384acf6SStephan Aßmus 746ac391b3SStephan Aßmus private: 754384acf6SStephan Aßmus AVFormatContext* fContext; 764384acf6SStephan Aßmus AVStream* fStream; 7754897d5cSStephan Aßmus AVPacket fPacket; 7854897d5cSStephan Aßmus // Since different threads may write to the target, 796ac391b3SStephan Aßmus // we need to protect the file position and I/O by a lock. 806ac391b3SStephan Aßmus BLocker* fStreamLock; 816ac391b3SStephan Aßmus }; 826ac391b3SStephan Aßmus 836ac391b3SStephan Aßmus 846ac391b3SStephan Aßmus 854384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context, 866ac391b3SStephan Aßmus BLocker* streamLock) 876ac391b3SStephan Aßmus : 884384acf6SStephan Aßmus fContext(context), 894384acf6SStephan Aßmus fStream(NULL), 900c72a8aeSStephan Aßmus fStreamLock(streamLock) 916ac391b3SStephan Aßmus { 920c72a8aeSStephan Aßmus av_init_packet(&fPacket); 936ac391b3SStephan Aßmus } 946ac391b3SStephan Aßmus 956ac391b3SStephan Aßmus 966ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie() 976ac391b3SStephan Aßmus { 986ac391b3SStephan Aßmus } 996ac391b3SStephan Aßmus 1006ac391b3SStephan Aßmus 1014384acf6SStephan Aßmus status_t 10254897d5cSStephan Aßmus AVFormatWriter::StreamCookie::Init(const media_format* format, 10354897d5cSStephan Aßmus const media_codec_info* codecInfo) 1044384acf6SStephan Aßmus { 1056e567425SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::Init()\n"); 1064384acf6SStephan Aßmus 1074384acf6SStephan Aßmus BAutolock _(fStreamLock); 1084384acf6SStephan Aßmus 10954897d5cSStephan Aßmus fPacket.stream_index = fContext->nb_streams; 11054897d5cSStephan Aßmus fStream = av_new_stream(fContext, fPacket.stream_index); 1114384acf6SStephan Aßmus 1124384acf6SStephan Aßmus if (fStream == NULL) { 1134384acf6SStephan Aßmus TRACE(" failed to add new stream\n"); 1144384acf6SStephan Aßmus return B_ERROR; 1154384acf6SStephan Aßmus } 1164384acf6SStephan Aßmus 1170c72a8aeSStephan Aßmus // TRACE(" fStream->codec: %p\n", fStream->codec); 1180c72a8aeSStephan Aßmus // TODO: This is a hack for now! Use avcodec_find_encoder_by_name() 1190c72a8aeSStephan Aßmus // or something similar... 1200c72a8aeSStephan Aßmus fStream->codec->codec_id = (CodecID)codecInfo->sub_id; 121eb01f516SStephan Aßmus if (fStream->codec->codec_id == CODEC_ID_NONE) 122eb01f516SStephan Aßmus fStream->codec->codec_id = raw_audio_codec_id_for(*format); 1230c72a8aeSStephan Aßmus 12454897d5cSStephan Aßmus // Setup the stream according to the media format... 12554897d5cSStephan Aßmus if (format->type == B_MEDIA_RAW_VIDEO) { 1260c72a8aeSStephan Aßmus fStream->codec->codec_type = CODEC_TYPE_VIDEO; 1270c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS 1280c72a8aeSStephan Aßmus // NOTE: API example does not do this: 1290c72a8aeSStephan Aßmus avcodec_get_context_defaults(fStream->codec); 1300c72a8aeSStephan Aßmus #endif 13154897d5cSStephan Aßmus // frame rate 13254897d5cSStephan Aßmus fStream->codec->time_base.den = (int)format->u.raw_video.field_rate; 13354897d5cSStephan Aßmus fStream->codec->time_base.num = 1; 1340c72a8aeSStephan Aßmus // NOTE: API example does not do this: 1350c72a8aeSStephan Aßmus // fStream->r_frame_rate.den = (int)format->u.raw_video.field_rate; 1360c72a8aeSStephan Aßmus // fStream->r_frame_rate.num = 1; 1370c72a8aeSStephan Aßmus // fStream->time_base.den = (int)format->u.raw_video.field_rate; 1380c72a8aeSStephan Aßmus // fStream->time_base.num = 1; 13954897d5cSStephan Aßmus // video size 14054897d5cSStephan Aßmus fStream->codec->width = format->u.raw_video.display.line_width; 14154897d5cSStephan Aßmus fStream->codec->height = format->u.raw_video.display.line_count; 14254897d5cSStephan Aßmus // pixel aspect ratio 14354897d5cSStephan Aßmus fStream->sample_aspect_ratio.num 14454897d5cSStephan Aßmus = format->u.raw_video.pixel_width_aspect; 14554897d5cSStephan Aßmus fStream->sample_aspect_ratio.den 14654897d5cSStephan Aßmus = format->u.raw_video.pixel_height_aspect; 14754897d5cSStephan Aßmus if (fStream->sample_aspect_ratio.num == 0 14854897d5cSStephan Aßmus || fStream->sample_aspect_ratio.den == 0) { 14954897d5cSStephan Aßmus av_reduce(&fStream->sample_aspect_ratio.num, 15054897d5cSStephan Aßmus &fStream->sample_aspect_ratio.den, fStream->codec->width, 15154897d5cSStephan Aßmus fStream->codec->height, 255); 15254897d5cSStephan Aßmus } 15354897d5cSStephan Aßmus 15454897d5cSStephan Aßmus fStream->codec->sample_aspect_ratio = fStream->sample_aspect_ratio; 15554897d5cSStephan Aßmus // TODO: Don't hard code this... 15654897d5cSStephan Aßmus fStream->codec->pix_fmt = PIX_FMT_YUV420P; 1570c72a8aeSStephan Aßmus 1580c72a8aeSStephan Aßmus // Some formats want stream headers to be separate 1590c72a8aeSStephan Aßmus if ((fContext->oformat->flags & AVFMT_GLOBALHEADER) != 0) 1600c72a8aeSStephan Aßmus fStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 16154897d5cSStephan Aßmus } else if (format->type == B_MEDIA_RAW_AUDIO) { 1620c72a8aeSStephan Aßmus fStream->codec->codec_type = CODEC_TYPE_AUDIO; 1630c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS 1640c72a8aeSStephan Aßmus // NOTE: API example does not do this: 1650c72a8aeSStephan Aßmus avcodec_get_context_defaults(fStream->codec); 1660c72a8aeSStephan Aßmus #endif 1670c72a8aeSStephan Aßmus // frame rate 1680c72a8aeSStephan Aßmus fStream->codec->sample_rate = (int)format->u.raw_audio.frame_rate; 1690c72a8aeSStephan Aßmus 1703ca4a7b1SStephan Aßmus // channels 1713ca4a7b1SStephan Aßmus fStream->codec->channels = format->u.raw_audio.channel_count; 1723ca4a7b1SStephan Aßmus switch (format->u.raw_audio.format) { 1733ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_FLOAT: 1743ca4a7b1SStephan Aßmus fStream->codec->sample_fmt = SAMPLE_FMT_FLT; 1753ca4a7b1SStephan Aßmus break; 1763ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_DOUBLE: 1773ca4a7b1SStephan Aßmus fStream->codec->sample_fmt = SAMPLE_FMT_DBL; 1783ca4a7b1SStephan Aßmus break; 1793ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_INT: 1803ca4a7b1SStephan Aßmus fStream->codec->sample_fmt = SAMPLE_FMT_S32; 1813ca4a7b1SStephan Aßmus break; 1823ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_SHORT: 1833ca4a7b1SStephan Aßmus fStream->codec->sample_fmt = SAMPLE_FMT_S16; 1843ca4a7b1SStephan Aßmus break; 1853ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_UCHAR: 1863ca4a7b1SStephan Aßmus fStream->codec->sample_fmt = SAMPLE_FMT_U8; 1873ca4a7b1SStephan Aßmus break; 1883ca4a7b1SStephan Aßmus 1893ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_CHAR: 1903ca4a7b1SStephan Aßmus default: 1913ca4a7b1SStephan Aßmus return B_MEDIA_BAD_FORMAT; 1923ca4a7b1SStephan Aßmus break; 19354897d5cSStephan Aßmus } 1943ca4a7b1SStephan Aßmus if (format->u.raw_audio.channel_mask == 0) { 1953ca4a7b1SStephan Aßmus // guess the channel mask... 1963ca4a7b1SStephan Aßmus switch (format->u.raw_audio.channel_count) { 1973ca4a7b1SStephan Aßmus default: 1983ca4a7b1SStephan Aßmus case 2: 1993ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_STEREO; 2003ca4a7b1SStephan Aßmus break; 2013ca4a7b1SStephan Aßmus case 1: 2023ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_MONO; 2033ca4a7b1SStephan Aßmus break; 2043ca4a7b1SStephan Aßmus case 3: 2053ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_SURROUND; 2063ca4a7b1SStephan Aßmus break; 2073ca4a7b1SStephan Aßmus case 4: 2083ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_QUAD; 2093ca4a7b1SStephan Aßmus break; 2103ca4a7b1SStephan Aßmus case 5: 2113ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_5POINT0; 2123ca4a7b1SStephan Aßmus break; 2133ca4a7b1SStephan Aßmus case 6: 2143ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_5POINT1; 2153ca4a7b1SStephan Aßmus break; 2163ca4a7b1SStephan Aßmus case 8: 2173ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_7POINT1; 2183ca4a7b1SStephan Aßmus break; 2193ca4a7b1SStephan Aßmus case 10: 2203ca4a7b1SStephan Aßmus fStream->codec->channel_layout = CH_LAYOUT_7POINT1_WIDE; 2213ca4a7b1SStephan Aßmus break; 2223ca4a7b1SStephan Aßmus } 2233ca4a7b1SStephan Aßmus } else { 2243ca4a7b1SStephan Aßmus // The bits match 1:1 for media_multi_channels and FFmpeg defines. 2253ca4a7b1SStephan Aßmus fStream->codec->channel_layout = format->u.raw_audio.channel_mask; 2263ca4a7b1SStephan Aßmus } 2273ca4a7b1SStephan Aßmus } 2283ca4a7b1SStephan Aßmus 2293ca4a7b1SStephan Aßmus TRACE(" stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n", 2303ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 2313ca4a7b1SStephan Aßmus fStream->codec->time_base.num, fStream->codec->time_base.den); 23254897d5cSStephan Aßmus 2334384acf6SStephan Aßmus return B_OK; 2344384acf6SStephan Aßmus } 2354384acf6SStephan Aßmus 2364384acf6SStephan Aßmus 2374384acf6SStephan Aßmus status_t 2384384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer, 2394384acf6SStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 2404384acf6SStephan Aßmus { 241*96590b5bSStephan Aßmus TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, " 242*96590b5bSStephan Aßmus "start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize, 243*96590b5bSStephan Aßmus encodeInfo->start_time); 2444384acf6SStephan Aßmus 2454384acf6SStephan Aßmus BAutolock _(fStreamLock); 2464384acf6SStephan Aßmus 24754897d5cSStephan Aßmus // TODO: Probably the AVCodecEncoder needs to pass packet data 24854897d5cSStephan Aßmus // in encodeInfo... 24954897d5cSStephan Aßmus 25054897d5cSStephan Aßmus fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer); 25154897d5cSStephan Aßmus fPacket.size = chunkSize; 25254897d5cSStephan Aßmus 253*96590b5bSStephan Aßmus fPacket.pts = int64_t((double)encodeInfo->start_time 254*96590b5bSStephan Aßmus * fStream->time_base.den / (1000000.0 * fStream->time_base.num) 255*96590b5bSStephan Aßmus + 0.5); 2563ca4a7b1SStephan Aßmus TRACE_PACKET(" PTS: %lld (stream->time_base: (%d/%d), " 2573ca4a7b1SStephan Aßmus "codec->time_base: (%d/%d))\n", fPacket.pts, 2583ca4a7b1SStephan Aßmus fStream->time_base.num, fStream->time_base.den, 2593ca4a7b1SStephan Aßmus fStream->codec->time_base.num, fStream->codec->time_base.den); 2603ca4a7b1SStephan Aßmus 2613ca4a7b1SStephan Aßmus // From ffmpeg.c::do_audio_out(): 2620c72a8aeSStephan Aßmus // TODO: 2633ca4a7b1SStephan Aßmus // if (enc->coded_frame && enc->coded_frame->pts != AV_NOPTS_VALUE) 2643ca4a7b1SStephan Aßmus // fPacket.pts = av_rescale_q(enc->coded_frame->pts, 2653ca4a7b1SStephan Aßmus // enc->time_base, ost->st->time_base); 2663ca4a7b1SStephan Aßmus 2673ca4a7b1SStephan Aßmus 26854897d5cSStephan Aßmus #if 0 26954897d5cSStephan Aßmus // TODO: Eventually, we need to write interleaved packets, but 27054897d5cSStephan Aßmus // maybe we are only supposed to use this if we have actually 27154897d5cSStephan Aßmus // more than one stream. For the moment, this crashes in AVPacket 27254897d5cSStephan Aßmus // shuffling inside libavformat. Maybe if we want to use this, we 27354897d5cSStephan Aßmus // need to allocate a separate AVPacket and copy the chunk buffer. 27454897d5cSStephan Aßmus int result = av_interleaved_write_frame(fContext, &fPacket); 27554897d5cSStephan Aßmus if (result < 0) 27654897d5cSStephan Aßmus TRACE(" av_interleaved_write_frame(): %d\n", result); 27754897d5cSStephan Aßmus #else 27854897d5cSStephan Aßmus int result = av_write_frame(fContext, &fPacket); 27954897d5cSStephan Aßmus if (result < 0) 28054897d5cSStephan Aßmus TRACE(" av_write_frame(): %d\n", result); 28154897d5cSStephan Aßmus #endif 28254897d5cSStephan Aßmus 28354897d5cSStephan Aßmus return result == 0 ? B_OK : B_ERROR; 2844384acf6SStephan Aßmus } 2854384acf6SStephan Aßmus 2864384acf6SStephan Aßmus 2874384acf6SStephan Aßmus status_t 2884384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code, 2894384acf6SStephan Aßmus const void* data, size_t size, uint32 flags) 2904384acf6SStephan Aßmus { 2914384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n", 2924384acf6SStephan Aßmus code, data, size, flags); 2934384acf6SStephan Aßmus 2944384acf6SStephan Aßmus BAutolock _(fStreamLock); 2954384acf6SStephan Aßmus 2964384acf6SStephan Aßmus return B_NOT_SUPPORTED; 2974384acf6SStephan Aßmus } 2984384acf6SStephan Aßmus 2994384acf6SStephan Aßmus 3006ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter 3016ac391b3SStephan Aßmus 3026ac391b3SStephan Aßmus 3036ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter() 3046ac391b3SStephan Aßmus : 3054384acf6SStephan Aßmus fContext(avformat_alloc_context()), 3064384acf6SStephan Aßmus fHeaderWritten(false), 3076ac391b3SStephan Aßmus fStreamLock("stream lock") 3086ac391b3SStephan Aßmus { 3096ac391b3SStephan Aßmus TRACE("AVFormatWriter::AVFormatWriter\n"); 3106ac391b3SStephan Aßmus } 3116ac391b3SStephan Aßmus 3126ac391b3SStephan Aßmus 3136ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter() 3146ac391b3SStephan Aßmus { 3156ac391b3SStephan Aßmus TRACE("AVFormatWriter::~AVFormatWriter\n"); 3164384acf6SStephan Aßmus 3170c72a8aeSStephan Aßmus // Free the streams and close the AVCodecContexts 3180c72a8aeSStephan Aßmus for(unsigned i = 0; i < fContext->nb_streams; i++) { 3190c72a8aeSStephan Aßmus #if OPEN_CODEC_CONTEXT 3200c72a8aeSStephan Aßmus // We only need to close the AVCodecContext when we opened it. 321fac8f830SStefano Ceccherini // This is experimental, see CommitHeader(). 322fac8f830SStefano Ceccherini if (fHeaderWritten) 3230c72a8aeSStephan Aßmus avcodec_close(fContext->streams[i]->codec); 3240c72a8aeSStephan Aßmus #endif 3250c72a8aeSStephan Aßmus av_freep(&fContext->streams[i]->codec); 3260c72a8aeSStephan Aßmus av_freep(&fContext->streams[i]); 3270c72a8aeSStephan Aßmus } 3280c72a8aeSStephan Aßmus 3294384acf6SStephan Aßmus av_free(fContext); 330*96590b5bSStephan Aßmus av_free(fIOContext.buffer); 3316ac391b3SStephan Aßmus } 3326ac391b3SStephan Aßmus 3336ac391b3SStephan Aßmus 3346ac391b3SStephan Aßmus // #pragma mark - 3356ac391b3SStephan Aßmus 3366ac391b3SStephan Aßmus 3376ac391b3SStephan Aßmus status_t 3384384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat) 3394384acf6SStephan Aßmus { 3404384acf6SStephan Aßmus TRACE("AVFormatWriter::Init()\n"); 3414384acf6SStephan Aßmus 342*96590b5bSStephan Aßmus uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize)); 343*96590b5bSStephan Aßmus if (buffer == NULL) 3444384acf6SStephan Aßmus return B_NO_MEMORY; 3454384acf6SStephan Aßmus 3464384acf6SStephan Aßmus // Init I/O context with buffer and hook functions, pass ourself as 3474384acf6SStephan Aßmus // cookie. 348*96590b5bSStephan Aßmus if (init_put_byte(&fIOContext, buffer, kIOBufferSize, 1, this, 3494384acf6SStephan Aßmus 0, _Write, _Seek) != 0) { 3504384acf6SStephan Aßmus TRACE(" init_put_byte() failed!\n"); 3514384acf6SStephan Aßmus return B_ERROR; 3524384acf6SStephan Aßmus } 3534384acf6SStephan Aßmus 35454897d5cSStephan Aßmus // Setup I/O hooks. This seems to be enough. 3554384acf6SStephan Aßmus fContext->pb = &fIOContext; 3564384acf6SStephan Aßmus 35754897d5cSStephan Aßmus // Set the AVOutputFormat according to fileFormat... 358*96590b5bSStephan Aßmus fContext->oformat = av_guess_format(fileFormat->short_name, 3594384acf6SStephan Aßmus fileFormat->file_extension, fileFormat->mime_type); 3604384acf6SStephan Aßmus if (fContext->oformat == NULL) { 3614384acf6SStephan Aßmus TRACE(" failed to find AVOuputFormat for %s\n", 3624384acf6SStephan Aßmus fileFormat->short_name); 3634384acf6SStephan Aßmus return B_NOT_SUPPORTED; 3644384acf6SStephan Aßmus } 3654384acf6SStephan Aßmus 3664384acf6SStephan Aßmus TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name, 3674384acf6SStephan Aßmus fContext->oformat->name); 3684384acf6SStephan Aßmus 3694384acf6SStephan Aßmus return B_OK; 3704384acf6SStephan Aßmus } 3714384acf6SStephan Aßmus 3724384acf6SStephan Aßmus 3734384acf6SStephan Aßmus status_t 3746ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright) 3756ac391b3SStephan Aßmus { 3766ac391b3SStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright); 3776ac391b3SStephan Aßmus 3786ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 3796ac391b3SStephan Aßmus } 3806ac391b3SStephan Aßmus 3816ac391b3SStephan Aßmus 3826ac391b3SStephan Aßmus status_t 3836ac391b3SStephan Aßmus AVFormatWriter::CommitHeader() 3846ac391b3SStephan Aßmus { 3856ac391b3SStephan Aßmus TRACE("AVFormatWriter::CommitHeader\n"); 3866ac391b3SStephan Aßmus 3874384acf6SStephan Aßmus if (fContext == NULL) 3884384acf6SStephan Aßmus return B_NO_INIT; 3894384acf6SStephan Aßmus 3904384acf6SStephan Aßmus if (fHeaderWritten) 3914384acf6SStephan Aßmus return B_NOT_ALLOWED; 3924384acf6SStephan Aßmus 3930c72a8aeSStephan Aßmus // According to output_example.c, the output parameters must be set even 3940c72a8aeSStephan Aßmus // if none are specified. In the example, this call is used after the 3950c72a8aeSStephan Aßmus // streams have been created. 3960c72a8aeSStephan Aßmus if (av_set_parameters(fContext, NULL) < 0) 3970c72a8aeSStephan Aßmus return B_ERROR; 3980c72a8aeSStephan Aßmus 3993ca4a7b1SStephan Aßmus for (unsigned i = 0; i < fContext->nb_streams; i++) { 4003ca4a7b1SStephan Aßmus AVStream* stream = fContext->streams[i]; 4010c72a8aeSStephan Aßmus #if OPEN_CODEC_CONTEXT 4020c72a8aeSStephan Aßmus // NOTE: Experimental, this should not be needed. Especially, since 4030c72a8aeSStephan Aßmus // we have no idea (in the future) what CodecID some encoder uses, 4040c72a8aeSStephan Aßmus // it may be an encoder from a different plugin. 4050c72a8aeSStephan Aßmus AVCodecContext* codecContext = stream->codec; 4060c72a8aeSStephan Aßmus AVCodec* codec = avcodec_find_encoder(codecContext->codec_id); 4070c72a8aeSStephan Aßmus if (codec == NULL || avcodec_open(codecContext, codec) < 0) { 4080c72a8aeSStephan Aßmus TRACE(" stream[%u] - failed to open AVCodecContext\n", i); 4090c72a8aeSStephan Aßmus } 4100c72a8aeSStephan Aßmus #endif 4113ca4a7b1SStephan Aßmus TRACE(" stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n", 4123ca4a7b1SStephan Aßmus i, stream->time_base.num, stream->time_base.den, 4133ca4a7b1SStephan Aßmus stream->codec->time_base.num, stream->codec->time_base.den); 4143ca4a7b1SStephan Aßmus } 4153ca4a7b1SStephan Aßmus 4164384acf6SStephan Aßmus int result = av_write_header(fContext); 4174384acf6SStephan Aßmus if (result < 0) 4184384acf6SStephan Aßmus TRACE(" av_write_header(): %d\n", result); 419*96590b5bSStephan Aßmus 420*96590b5bSStephan Aßmus // We need to close the codecs we opened, even in case of failure. 4214384acf6SStephan Aßmus fHeaderWritten = true; 4224384acf6SStephan Aßmus 423*96590b5bSStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER 4243ca4a7b1SStephan Aßmus TRACE(" wrote header\n"); 4253ca4a7b1SStephan Aßmus for (unsigned i = 0; i < fContext->nb_streams; i++) { 4263ca4a7b1SStephan Aßmus AVStream* stream = fContext->streams[i]; 4273ca4a7b1SStephan Aßmus TRACE(" stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n", 4283ca4a7b1SStephan Aßmus i, stream->time_base.num, stream->time_base.den, 4293ca4a7b1SStephan Aßmus stream->codec->time_base.num, stream->codec->time_base.den); 4303ca4a7b1SStephan Aßmus } 431eb01f516SStephan Aßmus #endif // TRACE_AVFORMAT_WRITER 4323ca4a7b1SStephan Aßmus 4334384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 4346ac391b3SStephan Aßmus } 4356ac391b3SStephan Aßmus 4366ac391b3SStephan Aßmus 4376ac391b3SStephan Aßmus status_t 4386ac391b3SStephan Aßmus AVFormatWriter::Flush() 4396ac391b3SStephan Aßmus { 4406ac391b3SStephan Aßmus TRACE("AVFormatWriter::Flush\n"); 4416ac391b3SStephan Aßmus 4426ac391b3SStephan Aßmus return B_NOT_SUPPORTED; 4436ac391b3SStephan Aßmus } 4446ac391b3SStephan Aßmus 4456ac391b3SStephan Aßmus 4466ac391b3SStephan Aßmus status_t 4476ac391b3SStephan Aßmus AVFormatWriter::Close() 4486ac391b3SStephan Aßmus { 4496ac391b3SStephan Aßmus TRACE("AVFormatWriter::Close\n"); 4506ac391b3SStephan Aßmus 4514384acf6SStephan Aßmus if (fContext == NULL) 4524384acf6SStephan Aßmus return B_NO_INIT; 4534384acf6SStephan Aßmus 4544384acf6SStephan Aßmus if (!fHeaderWritten) 4554384acf6SStephan Aßmus return B_NOT_ALLOWED; 4564384acf6SStephan Aßmus 4574384acf6SStephan Aßmus int result = av_write_trailer(fContext); 4584384acf6SStephan Aßmus if (result < 0) 4594384acf6SStephan Aßmus TRACE(" av_write_trailer(): %d\n", result); 4604384acf6SStephan Aßmus 4614384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 4626ac391b3SStephan Aßmus } 4636ac391b3SStephan Aßmus 4646ac391b3SStephan Aßmus 4656ac391b3SStephan Aßmus status_t 46654897d5cSStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format, 46754897d5cSStephan Aßmus const media_codec_info* codecInfo) 4686ac391b3SStephan Aßmus { 4696ac391b3SStephan Aßmus TRACE("AVFormatWriter::AllocateCookie()\n"); 4706ac391b3SStephan Aßmus 4710c72a8aeSStephan Aßmus if (fHeaderWritten) 4720c72a8aeSStephan Aßmus return B_NOT_ALLOWED; 4730c72a8aeSStephan Aßmus 4746ac391b3SStephan Aßmus BAutolock _(fStreamLock); 4756ac391b3SStephan Aßmus 4766ac391b3SStephan Aßmus if (_cookie == NULL) 4776ac391b3SStephan Aßmus return B_BAD_VALUE; 4786ac391b3SStephan Aßmus 4794384acf6SStephan Aßmus StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext, 4804384acf6SStephan Aßmus &fStreamLock); 4814384acf6SStephan Aßmus 48254897d5cSStephan Aßmus status_t ret = cookie->Init(format, codecInfo); 48354897d5cSStephan Aßmus if (ret != B_OK) { 48454897d5cSStephan Aßmus delete cookie; 48554897d5cSStephan Aßmus return ret; 48654897d5cSStephan Aßmus } 48754897d5cSStephan Aßmus 48854897d5cSStephan Aßmus *_cookie = cookie; 48954897d5cSStephan Aßmus return B_OK; 4906ac391b3SStephan Aßmus } 4916ac391b3SStephan Aßmus 4926ac391b3SStephan Aßmus 4936ac391b3SStephan Aßmus status_t 4946ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie) 4956ac391b3SStephan Aßmus { 4966ac391b3SStephan Aßmus BAutolock _(fStreamLock); 4976ac391b3SStephan Aßmus 4986ac391b3SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 4996ac391b3SStephan Aßmus delete cookie; 5006ac391b3SStephan Aßmus 5016ac391b3SStephan Aßmus return B_OK; 5026ac391b3SStephan Aßmus } 5036ac391b3SStephan Aßmus 5046ac391b3SStephan Aßmus 5056ac391b3SStephan Aßmus // #pragma mark - 5066ac391b3SStephan Aßmus 5076ac391b3SStephan Aßmus 5086ac391b3SStephan Aßmus status_t 509fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright) 510fa770e4cSStephan Aßmus { 511fa770e4cSStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright); 512fa770e4cSStephan Aßmus 513fa770e4cSStephan Aßmus return B_NOT_SUPPORTED; 514fa770e4cSStephan Aßmus } 515fa770e4cSStephan Aßmus 516fa770e4cSStephan Aßmus 517fa770e4cSStephan Aßmus status_t 5184384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code, 5196ac391b3SStephan Aßmus const void* data, size_t size, uint32 flags) 5206ac391b3SStephan Aßmus { 5216ac391b3SStephan Aßmus TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n", 5226ac391b3SStephan Aßmus code, data, size, flags); 5236ac391b3SStephan Aßmus 5244384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5254384acf6SStephan Aßmus return cookie->AddTrackInfo(code, data, size, flags); 5266ac391b3SStephan Aßmus } 5276ac391b3SStephan Aßmus 5286ac391b3SStephan Aßmus 5296ac391b3SStephan Aßmus status_t 5304384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer, 531fa770e4cSStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 5326ac391b3SStephan Aßmus { 5333ca4a7b1SStephan Aßmus TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer, 5343ca4a7b1SStephan Aßmus chunkSize, encodeInfo); 5356ac391b3SStephan Aßmus 5364384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 5374384acf6SStephan Aßmus return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo); 5386ac391b3SStephan Aßmus } 5396ac391b3SStephan Aßmus 5406ac391b3SStephan Aßmus 54154897d5cSStephan Aßmus // #pragma mark - I/O hooks 5422e9d65abSStephan Aßmus 5432e9d65abSStephan Aßmus 5442e9d65abSStephan Aßmus /*static*/ int 5457a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize) 5462e9d65abSStephan Aßmus { 5472e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n", 5482e9d65abSStephan Aßmus cookie, buffer, bufferSize); 5492e9d65abSStephan Aßmus 5502e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 5512e9d65abSStephan Aßmus 5522e9d65abSStephan Aßmus ssize_t written = writer->fTarget->Write(buffer, bufferSize); 5532e9d65abSStephan Aßmus 5542e9d65abSStephan Aßmus TRACE_IO(" written: %ld\n", written); 5552e9d65abSStephan Aßmus return (int)written; 5562e9d65abSStephan Aßmus 5572e9d65abSStephan Aßmus } 5582e9d65abSStephan Aßmus 5592e9d65abSStephan Aßmus 5602e9d65abSStephan Aßmus /*static*/ off_t 5612e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) 5622e9d65abSStephan Aßmus { 5632e9d65abSStephan Aßmus TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n", 5642e9d65abSStephan Aßmus cookie, offset, whence); 5652e9d65abSStephan Aßmus 5662e9d65abSStephan Aßmus AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie); 5672e9d65abSStephan Aßmus 5682e9d65abSStephan Aßmus BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget); 5692e9d65abSStephan Aßmus if (positionIO == NULL) 5702e9d65abSStephan Aßmus return -1; 5712e9d65abSStephan Aßmus 5724384acf6SStephan Aßmus // Support for special file size retrieval API without seeking anywhere: 5732e9d65abSStephan Aßmus if (whence == AVSEEK_SIZE) { 5742e9d65abSStephan Aßmus off_t size; 5752e9d65abSStephan Aßmus if (positionIO->GetSize(&size) == B_OK) 5762e9d65abSStephan Aßmus return size; 5772e9d65abSStephan Aßmus return -1; 5782e9d65abSStephan Aßmus } 5792e9d65abSStephan Aßmus 5802e9d65abSStephan Aßmus off_t position = positionIO->Seek(offset, whence); 5812e9d65abSStephan Aßmus TRACE_IO(" position: %lld\n", position); 5822e9d65abSStephan Aßmus if (position < 0) 5832e9d65abSStephan Aßmus return -1; 5842e9d65abSStephan Aßmus 5852e9d65abSStephan Aßmus return position; 5862e9d65abSStephan Aßmus } 5872e9d65abSStephan Aßmus 5882e9d65abSStephan Aßmus 589