xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp (revision 9e5c694668556b60e49a49e6708bfb550fc6ce1b)
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