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;
759bf436a2SPulkoMandy 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
StreamCookie(AVFormatContext * context,BLocker * streamLock)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 {
909bf436a2SPulkoMandy fPacket = av_packet_alloc();
916ac391b3SStephan Aßmus }
926ac391b3SStephan Aßmus
936ac391b3SStephan Aßmus
~StreamCookie()946ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie()
956ac391b3SStephan Aßmus {
969a6a570aSAdrien Destugues // fStream is freed automatically when the codec context is closed
979bf436a2SPulkoMandy av_packet_free(&fPacket);
986ac391b3SStephan Aßmus }
996ac391b3SStephan Aßmus
1006ac391b3SStephan Aßmus
101*1774dd5eSPulkoMandy static void
set_channel_count(AVCodecParameters * context,int count)102*1774dd5eSPulkoMandy set_channel_count(AVCodecParameters* context, int count)
103*1774dd5eSPulkoMandy {
104*1774dd5eSPulkoMandy #if LIBAVCODEC_VERSION_MAJOR >= 60
105*1774dd5eSPulkoMandy context->ch_layout.nb_channels = count;
106*1774dd5eSPulkoMandy #else
107*1774dd5eSPulkoMandy context->channels = count;
108*1774dd5eSPulkoMandy #endif
109*1774dd5eSPulkoMandy }
110*1774dd5eSPulkoMandy
111*1774dd5eSPulkoMandy
1124384acf6SStephan Aßmus status_t
Init(media_format * format,const media_codec_info * codecInfo)11369372b55SStephan Aßmus AVFormatWriter::StreamCookie::Init(media_format* format,
11454897d5cSStephan Aßmus const media_codec_info* codecInfo)
1154384acf6SStephan Aßmus {
1166e567425SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::Init()\n");
1174384acf6SStephan Aßmus
1184384acf6SStephan Aßmus BAutolock _(fStreamLock);
1194384acf6SStephan Aßmus
1209bf436a2SPulkoMandy fPacket->stream_index = fFormatContext->nb_streams;
121ee9d0e02SBarrett17 fStream = avformat_new_stream(fFormatContext, NULL);
1224384acf6SStephan Aßmus
1234384acf6SStephan Aßmus if (fStream == NULL) {
1244384acf6SStephan Aßmus TRACE(" failed to add new stream\n");
1254384acf6SStephan Aßmus return B_ERROR;
1264384acf6SStephan Aßmus }
1274384acf6SStephan Aßmus
1289bf436a2SPulkoMandy fStream->id = fPacket->stream_index;
1298d721650SJackBurton79
130428d87c5SBarrett17 // TRACE(" fStream->codecpar: %p\n", fStream->codecpar);
1310c72a8aeSStephan Aßmus // TODO: This is a hack for now! Use avcodec_find_encoder_by_name()
1320c72a8aeSStephan Aßmus // or something similar...
133428d87c5SBarrett17 fStream->codecpar->codec_id = (CodecID)codecInfo->sub_id;
134428d87c5SBarrett17 if (fStream->codecpar->codec_id == AV_CODEC_ID_NONE)
135428d87c5SBarrett17 fStream->codecpar->codec_id = raw_audio_codec_id_for(*format);
1360c72a8aeSStephan Aßmus
13754897d5cSStephan Aßmus // Setup the stream according to the media format...
13854897d5cSStephan Aßmus if (format->type == B_MEDIA_RAW_VIDEO) {
139428d87c5SBarrett17 fStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
1405ba361f8SBarrett17 fStream->time_base.den = (int)format->u.raw_video.field_rate;
1415ba361f8SBarrett17 fStream->time_base.num = 1;
1425ba361f8SBarrett17
14354897d5cSStephan Aßmus // video size
144428d87c5SBarrett17 fStream->codecpar->width = format->u.raw_video.display.line_width;
145428d87c5SBarrett17 fStream->codecpar->height = format->u.raw_video.display.line_count;
14654897d5cSStephan Aßmus // pixel aspect ratio
14754897d5cSStephan Aßmus fStream->sample_aspect_ratio.num
14854897d5cSStephan Aßmus = format->u.raw_video.pixel_width_aspect;
14954897d5cSStephan Aßmus fStream->sample_aspect_ratio.den
15054897d5cSStephan Aßmus = format->u.raw_video.pixel_height_aspect;
15154897d5cSStephan Aßmus if (fStream->sample_aspect_ratio.num == 0
15254897d5cSStephan Aßmus || fStream->sample_aspect_ratio.den == 0) {
15354897d5cSStephan Aßmus av_reduce(&fStream->sample_aspect_ratio.num,
154428d87c5SBarrett17 &fStream->sample_aspect_ratio.den, fStream->codecpar->width,
155428d87c5SBarrett17 fStream->codecpar->height, 255);
15654897d5cSStephan Aßmus }
15754897d5cSStephan Aßmus
158428d87c5SBarrett17 fStream->codecpar->sample_aspect_ratio = fStream->sample_aspect_ratio;
15969372b55SStephan Aßmus
16069372b55SStephan Aßmus // Use the last supported pixel format of the AVCodec, which we hope
16169372b55SStephan Aßmus // is the one with the best quality (true for all currently supported
16269372b55SStephan Aßmus // encoders).
163428d87c5SBarrett17 // AVCodec* codec = fStream->codecpar->codec;
164bb188e4dSStephan Aßmus // for (int i = 0; codec->pix_fmts[i] != PIX_FMT_NONE; i++)
165428d87c5SBarrett17 // fStream->codecpar->pix_fmt = codec->pix_fmts[i];
166428d87c5SBarrett17 fStream->codecpar->format = AV_PIX_FMT_YUV420P;
16769372b55SStephan Aßmus
16854897d5cSStephan Aßmus } else if (format->type == B_MEDIA_RAW_AUDIO) {
169428d87c5SBarrett17 fStream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
170428d87c5SBarrett17
1710c72a8aeSStephan Aßmus // frame rate
172428d87c5SBarrett17 fStream->codecpar->sample_rate = (int)format->u.raw_audio.frame_rate;
1730c72a8aeSStephan Aßmus
1743ca4a7b1SStephan Aßmus // channels
175*1774dd5eSPulkoMandy set_channel_count(fStream->codecpar, format->u.raw_audio.channel_count);
1765e0e1689SAdrien Destugues
1775e0e1689SAdrien Destugues // set fStream to the audio format we want to use. This is only a hint
1785e0e1689SAdrien Destugues // (each encoder has a different set of accepted formats)
1793ca4a7b1SStephan Aßmus switch (format->u.raw_audio.format) {
1803ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_FLOAT:
181428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_FLT;
1823ca4a7b1SStephan Aßmus break;
1833ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_DOUBLE:
184428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_DBL;
1853ca4a7b1SStephan Aßmus break;
1863ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_INT:
187428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_S32;
1883ca4a7b1SStephan Aßmus break;
1893ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_SHORT:
190428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_S16;
1913ca4a7b1SStephan Aßmus break;
1923ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_UCHAR:
193428d87c5SBarrett17 fStream->codecpar->format = AV_SAMPLE_FMT_U8;
1943ca4a7b1SStephan Aßmus break;
1953ca4a7b1SStephan Aßmus
1963ca4a7b1SStephan Aßmus case media_raw_audio_format::B_AUDIO_CHAR:
1973ca4a7b1SStephan Aßmus default:
1983ca4a7b1SStephan Aßmus return B_MEDIA_BAD_FORMAT;
1993ca4a7b1SStephan Aßmus break;
20054897d5cSStephan Aßmus }
2015e0e1689SAdrien Destugues
2025e0e1689SAdrien Destugues // Now negociate the actual format with the encoder
2035e0e1689SAdrien Destugues // First check if the requested format is acceptable
204926289d3SPulkoMandy const AVCodec* codec = avcodec_find_encoder(fStream->codecpar->codec_id);
2059e5c6946SAdrien Destugues
2069e5c6946SAdrien Destugues if (codec == NULL)
2079e5c6946SAdrien Destugues return B_MEDIA_BAD_FORMAT;
2089e5c6946SAdrien Destugues
2095e0e1689SAdrien Destugues const enum AVSampleFormat *p = codec->sample_fmts;
2105e0e1689SAdrien Destugues for (; *p != -1; p++) {
211428d87c5SBarrett17 if (*p == fStream->codecpar->format)
2125e0e1689SAdrien Destugues break;
2135e0e1689SAdrien Destugues }
2145e0e1689SAdrien Destugues // If not, force one of the acceptable ones
2155e0e1689SAdrien Destugues if (*p == -1) {
216428d87c5SBarrett17 fStream->codecpar->format = codec->sample_fmts[0];
2175e0e1689SAdrien Destugues
2185e0e1689SAdrien Destugues // And finally set the format struct to the accepted format. It is
2195e0e1689SAdrien Destugues // then up to the caller to make sure we get data matching that
2205e0e1689SAdrien Destugues // format.
221428d87c5SBarrett17 switch (fStream->codecpar->format) {
2225e0e1689SAdrien Destugues case AV_SAMPLE_FMT_FLT:
2235e0e1689SAdrien Destugues format->u.raw_audio.format
2245e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_FLOAT;
2255e0e1689SAdrien Destugues break;
2265e0e1689SAdrien Destugues case AV_SAMPLE_FMT_DBL:
2275e0e1689SAdrien Destugues format->u.raw_audio.format
2285e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_DOUBLE;
2295e0e1689SAdrien Destugues break;
2305e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S32:
2315e0e1689SAdrien Destugues format->u.raw_audio.format
2325e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_INT;
2335e0e1689SAdrien Destugues break;
2345e0e1689SAdrien Destugues case AV_SAMPLE_FMT_S16:
2355e0e1689SAdrien Destugues format->u.raw_audio.format
2365e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_SHORT;
2375e0e1689SAdrien Destugues break;
2385e0e1689SAdrien Destugues case AV_SAMPLE_FMT_U8:
2395e0e1689SAdrien Destugues format->u.raw_audio.format
2405e0e1689SAdrien Destugues = media_raw_audio_format::B_AUDIO_UCHAR;
2415e0e1689SAdrien Destugues break;
2425e0e1689SAdrien Destugues default:
2435e0e1689SAdrien Destugues return B_MEDIA_BAD_FORMAT;
2445e0e1689SAdrien Destugues break;
2455e0e1689SAdrien Destugues }
2465e0e1689SAdrien Destugues }
2475e0e1689SAdrien Destugues
248*1774dd5eSPulkoMandy #if LIBAVCODEC_VERSION_MAJOR >= 60
249*1774dd5eSPulkoMandy if (format->u.raw_audio.channel_mask == 0) {
250*1774dd5eSPulkoMandy // guess the channel mask...
251*1774dd5eSPulkoMandy av_channel_layout_default(&fStream->codecpar->ch_layout,
252*1774dd5eSPulkoMandy format->u.raw_audio.channel_count);
253*1774dd5eSPulkoMandy } else {
254*1774dd5eSPulkoMandy // The bits match 1:1 for media_multi_channels and FFmpeg defines.
255*1774dd5eSPulkoMandy av_channel_layout_from_mask(&fStream->codecpar->ch_layout,
256*1774dd5eSPulkoMandy format->u.raw_audio.channel_mask);
257*1774dd5eSPulkoMandy }
258*1774dd5eSPulkoMandy #else
2593ca4a7b1SStephan Aßmus if (format->u.raw_audio.channel_mask == 0) {
2603ca4a7b1SStephan Aßmus // guess the channel mask...
2613ca4a7b1SStephan Aßmus switch (format->u.raw_audio.channel_count) {
2623ca4a7b1SStephan Aßmus default:
2633ca4a7b1SStephan Aßmus case 2:
264428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
2653ca4a7b1SStephan Aßmus break;
2663ca4a7b1SStephan Aßmus case 1:
267428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
2683ca4a7b1SStephan Aßmus break;
2693ca4a7b1SStephan Aßmus case 3:
270428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_SURROUND;
2713ca4a7b1SStephan Aßmus break;
2723ca4a7b1SStephan Aßmus case 4:
273428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_QUAD;
2743ca4a7b1SStephan Aßmus break;
2753ca4a7b1SStephan Aßmus case 5:
276428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT0;
2773ca4a7b1SStephan Aßmus break;
2783ca4a7b1SStephan Aßmus case 6:
279428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT1;
2803ca4a7b1SStephan Aßmus break;
2813ca4a7b1SStephan Aßmus case 8:
282428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1;
2833ca4a7b1SStephan Aßmus break;
2843ca4a7b1SStephan Aßmus case 10:
285428d87c5SBarrett17 fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE;
2863ca4a7b1SStephan Aßmus break;
2873ca4a7b1SStephan Aßmus }
2883ca4a7b1SStephan Aßmus } else {
2893ca4a7b1SStephan Aßmus // The bits match 1:1 for media_multi_channels and FFmpeg defines.
290428d87c5SBarrett17 fStream->codecpar->channel_layout = format->u.raw_audio.channel_mask;
2913ca4a7b1SStephan Aßmus }
292*1774dd5eSPulkoMandy #endif
2933ca4a7b1SStephan Aßmus }
2943ca4a7b1SStephan Aßmus
29583e06e62SPulkoMandy TRACE(" stream->time_base: (%d/%d)\n",
29683e06e62SPulkoMandy fStream->time_base.num, fStream->time_base.den);
29754897d5cSStephan Aßmus
298657983b8SStephan Aßmus #if 0
299657983b8SStephan Aßmus // Write the AVCodecContext pointer to the user data section of the
300657983b8SStephan Aßmus // media_format. For some encoders, it seems to be necessary to use
301657983b8SStephan Aßmus // the AVCodecContext of the AVStream in order to successfully encode
302657983b8SStephan Aßmus // anything and write valid media files. For example some codecs need
303657983b8SStephan Aßmus // to store meta data or global data in the container.
304657983b8SStephan Aßmus app_info appInfo;
305657983b8SStephan Aßmus if (be_app->GetAppInfo(&appInfo) == B_OK) {
306657983b8SStephan Aßmus uchar* userData = format->user_data;
307657983b8SStephan Aßmus *(uint32*)userData = 'ffmp';
308657983b8SStephan Aßmus userData += sizeof(uint32);
309657983b8SStephan Aßmus *(team_id*)userData = appInfo.team;
310657983b8SStephan Aßmus userData += sizeof(team_id);
311657983b8SStephan Aßmus *(AVCodecContext**)userData = fStream->codec;
312657983b8SStephan Aßmus }
313657983b8SStephan Aßmus #endif
314657983b8SStephan Aßmus
3154384acf6SStephan Aßmus return B_OK;
3164384acf6SStephan Aßmus }
3174384acf6SStephan Aßmus
3184384acf6SStephan Aßmus
3194384acf6SStephan Aßmus status_t
WriteChunk(const void * chunkBuffer,size_t chunkSize,media_encode_info * encodeInfo)3204384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
3214384acf6SStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo)
3224384acf6SStephan Aßmus {
32396590b5bSStephan Aßmus TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, "
32483e06e62SPulkoMandy "start_time: %" B_PRIdBIGTIME ")\n", fStream->index, chunkBuffer, chunkSize,
32596590b5bSStephan Aßmus encodeInfo->start_time);
3264384acf6SStephan Aßmus
3274384acf6SStephan Aßmus BAutolock _(fStreamLock);
3284384acf6SStephan Aßmus
3299bf436a2SPulkoMandy fPacket->data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
3309bf436a2SPulkoMandy fPacket->size = chunkSize;
3319bf436a2SPulkoMandy fPacket->stream_index = fStream->index;
33254897d5cSStephan Aßmus
3339bf436a2SPulkoMandy fPacket->pts = int64_t((double)encodeInfo->start_time
33496590b5bSStephan Aßmus * fStream->time_base.den / (1000000.0 * fStream->time_base.num)
33596590b5bSStephan Aßmus + 0.5);
33669372b55SStephan Aßmus
3379bf436a2SPulkoMandy fPacket->dts = fPacket->pts;
338e93fce62SBarrett17
3399bf436a2SPulkoMandy fPacket->flags = 0;
34069372b55SStephan Aßmus if ((encodeInfo->flags & B_MEDIA_KEY_FRAME) != 0)
3419bf436a2SPulkoMandy fPacket->flags |= AV_PKT_FLAG_KEY;
34269372b55SStephan Aßmus
3439bf436a2SPulkoMandy TRACE_PACKET(" PTS: %" PRId64 " (stream->time_base: (%d/%d)\n", fPacket->pts,
34483e06e62SPulkoMandy fStream->time_base.num, fStream->time_base.den);
3453ca4a7b1SStephan Aßmus
34654897d5cSStephan Aßmus #if 0
34754897d5cSStephan Aßmus // TODO: Eventually, we need to write interleaved packets, but
34854897d5cSStephan Aßmus // maybe we are only supposed to use this if we have actually
34954897d5cSStephan Aßmus // more than one stream. For the moment, this crashes in AVPacket
35054897d5cSStephan Aßmus // shuffling inside libavformat. Maybe if we want to use this, we
35154897d5cSStephan Aßmus // need to allocate a separate AVPacket and copy the chunk buffer.
3529bf436a2SPulkoMandy int result = av_interleaved_write_frame(fFormatContext, fPacket);
35354897d5cSStephan Aßmus if (result < 0)
35454897d5cSStephan Aßmus TRACE(" av_interleaved_write_frame(): %d\n", result);
35554897d5cSStephan Aßmus #else
3569bf436a2SPulkoMandy int result = av_write_frame(fFormatContext, fPacket);
35754897d5cSStephan Aßmus if (result < 0)
35854897d5cSStephan Aßmus TRACE(" av_write_frame(): %d\n", result);
35954897d5cSStephan Aßmus #endif
36054897d5cSStephan Aßmus
36154897d5cSStephan Aßmus return result == 0 ? B_OK : B_ERROR;
3624384acf6SStephan Aßmus }
3634384acf6SStephan Aßmus
3644384acf6SStephan Aßmus
3654384acf6SStephan Aßmus status_t
AddTrackInfo(uint32 code,const void * data,size_t size,uint32 flags)3664384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code,
3674384acf6SStephan Aßmus const void* data, size_t size, uint32 flags)
3684384acf6SStephan Aßmus {
36983e06e62SPulkoMandy TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%" B_PRIu32 ", %p, %ld, %" B_PRIu32 ")\n",
3704384acf6SStephan Aßmus code, data, size, flags);
3714384acf6SStephan Aßmus
3724384acf6SStephan Aßmus BAutolock _(fStreamLock);
3734384acf6SStephan Aßmus
3744384acf6SStephan Aßmus return B_NOT_SUPPORTED;
3754384acf6SStephan Aßmus }
3764384acf6SStephan Aßmus
3774384acf6SStephan Aßmus
3786ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter
3796ac391b3SStephan Aßmus
3806ac391b3SStephan Aßmus
AVFormatWriter()3816ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter()
3826ac391b3SStephan Aßmus :
383ee9d0e02SBarrett17 fFormatContext(avformat_alloc_context()),
3846266cf35SDario Casalinuovo fCodecOpened(false),
385cbada661SDario Casalinuovo fHeaderError(-1),
386b95fa248SJérôme Duval fIOContext(NULL),
3876ac391b3SStephan Aßmus fStreamLock("stream lock")
3886ac391b3SStephan Aßmus {
3896ac391b3SStephan Aßmus TRACE("AVFormatWriter::AVFormatWriter\n");
3906ac391b3SStephan Aßmus }
3916ac391b3SStephan Aßmus
3926ac391b3SStephan Aßmus
~AVFormatWriter()3936ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter()
3946ac391b3SStephan Aßmus {
3956ac391b3SStephan Aßmus TRACE("AVFormatWriter::~AVFormatWriter\n");
3964384acf6SStephan Aßmus
3970c72a8aeSStephan Aßmus // Free the streams and close the AVCodecContexts
398ee9d0e02SBarrett17 for (unsigned i = 0; i < fFormatContext->nb_streams; i++) {
399ee9d0e02SBarrett17 av_freep(&fFormatContext->streams[i]->codecpar);
400ee9d0e02SBarrett17 av_freep(&fFormatContext->streams[i]);
4010c72a8aeSStephan Aßmus }
4020c72a8aeSStephan Aßmus
403a4e89ff8SJackBurton79 avformat_free_context(fFormatContext);
404b95fa248SJérôme Duval av_free(fIOContext->buffer);
405b95fa248SJérôme Duval av_free(fIOContext);
4066ac391b3SStephan Aßmus }
4076ac391b3SStephan Aßmus
4086ac391b3SStephan Aßmus
4096ac391b3SStephan Aßmus // #pragma mark -
4106ac391b3SStephan Aßmus
4116ac391b3SStephan Aßmus
4126ac391b3SStephan Aßmus status_t
Init(const media_file_format * fileFormat)4134384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat)
4144384acf6SStephan Aßmus {
4154384acf6SStephan Aßmus TRACE("AVFormatWriter::Init()\n");
4164384acf6SStephan Aßmus
4179a6a570aSAdrien Destugues if (fIOContext == NULL) {
41896590b5bSStephan Aßmus uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize));
41996590b5bSStephan Aßmus if (buffer == NULL)
4204384acf6SStephan Aßmus return B_NO_MEMORY;
4214384acf6SStephan Aßmus
4227e75e564SStefano Ceccherini // Allocate I/O context and initialize it with buffer
4237e75e564SStefano Ceccherini // and hook functions, pass ourself as cookie.
42484e70401SAdrien Destugues fIOContext = avio_alloc_context(buffer, kIOBufferSize, 1, this,
4257e75e564SStefano Ceccherini 0, _Write, _Seek);
4267e75e564SStefano Ceccherini if (fIOContext == NULL) {
427a8ea8cd1SAdrien Destugues av_free(buffer);
4287e75e564SStefano Ceccherini TRACE("av_alloc_put_byte() failed!\n");
4294384acf6SStephan Aßmus return B_ERROR;
4304384acf6SStephan Aßmus }
4314384acf6SStephan Aßmus
43254897d5cSStephan Aßmus // Setup I/O hooks. This seems to be enough.
433ee9d0e02SBarrett17 fFormatContext->pb = fIOContext;
4349a6a570aSAdrien Destugues }
4354384acf6SStephan Aßmus
43654897d5cSStephan Aßmus // Set the AVOutputFormat according to fileFormat...
437ee9d0e02SBarrett17 fFormatContext->oformat = av_guess_format(fileFormat->short_name,
4384384acf6SStephan Aßmus fileFormat->file_extension, fileFormat->mime_type);
439ee9d0e02SBarrett17 if (fFormatContext->oformat == NULL) {
4404384acf6SStephan Aßmus TRACE(" failed to find AVOuputFormat for %s\n",
4414384acf6SStephan Aßmus fileFormat->short_name);
4424384acf6SStephan Aßmus return B_NOT_SUPPORTED;
4434384acf6SStephan Aßmus }
4444384acf6SStephan Aßmus
4454384acf6SStephan Aßmus TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name,
446ee9d0e02SBarrett17 fFormatContext->oformat->name);
4474384acf6SStephan Aßmus
4484384acf6SStephan Aßmus return B_OK;
4494384acf6SStephan Aßmus }
4504384acf6SStephan Aßmus
4514384acf6SStephan Aßmus
4524384acf6SStephan Aßmus status_t
SetCopyright(const char * copyright)453218a8c03SAugustin Cavalier AVFormatWriter::SetCopyright(const char* copyright)
4546ac391b3SStephan Aßmus {
4556ac391b3SStephan Aßmus TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright);
4566ac391b3SStephan Aßmus
4576ac391b3SStephan Aßmus return B_NOT_SUPPORTED;
4586ac391b3SStephan Aßmus }
4596ac391b3SStephan Aßmus
4606ac391b3SStephan Aßmus
4616ac391b3SStephan Aßmus status_t
CommitHeader()4626ac391b3SStephan Aßmus AVFormatWriter::CommitHeader()
4636ac391b3SStephan Aßmus {
4646ac391b3SStephan Aßmus TRACE("AVFormatWriter::CommitHeader\n");
4656ac391b3SStephan Aßmus
466ee9d0e02SBarrett17 if (fFormatContext == NULL)
4674384acf6SStephan Aßmus return B_NO_INIT;
4684384acf6SStephan Aßmus
4696266cf35SDario Casalinuovo if (fCodecOpened)
4704384acf6SStephan Aßmus return B_NOT_ALLOWED;
4714384acf6SStephan Aßmus
4726266cf35SDario Casalinuovo // We need to close the codecs we opened, even in case of failure.
4736266cf35SDario Casalinuovo fCodecOpened = true;
4746266cf35SDario Casalinuovo
475ee9d0e02SBarrett17 fHeaderError = avformat_write_header(fFormatContext, NULL);
47696590b5bSStephan Aßmus
47796590b5bSStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER
47883e06e62SPulkoMandy if (fHeaderError < 0) {
47983e06e62SPulkoMandy char errorBuffer[AV_ERROR_MAX_STRING_SIZE];
48083e06e62SPulkoMandy av_strerror(fHeaderError, errorBuffer, sizeof(errorBuffer));
48183e06e62SPulkoMandy TRACE(" avformat_write_header(): %s\n", errorBuffer);
48283e06e62SPulkoMandy } else {
4833ca4a7b1SStephan Aßmus TRACE(" wrote header\n");
48483e06e62SPulkoMandy }
48583e06e62SPulkoMandy
486ee9d0e02SBarrett17 for (unsigned i = 0; i < fFormatContext->nb_streams; i++) {
487ee9d0e02SBarrett17 AVStream* stream = fFormatContext->streams[i];
48883e06e62SPulkoMandy TRACE(" stream[%u] time_base: (%d/%d)\n",
48983e06e62SPulkoMandy i, stream->time_base.num, stream->time_base.den);
4903ca4a7b1SStephan Aßmus }
491eb01f516SStephan Aßmus #endif // TRACE_AVFORMAT_WRITER
4923ca4a7b1SStephan Aßmus
493cbada661SDario Casalinuovo return fHeaderError == 0 ? B_OK : B_ERROR;
4946ac391b3SStephan Aßmus }
4956ac391b3SStephan Aßmus
4966ac391b3SStephan Aßmus
4976ac391b3SStephan Aßmus status_t
Flush()4986ac391b3SStephan Aßmus AVFormatWriter::Flush()
4996ac391b3SStephan Aßmus {
5006ac391b3SStephan Aßmus TRACE("AVFormatWriter::Flush\n");
5016ac391b3SStephan Aßmus
5026ac391b3SStephan Aßmus return B_NOT_SUPPORTED;
5036ac391b3SStephan Aßmus }
5046ac391b3SStephan Aßmus
5056ac391b3SStephan Aßmus
5066ac391b3SStephan Aßmus status_t
Close()5076ac391b3SStephan Aßmus AVFormatWriter::Close()
5086ac391b3SStephan Aßmus {
5096ac391b3SStephan Aßmus TRACE("AVFormatWriter::Close\n");
5106ac391b3SStephan Aßmus
511ee9d0e02SBarrett17 if (fFormatContext == NULL)
5124384acf6SStephan Aßmus return B_NO_INIT;
5134384acf6SStephan Aßmus
5146266cf35SDario Casalinuovo if (!fCodecOpened)
5154384acf6SStephan Aßmus return B_NOT_ALLOWED;
5164384acf6SStephan Aßmus
517cbada661SDario Casalinuovo // From ffmpeg documentation: [av_write_trailer] may only be called
518cbada661SDario Casalinuovo // after a successful call to avformat_write_header.
5196266cf35SDario Casalinuovo if (fHeaderError != 0)
5206266cf35SDario Casalinuovo return B_ERROR;
5216266cf35SDario Casalinuovo
522ee9d0e02SBarrett17 int result = av_write_trailer(fFormatContext);
5234384acf6SStephan Aßmus if (result < 0)
5244384acf6SStephan Aßmus TRACE(" av_write_trailer(): %d\n", result);
5254384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR;
5266ac391b3SStephan Aßmus }
5276ac391b3SStephan Aßmus
5286ac391b3SStephan Aßmus
5296ac391b3SStephan Aßmus status_t
AllocateCookie(void ** _cookie,media_format * format,const media_codec_info * codecInfo)53069372b55SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, media_format* format,
53154897d5cSStephan Aßmus const media_codec_info* codecInfo)
5326ac391b3SStephan Aßmus {
5336ac391b3SStephan Aßmus TRACE("AVFormatWriter::AllocateCookie()\n");
5346ac391b3SStephan Aßmus
5356266cf35SDario Casalinuovo if (fCodecOpened)
5360c72a8aeSStephan Aßmus return B_NOT_ALLOWED;
5370c72a8aeSStephan Aßmus
5386ac391b3SStephan Aßmus BAutolock _(fStreamLock);
5396ac391b3SStephan Aßmus
5406ac391b3SStephan Aßmus if (_cookie == NULL)
5416ac391b3SStephan Aßmus return B_BAD_VALUE;
5426ac391b3SStephan Aßmus
543ee9d0e02SBarrett17 StreamCookie* cookie = new(std::nothrow) StreamCookie(fFormatContext,
5444384acf6SStephan Aßmus &fStreamLock);
5454384acf6SStephan Aßmus
54654897d5cSStephan Aßmus status_t ret = cookie->Init(format, codecInfo);
54754897d5cSStephan Aßmus if (ret != B_OK) {
54854897d5cSStephan Aßmus delete cookie;
54954897d5cSStephan Aßmus return ret;
55054897d5cSStephan Aßmus }
55154897d5cSStephan Aßmus
55254897d5cSStephan Aßmus *_cookie = cookie;
55354897d5cSStephan Aßmus return B_OK;
5546ac391b3SStephan Aßmus }
5556ac391b3SStephan Aßmus
5566ac391b3SStephan Aßmus
5576ac391b3SStephan Aßmus status_t
FreeCookie(void * _cookie)5586ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie)
5596ac391b3SStephan Aßmus {
5606ac391b3SStephan Aßmus BAutolock _(fStreamLock);
5616ac391b3SStephan Aßmus
5626ac391b3SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5636ac391b3SStephan Aßmus delete cookie;
5646ac391b3SStephan Aßmus
5656ac391b3SStephan Aßmus return B_OK;
5666ac391b3SStephan Aßmus }
5676ac391b3SStephan Aßmus
5686ac391b3SStephan Aßmus
5696ac391b3SStephan Aßmus // #pragma mark -
5706ac391b3SStephan Aßmus
5716ac391b3SStephan Aßmus
5726ac391b3SStephan Aßmus status_t
SetCopyright(void * cookie,const char * copyright)573218a8c03SAugustin Cavalier AVFormatWriter::SetCopyright(void* cookie, const char* copyright)
574fa770e4cSStephan Aßmus {
575218a8c03SAugustin Cavalier TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright);
576fa770e4cSStephan Aßmus
577fa770e4cSStephan Aßmus return B_NOT_SUPPORTED;
578fa770e4cSStephan Aßmus }
579fa770e4cSStephan Aßmus
580fa770e4cSStephan Aßmus
581fa770e4cSStephan Aßmus status_t
AddTrackInfo(void * _cookie,uint32 code,const void * data,size_t size,uint32 flags)5824384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code,
5836ac391b3SStephan Aßmus const void* data, size_t size, uint32 flags)
5846ac391b3SStephan Aßmus {
58583e06e62SPulkoMandy TRACE("AVFormatWriter::AddTrackInfo(%" B_PRIu32 ", %p, %ld, %" B_PRIu32 ")\n",
5866ac391b3SStephan Aßmus code, data, size, flags);
5876ac391b3SStephan Aßmus
588ec2c5619SDario Casalinuovo if (fHeaderError != 0)
589ec2c5619SDario Casalinuovo return B_ERROR;
590ec2c5619SDario Casalinuovo
5914384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5924384acf6SStephan Aßmus return cookie->AddTrackInfo(code, data, size, flags);
5936ac391b3SStephan Aßmus }
5946ac391b3SStephan Aßmus
5956ac391b3SStephan Aßmus
5966ac391b3SStephan Aßmus status_t
WriteChunk(void * _cookie,const void * chunkBuffer,size_t chunkSize,media_encode_info * encodeInfo)5974384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
598fa770e4cSStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo)
5996ac391b3SStephan Aßmus {
6003ca4a7b1SStephan Aßmus TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer,
6013ca4a7b1SStephan Aßmus chunkSize, encodeInfo);
6026ac391b3SStephan Aßmus
603ec2c5619SDario Casalinuovo if (fHeaderError != 0)
604ec2c5619SDario Casalinuovo return B_ERROR;
605ec2c5619SDario Casalinuovo
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
_Write(void * cookie,uint8 * buffer,int bufferSize)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
622218a8c03SAugustin Cavalier 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
_Seek(void * cookie,off_t offset,int whence)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
638218a8c03SAugustin Cavalier BMediaIO* mediaIO = dynamic_cast<BMediaIO*>(writer->fTarget);
639a6b34a8cSDario Casalinuovo if (mediaIO == 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;
645a6b34a8cSDario Casalinuovo if (mediaIO->GetSize(&size) == B_OK)
6462e9d65abSStephan Aßmus return size;
647a6b34a8cSDario Casalinuovo
6482e9d65abSStephan Aßmus return -1;
6492e9d65abSStephan Aßmus }
6502e9d65abSStephan Aßmus
651a6b34a8cSDario Casalinuovo off_t position = mediaIO->Seek(offset, whence);
6522e9d65abSStephan Aßmus TRACE_IO(" position: %lld\n", position);
6532e9d65abSStephan Aßmus if (position < 0)
6542e9d65abSStephan Aßmus return -1;
6552e9d65abSStephan Aßmus
6562e9d65abSStephan Aßmus return position;
6572e9d65abSStephan Aßmus }
6582e9d65abSStephan Aßmus
6592e9d65abSStephan Aßmus
660