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