xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp (revision 8d721650cecad11fd6468804c9bd8ecb043aad09)
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;
7554897d5cSStephan Aßmus 			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 
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 {
900c72a8aeSStephan Aßmus 	av_init_packet(&fPacket);
916ac391b3SStephan Aßmus }
926ac391b3SStephan Aßmus 
936ac391b3SStephan Aßmus 
946ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie()
956ac391b3SStephan Aßmus {
966ac391b3SStephan Aßmus }
976ac391b3SStephan Aßmus 
986ac391b3SStephan Aßmus 
994384acf6SStephan Aßmus status_t
10069372b55SStephan Aßmus AVFormatWriter::StreamCookie::Init(media_format* format,
10154897d5cSStephan Aßmus 	const media_codec_info* codecInfo)
1024384acf6SStephan Aßmus {
1036e567425SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::Init()\n");
1044384acf6SStephan Aßmus 
1054384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
1064384acf6SStephan Aßmus 
107ee9d0e02SBarrett17 	fPacket.stream_index = fFormatContext->nb_streams;
108ee9d0e02SBarrett17 	fStream = avformat_new_stream(fFormatContext, NULL);
1094384acf6SStephan Aßmus 
1104384acf6SStephan Aßmus 	if (fStream == NULL) {
1114384acf6SStephan Aßmus 		TRACE("  failed to add new stream\n");
1124384acf6SStephan Aßmus 		return B_ERROR;
1134384acf6SStephan Aßmus 	}
1144384acf6SStephan Aßmus 
115*8d721650SJackBurton79 	fStream->id = fPacket.stream_index;
116*8d721650SJackBurton79 
117428d87c5SBarrett17 //	TRACE("  fStream->codecpar: %p\n", fStream->codecpar);
1180c72a8aeSStephan Aßmus 	// TODO: This is a hack for now! Use avcodec_find_encoder_by_name()
1190c72a8aeSStephan Aßmus 	// or something similar...
120428d87c5SBarrett17 	fStream->codecpar->codec_id = (CodecID)codecInfo->sub_id;
121428d87c5SBarrett17 	if (fStream->codecpar->codec_id == AV_CODEC_ID_NONE)
122428d87c5SBarrett17 		fStream->codecpar->codec_id = raw_audio_codec_id_for(*format);
1230c72a8aeSStephan Aßmus 
12454897d5cSStephan Aßmus 	// Setup the stream according to the media format...
12554897d5cSStephan Aßmus 	if (format->type == B_MEDIA_RAW_VIDEO) {
126428d87c5SBarrett17 		fStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
1275ba361f8SBarrett17 		fStream->time_base.den = (int)format->u.raw_video.field_rate;
1285ba361f8SBarrett17 		fStream->time_base.num = 1;
1295ba361f8SBarrett17 
13054897d5cSStephan Aßmus 		// video size
131428d87c5SBarrett17 		fStream->codecpar->width = format->u.raw_video.display.line_width;
132428d87c5SBarrett17 		fStream->codecpar->height = format->u.raw_video.display.line_count;
13354897d5cSStephan Aßmus 		// pixel aspect ratio
13454897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.num
13554897d5cSStephan Aßmus 			= format->u.raw_video.pixel_width_aspect;
13654897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.den
13754897d5cSStephan Aßmus 			= format->u.raw_video.pixel_height_aspect;
13854897d5cSStephan Aßmus 		if (fStream->sample_aspect_ratio.num == 0
13954897d5cSStephan Aßmus 			|| fStream->sample_aspect_ratio.den == 0) {
14054897d5cSStephan Aßmus 			av_reduce(&fStream->sample_aspect_ratio.num,
141428d87c5SBarrett17 				&fStream->sample_aspect_ratio.den, fStream->codecpar->width,
142428d87c5SBarrett17 				fStream->codecpar->height, 255);
14354897d5cSStephan Aßmus 		}
14454897d5cSStephan Aßmus 
145428d87c5SBarrett17 		fStream->codecpar->sample_aspect_ratio = fStream->sample_aspect_ratio;
14669372b55SStephan Aßmus 
14769372b55SStephan Aßmus 		// Use the last supported pixel format of the AVCodec, which we hope
14869372b55SStephan Aßmus 		// is the one with the best quality (true for all currently supported
14969372b55SStephan Aßmus 		// encoders).
150428d87c5SBarrett17 //		AVCodec* codec = fStream->codecpar->codec;
151bb188e4dSStephan Aßmus //		for (int i = 0; codec->pix_fmts[i] != PIX_FMT_NONE; i++)
152428d87c5SBarrett17 //			fStream->codecpar->pix_fmt = codec->pix_fmts[i];
153428d87c5SBarrett17 		fStream->codecpar->format = AV_PIX_FMT_YUV420P;
15469372b55SStephan Aßmus 
15554897d5cSStephan Aßmus 	} else if (format->type == B_MEDIA_RAW_AUDIO) {
156428d87c5SBarrett17 		fStream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
157428d87c5SBarrett17 
1580c72a8aeSStephan Aßmus 		// frame rate
159428d87c5SBarrett17 		fStream->codecpar->sample_rate = (int)format->u.raw_audio.frame_rate;
1600c72a8aeSStephan Aßmus 
1613ca4a7b1SStephan Aßmus 		// channels
162428d87c5SBarrett17 		fStream->codecpar->channels = format->u.raw_audio.channel_count;
1635e0e1689SAdrien Destugues 
1645e0e1689SAdrien Destugues 		// set fStream to the audio format we want to use. This is only a hint
1655e0e1689SAdrien Destugues 		// (each encoder has a different set of accepted formats)
1663ca4a7b1SStephan Aßmus 		switch (format->u.raw_audio.format) {
1673ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_FLOAT:
168428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_FLT;
1693ca4a7b1SStephan Aßmus 				break;
1703ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_DOUBLE:
171428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_DBL;
1723ca4a7b1SStephan Aßmus 				break;
1733ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_INT:
174428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_S32;
1753ca4a7b1SStephan Aßmus 				break;
1763ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_SHORT:
177428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_S16;
1783ca4a7b1SStephan Aßmus 				break;
1793ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_UCHAR:
180428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_U8;
1813ca4a7b1SStephan Aßmus 				break;
1823ca4a7b1SStephan Aßmus 
1833ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_CHAR:
1843ca4a7b1SStephan Aßmus 			default:
1853ca4a7b1SStephan Aßmus 				return B_MEDIA_BAD_FORMAT;
1863ca4a7b1SStephan Aßmus 				break;
18754897d5cSStephan Aßmus 		}
1885e0e1689SAdrien Destugues 
1895e0e1689SAdrien Destugues 		// Now negociate the actual format with the encoder
1905e0e1689SAdrien Destugues 		// First check if the requested format is acceptable
191428d87c5SBarrett17 		AVCodec* codec = avcodec_find_encoder(fStream->codecpar->codec_id);
1929e5c6946SAdrien Destugues 
1939e5c6946SAdrien Destugues 		if (codec == NULL)
1949e5c6946SAdrien Destugues 			return B_MEDIA_BAD_FORMAT;
1959e5c6946SAdrien Destugues 
1965e0e1689SAdrien Destugues 		const enum AVSampleFormat *p = codec->sample_fmts;
1975e0e1689SAdrien Destugues 		for (; *p != -1; p++) {
198428d87c5SBarrett17 			if (*p == fStream->codecpar->format)
1995e0e1689SAdrien Destugues 				break;
2005e0e1689SAdrien Destugues 		}
2015e0e1689SAdrien Destugues 		// If not, force one of the acceptable ones
2025e0e1689SAdrien Destugues 		if (*p == -1) {
203428d87c5SBarrett17 			fStream->codecpar->format = codec->sample_fmts[0];
2045e0e1689SAdrien Destugues 
2055e0e1689SAdrien Destugues 			// And finally set the format struct to the accepted format. It is
2065e0e1689SAdrien Destugues 			// then up to the caller to make sure we get data matching that
2075e0e1689SAdrien Destugues 			// format.
208428d87c5SBarrett17 			switch (fStream->codecpar->format) {
2095e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_FLT:
2105e0e1689SAdrien Destugues 					format->u.raw_audio.format
2115e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_FLOAT;
2125e0e1689SAdrien Destugues 					break;
2135e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_DBL:
2145e0e1689SAdrien Destugues 					format->u.raw_audio.format
2155e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_DOUBLE;
2165e0e1689SAdrien Destugues 					break;
2175e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_S32:
2185e0e1689SAdrien Destugues 					format->u.raw_audio.format
2195e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_INT;
2205e0e1689SAdrien Destugues 					break;
2215e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_S16:
2225e0e1689SAdrien Destugues 					format->u.raw_audio.format
2235e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_SHORT;
2245e0e1689SAdrien Destugues 					break;
2255e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_U8:
2265e0e1689SAdrien Destugues 					format->u.raw_audio.format
2275e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_UCHAR;
2285e0e1689SAdrien Destugues 					break;
2295e0e1689SAdrien Destugues 				default:
2305e0e1689SAdrien Destugues 					return B_MEDIA_BAD_FORMAT;
2315e0e1689SAdrien Destugues 					break;
2325e0e1689SAdrien Destugues 			}
2335e0e1689SAdrien Destugues 		}
2345e0e1689SAdrien Destugues 
2353ca4a7b1SStephan Aßmus 		if (format->u.raw_audio.channel_mask == 0) {
2363ca4a7b1SStephan Aßmus 			// guess the channel mask...
2373ca4a7b1SStephan Aßmus 			switch (format->u.raw_audio.channel_count) {
2383ca4a7b1SStephan Aßmus 				default:
2393ca4a7b1SStephan Aßmus 				case 2:
240428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
2413ca4a7b1SStephan Aßmus 					break;
2423ca4a7b1SStephan Aßmus 				case 1:
243428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
2443ca4a7b1SStephan Aßmus 					break;
2453ca4a7b1SStephan Aßmus 				case 3:
246428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_SURROUND;
2473ca4a7b1SStephan Aßmus 					break;
2483ca4a7b1SStephan Aßmus 				case 4:
249428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_QUAD;
2503ca4a7b1SStephan Aßmus 					break;
2513ca4a7b1SStephan Aßmus 				case 5:
252428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT0;
2533ca4a7b1SStephan Aßmus 					break;
2543ca4a7b1SStephan Aßmus 				case 6:
255428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT1;
2563ca4a7b1SStephan Aßmus 					break;
2573ca4a7b1SStephan Aßmus 				case 8:
258428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1;
2593ca4a7b1SStephan Aßmus 					break;
2603ca4a7b1SStephan Aßmus 				case 10:
261428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE;
2623ca4a7b1SStephan Aßmus 					break;
2633ca4a7b1SStephan Aßmus 			}
2643ca4a7b1SStephan Aßmus 		} else {
2653ca4a7b1SStephan Aßmus 			// The bits match 1:1 for media_multi_channels and FFmpeg defines.
266428d87c5SBarrett17 			fStream->codecpar->channel_layout = format->u.raw_audio.channel_mask;
2673ca4a7b1SStephan Aßmus 		}
2683ca4a7b1SStephan Aßmus 	}
2693ca4a7b1SStephan Aßmus 
2703ca4a7b1SStephan Aßmus 	TRACE("  stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n",
2713ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
27242cc4631SBarrett17 		fStream->codec->time_base.num, fStream->codec->time_base.den);
27354897d5cSStephan Aßmus 
274657983b8SStephan Aßmus #if 0
275657983b8SStephan Aßmus 	// Write the AVCodecContext pointer to the user data section of the
276657983b8SStephan Aßmus 	// media_format. For some encoders, it seems to be necessary to use
277657983b8SStephan Aßmus 	// the AVCodecContext of the AVStream in order to successfully encode
278657983b8SStephan Aßmus 	// anything and write valid media files. For example some codecs need
279657983b8SStephan Aßmus 	// to store meta data or global data in the container.
280657983b8SStephan Aßmus 	app_info appInfo;
281657983b8SStephan Aßmus 	if (be_app->GetAppInfo(&appInfo) == B_OK) {
282657983b8SStephan Aßmus 		uchar* userData = format->user_data;
283657983b8SStephan Aßmus 		*(uint32*)userData = 'ffmp';
284657983b8SStephan Aßmus 		userData += sizeof(uint32);
285657983b8SStephan Aßmus 		*(team_id*)userData = appInfo.team;
286657983b8SStephan Aßmus 		userData += sizeof(team_id);
287657983b8SStephan Aßmus 		*(AVCodecContext**)userData = fStream->codec;
288657983b8SStephan Aßmus 	}
289657983b8SStephan Aßmus #endif
290657983b8SStephan Aßmus 
2914384acf6SStephan Aßmus 	return B_OK;
2924384acf6SStephan Aßmus }
2934384acf6SStephan Aßmus 
2944384acf6SStephan Aßmus 
2954384acf6SStephan Aßmus status_t
2964384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
2974384acf6SStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
2984384acf6SStephan Aßmus {
29996590b5bSStephan Aßmus 	TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, "
30096590b5bSStephan Aßmus 		"start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize,
30196590b5bSStephan Aßmus 		encodeInfo->start_time);
3024384acf6SStephan Aßmus 
3034384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
3044384acf6SStephan Aßmus 
30554897d5cSStephan Aßmus 	fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
30654897d5cSStephan Aßmus 	fPacket.size = chunkSize;
3075e2290b0SBarrett17 	fPacket.stream_index = fStream->index;
30854897d5cSStephan Aßmus 
30996590b5bSStephan Aßmus 	fPacket.pts = int64_t((double)encodeInfo->start_time
31096590b5bSStephan Aßmus 		* fStream->time_base.den / (1000000.0 * fStream->time_base.num)
31196590b5bSStephan Aßmus 		+ 0.5);
31269372b55SStephan Aßmus 
313e93fce62SBarrett17 	fPacket.dts = fPacket.pts;
314e93fce62SBarrett17 
31569372b55SStephan Aßmus 	fPacket.flags = 0;
31669372b55SStephan Aßmus 	if ((encodeInfo->flags & B_MEDIA_KEY_FRAME) != 0)
31769372b55SStephan Aßmus 		fPacket.flags |= AV_PKT_FLAG_KEY;
31869372b55SStephan Aßmus 
3193ca4a7b1SStephan Aßmus 	TRACE_PACKET("  PTS: %lld (stream->time_base: (%d/%d), "
3203ca4a7b1SStephan Aßmus 		"codec->time_base: (%d/%d))\n", fPacket.pts,
3213ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
322bbe27469SBarrett17 		fStream->codec->time_base.num, fStream->codec->time_base.den);
3233ca4a7b1SStephan Aßmus 
32454897d5cSStephan Aßmus #if 0
32554897d5cSStephan Aßmus 	// TODO: Eventually, we need to write interleaved packets, but
32654897d5cSStephan Aßmus 	// maybe we are only supposed to use this if we have actually
32754897d5cSStephan Aßmus 	// more than one stream. For the moment, this crashes in AVPacket
32854897d5cSStephan Aßmus 	// shuffling inside libavformat. Maybe if we want to use this, we
32954897d5cSStephan Aßmus 	// need to allocate a separate AVPacket and copy the chunk buffer.
330ee9d0e02SBarrett17 	int result = av_interleaved_write_frame(fFormatContext, &fPacket);
33154897d5cSStephan Aßmus 	if (result < 0)
33254897d5cSStephan Aßmus 		TRACE("  av_interleaved_write_frame(): %d\n", result);
33354897d5cSStephan Aßmus #else
334ee9d0e02SBarrett17 	int result = av_write_frame(fFormatContext, &fPacket);
33554897d5cSStephan Aßmus 	if (result < 0)
33654897d5cSStephan Aßmus 		TRACE("  av_write_frame(): %d\n", result);
33754897d5cSStephan Aßmus #endif
33854897d5cSStephan Aßmus 
33954897d5cSStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
3404384acf6SStephan Aßmus }
3414384acf6SStephan Aßmus 
3424384acf6SStephan Aßmus 
3434384acf6SStephan Aßmus status_t
3444384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code,
3454384acf6SStephan Aßmus 	const void* data, size_t size, uint32 flags)
3464384acf6SStephan Aßmus {
3474384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n",
3484384acf6SStephan Aßmus 		code, data, size, flags);
3494384acf6SStephan Aßmus 
3504384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
3514384acf6SStephan Aßmus 
3524384acf6SStephan Aßmus 	return B_NOT_SUPPORTED;
3534384acf6SStephan Aßmus }
3544384acf6SStephan Aßmus 
3554384acf6SStephan Aßmus 
3566ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter
3576ac391b3SStephan Aßmus 
3586ac391b3SStephan Aßmus 
3596ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter()
3606ac391b3SStephan Aßmus 	:
361ee9d0e02SBarrett17 	fFormatContext(avformat_alloc_context()),
3626266cf35SDario Casalinuovo 	fCodecOpened(false),
363cbada661SDario Casalinuovo 	fHeaderError(-1),
364b95fa248SJérôme Duval 	fIOContext(NULL),
3656ac391b3SStephan Aßmus 	fStreamLock("stream lock")
3666ac391b3SStephan Aßmus {
3676ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AVFormatWriter\n");
3686ac391b3SStephan Aßmus }
3696ac391b3SStephan Aßmus 
3706ac391b3SStephan Aßmus 
3716ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter()
3726ac391b3SStephan Aßmus {
3736ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::~AVFormatWriter\n");
3744384acf6SStephan Aßmus 
3750c72a8aeSStephan Aßmus 	// Free the streams and close the AVCodecContexts
376ee9d0e02SBarrett17 	for (unsigned i = 0; i < fFormatContext->nb_streams; i++) {
377ee9d0e02SBarrett17 		av_freep(&fFormatContext->streams[i]->codecpar);
378ee9d0e02SBarrett17 		av_freep(&fFormatContext->streams[i]);
3790c72a8aeSStephan Aßmus 	}
3800c72a8aeSStephan Aßmus 
381a4e89ff8SJackBurton79 	avformat_free_context(fFormatContext);
382b95fa248SJérôme Duval 	av_free(fIOContext->buffer);
383b95fa248SJérôme Duval 	av_free(fIOContext);
3846ac391b3SStephan Aßmus }
3856ac391b3SStephan Aßmus 
3866ac391b3SStephan Aßmus 
3876ac391b3SStephan Aßmus // #pragma mark -
3886ac391b3SStephan Aßmus 
3896ac391b3SStephan Aßmus 
3906ac391b3SStephan Aßmus status_t
3914384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat)
3924384acf6SStephan Aßmus {
3934384acf6SStephan Aßmus 	TRACE("AVFormatWriter::Init()\n");
3944384acf6SStephan Aßmus 
39596590b5bSStephan Aßmus 	uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize));
39696590b5bSStephan Aßmus 	if (buffer == NULL)
3974384acf6SStephan Aßmus 		return B_NO_MEMORY;
3984384acf6SStephan Aßmus 
3997e75e564SStefano Ceccherini 	// Allocate I/O context and initialize it with buffer
4007e75e564SStefano Ceccherini 	// and hook functions, pass ourself as cookie.
40184e70401SAdrien Destugues 	fIOContext = avio_alloc_context(buffer, kIOBufferSize, 1, this,
4027e75e564SStefano Ceccherini 			0, _Write, _Seek);
4037e75e564SStefano Ceccherini 	if (fIOContext == NULL) {
4047e75e564SStefano Ceccherini 		TRACE("av_alloc_put_byte() failed!\n");
4054384acf6SStephan Aßmus 		return B_ERROR;
4064384acf6SStephan Aßmus 	}
4074384acf6SStephan Aßmus 
40854897d5cSStephan Aßmus 	// Setup I/O hooks. This seems to be enough.
409ee9d0e02SBarrett17 	fFormatContext->pb = fIOContext;
4104384acf6SStephan Aßmus 
41154897d5cSStephan Aßmus 	// Set the AVOutputFormat according to fileFormat...
412ee9d0e02SBarrett17 	fFormatContext->oformat = av_guess_format(fileFormat->short_name,
4134384acf6SStephan Aßmus 		fileFormat->file_extension, fileFormat->mime_type);
414ee9d0e02SBarrett17 	if (fFormatContext->oformat == NULL) {
4154384acf6SStephan Aßmus 		TRACE("  failed to find AVOuputFormat for %s\n",
4164384acf6SStephan Aßmus 			fileFormat->short_name);
4174384acf6SStephan Aßmus 		return B_NOT_SUPPORTED;
4184384acf6SStephan Aßmus 	}
4194384acf6SStephan Aßmus 
4204384acf6SStephan Aßmus 	TRACE("  found AVOuputFormat for %s: %s\n", fileFormat->short_name,
421ee9d0e02SBarrett17 		fFormatContext->oformat->name);
4224384acf6SStephan Aßmus 
4234384acf6SStephan Aßmus 	return B_OK;
4244384acf6SStephan Aßmus }
4254384acf6SStephan Aßmus 
4264384acf6SStephan Aßmus 
4274384acf6SStephan Aßmus status_t
4286ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright)
4296ac391b3SStephan Aßmus {
4306ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright);
4316ac391b3SStephan Aßmus 
4326ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
4336ac391b3SStephan Aßmus }
4346ac391b3SStephan Aßmus 
4356ac391b3SStephan Aßmus 
4366ac391b3SStephan Aßmus status_t
4376ac391b3SStephan Aßmus AVFormatWriter::CommitHeader()
4386ac391b3SStephan Aßmus {
4396ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::CommitHeader\n");
4406ac391b3SStephan Aßmus 
441ee9d0e02SBarrett17 	if (fFormatContext == NULL)
4424384acf6SStephan Aßmus 		return B_NO_INIT;
4434384acf6SStephan Aßmus 
4446266cf35SDario Casalinuovo 	if (fCodecOpened)
4454384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
4464384acf6SStephan Aßmus 
4476266cf35SDario Casalinuovo 	// We need to close the codecs we opened, even in case of failure.
4486266cf35SDario Casalinuovo 	fCodecOpened = true;
4496266cf35SDario Casalinuovo 
450ee9d0e02SBarrett17 	fHeaderError = avformat_write_header(fFormatContext, NULL);
451cbada661SDario Casalinuovo 	if (fHeaderError < 0)
452cbada661SDario Casalinuovo 		TRACE("  avformat_write_header(): %d\n", fHeaderError);
45396590b5bSStephan Aßmus 
45496590b5bSStephan Aßmus 	#ifdef TRACE_AVFORMAT_WRITER
4553ca4a7b1SStephan Aßmus 	TRACE("  wrote header\n");
456ee9d0e02SBarrett17 	for (unsigned i = 0; i < fFormatContext->nb_streams; i++) {
457ee9d0e02SBarrett17 		AVStream* stream = fFormatContext->streams[i];
4583ca4a7b1SStephan Aßmus 		TRACE("  stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n",
4593ca4a7b1SStephan Aßmus 			i, stream->time_base.num, stream->time_base.den,
4603ca4a7b1SStephan Aßmus 			stream->codec->time_base.num, stream->codec->time_base.den);
4613ca4a7b1SStephan Aßmus 	}
462eb01f516SStephan Aßmus 	#endif // TRACE_AVFORMAT_WRITER
4633ca4a7b1SStephan Aßmus 
464cbada661SDario Casalinuovo 	return fHeaderError == 0 ? B_OK : B_ERROR;
4656ac391b3SStephan Aßmus }
4666ac391b3SStephan Aßmus 
4676ac391b3SStephan Aßmus 
4686ac391b3SStephan Aßmus status_t
4696ac391b3SStephan Aßmus AVFormatWriter::Flush()
4706ac391b3SStephan Aßmus {
4716ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Flush\n");
4726ac391b3SStephan Aßmus 
4736ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
4746ac391b3SStephan Aßmus }
4756ac391b3SStephan Aßmus 
4766ac391b3SStephan Aßmus 
4776ac391b3SStephan Aßmus status_t
4786ac391b3SStephan Aßmus AVFormatWriter::Close()
4796ac391b3SStephan Aßmus {
4806ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Close\n");
4816ac391b3SStephan Aßmus 
482ee9d0e02SBarrett17 	if (fFormatContext == NULL)
4834384acf6SStephan Aßmus 		return B_NO_INIT;
4844384acf6SStephan Aßmus 
4856266cf35SDario Casalinuovo 	if (!fCodecOpened)
4864384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
4874384acf6SStephan Aßmus 
488cbada661SDario Casalinuovo 	// From ffmpeg documentation: [av_write_trailer] may only be called
489cbada661SDario Casalinuovo 	// after a successful call to avformat_write_header.
4906266cf35SDario Casalinuovo 	if (fHeaderError != 0)
4916266cf35SDario Casalinuovo 		return B_ERROR;
4926266cf35SDario Casalinuovo 
493ee9d0e02SBarrett17 	int result = av_write_trailer(fFormatContext);
4944384acf6SStephan Aßmus 	if (result < 0)
4954384acf6SStephan Aßmus 		TRACE("  av_write_trailer(): %d\n", result);
4964384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
4976ac391b3SStephan Aßmus }
4986ac391b3SStephan Aßmus 
4996ac391b3SStephan Aßmus 
5006ac391b3SStephan Aßmus status_t
50169372b55SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, media_format* format,
50254897d5cSStephan Aßmus 	const media_codec_info* codecInfo)
5036ac391b3SStephan Aßmus {
5046ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AllocateCookie()\n");
5056ac391b3SStephan Aßmus 
5066266cf35SDario Casalinuovo 	if (fCodecOpened)
5070c72a8aeSStephan Aßmus 		return B_NOT_ALLOWED;
5080c72a8aeSStephan Aßmus 
5096ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
5106ac391b3SStephan Aßmus 
5116ac391b3SStephan Aßmus 	if (_cookie == NULL)
5126ac391b3SStephan Aßmus 		return B_BAD_VALUE;
5136ac391b3SStephan Aßmus 
514ee9d0e02SBarrett17 	StreamCookie* cookie = new(std::nothrow) StreamCookie(fFormatContext,
5154384acf6SStephan Aßmus 		&fStreamLock);
5164384acf6SStephan Aßmus 
51754897d5cSStephan Aßmus 	status_t ret = cookie->Init(format, codecInfo);
51854897d5cSStephan Aßmus 	if (ret != B_OK) {
51954897d5cSStephan Aßmus 		delete cookie;
52054897d5cSStephan Aßmus 		return ret;
52154897d5cSStephan Aßmus 	}
52254897d5cSStephan Aßmus 
52354897d5cSStephan Aßmus 	*_cookie = cookie;
52454897d5cSStephan Aßmus 	return B_OK;
5256ac391b3SStephan Aßmus }
5266ac391b3SStephan Aßmus 
5276ac391b3SStephan Aßmus 
5286ac391b3SStephan Aßmus status_t
5296ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie)
5306ac391b3SStephan Aßmus {
5316ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
5326ac391b3SStephan Aßmus 
5336ac391b3SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5346ac391b3SStephan Aßmus 	delete cookie;
5356ac391b3SStephan Aßmus 
5366ac391b3SStephan Aßmus 	return B_OK;
5376ac391b3SStephan Aßmus }
5386ac391b3SStephan Aßmus 
5396ac391b3SStephan Aßmus 
5406ac391b3SStephan Aßmus // #pragma mark -
5416ac391b3SStephan Aßmus 
5426ac391b3SStephan Aßmus 
5436ac391b3SStephan Aßmus status_t
544fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright)
545fa770e4cSStephan Aßmus {
546fa770e4cSStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright);
547fa770e4cSStephan Aßmus 
548fa770e4cSStephan Aßmus 	return B_NOT_SUPPORTED;
549fa770e4cSStephan Aßmus }
550fa770e4cSStephan Aßmus 
551fa770e4cSStephan Aßmus 
552fa770e4cSStephan Aßmus status_t
5534384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code,
5546ac391b3SStephan Aßmus 	const void* data, size_t size, uint32 flags)
5556ac391b3SStephan Aßmus {
5566ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n",
5576ac391b3SStephan Aßmus 		code, data, size, flags);
5586ac391b3SStephan Aßmus 
559ec2c5619SDario Casalinuovo 	if (fHeaderError != 0)
560ec2c5619SDario Casalinuovo 		return B_ERROR;
561ec2c5619SDario Casalinuovo 
5624384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5634384acf6SStephan Aßmus 	return cookie->AddTrackInfo(code, data, size, flags);
5646ac391b3SStephan Aßmus }
5656ac391b3SStephan Aßmus 
5666ac391b3SStephan Aßmus 
5676ac391b3SStephan Aßmus status_t
5684384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
569fa770e4cSStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
5706ac391b3SStephan Aßmus {
5713ca4a7b1SStephan Aßmus 	TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer,
5723ca4a7b1SStephan Aßmus 		chunkSize, encodeInfo);
5736ac391b3SStephan Aßmus 
574ec2c5619SDario Casalinuovo 	if (fHeaderError != 0)
575ec2c5619SDario Casalinuovo 		return B_ERROR;
576ec2c5619SDario Casalinuovo 
5774384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5784384acf6SStephan Aßmus 	return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo);
5796ac391b3SStephan Aßmus }
5806ac391b3SStephan Aßmus 
5816ac391b3SStephan Aßmus 
58254897d5cSStephan Aßmus // #pragma mark - I/O hooks
5832e9d65abSStephan Aßmus 
5842e9d65abSStephan Aßmus 
5852e9d65abSStephan Aßmus /*static*/ int
5867a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize)
5872e9d65abSStephan Aßmus {
5882e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n",
5892e9d65abSStephan Aßmus 		cookie, buffer, bufferSize);
5902e9d65abSStephan Aßmus 
5912e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
5922e9d65abSStephan Aßmus 
5932e9d65abSStephan Aßmus 	ssize_t written = writer->fTarget->Write(buffer, bufferSize);
5942e9d65abSStephan Aßmus 
5952e9d65abSStephan Aßmus 	TRACE_IO("  written: %ld\n", written);
5962e9d65abSStephan Aßmus 	return (int)written;
5972e9d65abSStephan Aßmus 
5982e9d65abSStephan Aßmus }
5992e9d65abSStephan Aßmus 
6002e9d65abSStephan Aßmus 
6012e9d65abSStephan Aßmus /*static*/ off_t
6022e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence)
6032e9d65abSStephan Aßmus {
6042e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n",
6052e9d65abSStephan Aßmus 		cookie, offset, whence);
6062e9d65abSStephan Aßmus 
6072e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
6082e9d65abSStephan Aßmus 
609a6b34a8cSDario Casalinuovo 	BMediaIO* mediaIO = dynamic_cast<BMediaIO*>(writer->fTarget);
610a6b34a8cSDario Casalinuovo 	if (mediaIO == NULL)
6112e9d65abSStephan Aßmus 		return -1;
6122e9d65abSStephan Aßmus 
6134384acf6SStephan Aßmus 	// Support for special file size retrieval API without seeking anywhere:
6142e9d65abSStephan Aßmus 	if (whence == AVSEEK_SIZE) {
6152e9d65abSStephan Aßmus 		off_t size;
616a6b34a8cSDario Casalinuovo 		if (mediaIO->GetSize(&size) == B_OK)
6172e9d65abSStephan Aßmus 			return size;
618a6b34a8cSDario Casalinuovo 
6192e9d65abSStephan Aßmus 		return -1;
6202e9d65abSStephan Aßmus 	}
6212e9d65abSStephan Aßmus 
622a6b34a8cSDario Casalinuovo 	off_t position = mediaIO->Seek(offset, whence);
6232e9d65abSStephan Aßmus 	TRACE_IO("  position: %lld\n", position);
6242e9d65abSStephan Aßmus 	if (position < 0)
6252e9d65abSStephan Aßmus 		return -1;
6262e9d65abSStephan Aßmus 
6272e9d65abSStephan Aßmus 	return position;
6282e9d65abSStephan Aßmus }
6292e9d65abSStephan Aßmus 
6302e9d65abSStephan Aßmus 
631