xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp (revision e93fce62f2ed4935930c236fa708a99af54384c4)
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);
1099e5c6946SAdrien Destugues 	fStream->id = fPacket.stream_index;
1104384acf6SStephan Aßmus 
1114384acf6SStephan Aßmus 	if (fStream == NULL) {
1124384acf6SStephan Aßmus 		TRACE("  failed to add new stream\n");
1134384acf6SStephan Aßmus 		return B_ERROR;
1144384acf6SStephan Aßmus 	}
1154384acf6SStephan Aßmus 
116428d87c5SBarrett17 //	TRACE("  fStream->codecpar: %p\n", fStream->codecpar);
1170c72a8aeSStephan Aßmus 	// TODO: This is a hack for now! Use avcodec_find_encoder_by_name()
1180c72a8aeSStephan Aßmus 	// or something similar...
119428d87c5SBarrett17 	fStream->codecpar->codec_id = (CodecID)codecInfo->sub_id;
120428d87c5SBarrett17 	if (fStream->codecpar->codec_id == AV_CODEC_ID_NONE)
121428d87c5SBarrett17 		fStream->codecpar->codec_id = raw_audio_codec_id_for(*format);
1220c72a8aeSStephan Aßmus 
12354897d5cSStephan Aßmus 	// Setup the stream according to the media format...
12454897d5cSStephan Aßmus 	if (format->type == B_MEDIA_RAW_VIDEO) {
125428d87c5SBarrett17 		fStream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
1265ba361f8SBarrett17 		fStream->time_base.den = (int)format->u.raw_video.field_rate;
1275ba361f8SBarrett17 		fStream->time_base.num = 1;
1285ba361f8SBarrett17 
12954897d5cSStephan Aßmus 		// video size
130428d87c5SBarrett17 		fStream->codecpar->width = format->u.raw_video.display.line_width;
131428d87c5SBarrett17 		fStream->codecpar->height = format->u.raw_video.display.line_count;
13254897d5cSStephan Aßmus 		// pixel aspect ratio
13354897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.num
13454897d5cSStephan Aßmus 			= format->u.raw_video.pixel_width_aspect;
13554897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.den
13654897d5cSStephan Aßmus 			= format->u.raw_video.pixel_height_aspect;
13754897d5cSStephan Aßmus 		if (fStream->sample_aspect_ratio.num == 0
13854897d5cSStephan Aßmus 			|| fStream->sample_aspect_ratio.den == 0) {
13954897d5cSStephan Aßmus 			av_reduce(&fStream->sample_aspect_ratio.num,
140428d87c5SBarrett17 				&fStream->sample_aspect_ratio.den, fStream->codecpar->width,
141428d87c5SBarrett17 				fStream->codecpar->height, 255);
14254897d5cSStephan Aßmus 		}
14354897d5cSStephan Aßmus 
144428d87c5SBarrett17 		fStream->codecpar->sample_aspect_ratio = fStream->sample_aspect_ratio;
14569372b55SStephan Aßmus 
14669372b55SStephan Aßmus 		// Use the last supported pixel format of the AVCodec, which we hope
14769372b55SStephan Aßmus 		// is the one with the best quality (true for all currently supported
14869372b55SStephan Aßmus 		// encoders).
149428d87c5SBarrett17 //		AVCodec* codec = fStream->codecpar->codec;
150bb188e4dSStephan Aßmus //		for (int i = 0; codec->pix_fmts[i] != PIX_FMT_NONE; i++)
151428d87c5SBarrett17 //			fStream->codecpar->pix_fmt = codec->pix_fmts[i];
152428d87c5SBarrett17 		fStream->codecpar->format = AV_PIX_FMT_YUV420P;
15369372b55SStephan Aßmus 
15454897d5cSStephan Aßmus 	} else if (format->type == B_MEDIA_RAW_AUDIO) {
155428d87c5SBarrett17 		fStream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
156428d87c5SBarrett17 
1570c72a8aeSStephan Aßmus 		// frame rate
158428d87c5SBarrett17 		fStream->codecpar->sample_rate = (int)format->u.raw_audio.frame_rate;
1590c72a8aeSStephan Aßmus 
1603ca4a7b1SStephan Aßmus 		// channels
161428d87c5SBarrett17 		fStream->codecpar->channels = format->u.raw_audio.channel_count;
1625e0e1689SAdrien Destugues 
1635e0e1689SAdrien Destugues 		// set fStream to the audio format we want to use. This is only a hint
1645e0e1689SAdrien Destugues 		// (each encoder has a different set of accepted formats)
1653ca4a7b1SStephan Aßmus 		switch (format->u.raw_audio.format) {
1663ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_FLOAT:
167428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_FLT;
1683ca4a7b1SStephan Aßmus 				break;
1693ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_DOUBLE:
170428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_DBL;
1713ca4a7b1SStephan Aßmus 				break;
1723ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_INT:
173428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_S32;
1743ca4a7b1SStephan Aßmus 				break;
1753ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_SHORT:
176428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_S16;
1773ca4a7b1SStephan Aßmus 				break;
1783ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_UCHAR:
179428d87c5SBarrett17 				fStream->codecpar->format = AV_SAMPLE_FMT_U8;
1803ca4a7b1SStephan Aßmus 				break;
1813ca4a7b1SStephan Aßmus 
1823ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_CHAR:
1833ca4a7b1SStephan Aßmus 			default:
1843ca4a7b1SStephan Aßmus 				return B_MEDIA_BAD_FORMAT;
1853ca4a7b1SStephan Aßmus 				break;
18654897d5cSStephan Aßmus 		}
1875e0e1689SAdrien Destugues 
1885e0e1689SAdrien Destugues 		// Now negociate the actual format with the encoder
1895e0e1689SAdrien Destugues 		// First check if the requested format is acceptable
190428d87c5SBarrett17 		AVCodec* codec = avcodec_find_encoder(fStream->codecpar->codec_id);
1919e5c6946SAdrien Destugues 
1929e5c6946SAdrien Destugues 		if (codec == NULL)
1939e5c6946SAdrien Destugues 			return B_MEDIA_BAD_FORMAT;
1949e5c6946SAdrien Destugues 
1955e0e1689SAdrien Destugues 		const enum AVSampleFormat *p = codec->sample_fmts;
1965e0e1689SAdrien Destugues 		for (; *p != -1; p++) {
197428d87c5SBarrett17 			if (*p == fStream->codecpar->format)
1985e0e1689SAdrien Destugues 				break;
1995e0e1689SAdrien Destugues 		}
2005e0e1689SAdrien Destugues 		// If not, force one of the acceptable ones
2015e0e1689SAdrien Destugues 		if (*p == -1) {
202428d87c5SBarrett17 			fStream->codecpar->format = codec->sample_fmts[0];
2035e0e1689SAdrien Destugues 
2045e0e1689SAdrien Destugues 			// And finally set the format struct to the accepted format. It is
2055e0e1689SAdrien Destugues 			// then up to the caller to make sure we get data matching that
2065e0e1689SAdrien Destugues 			// format.
207428d87c5SBarrett17 			switch (fStream->codecpar->format) {
2085e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_FLT:
2095e0e1689SAdrien Destugues 					format->u.raw_audio.format
2105e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_FLOAT;
2115e0e1689SAdrien Destugues 					break;
2125e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_DBL:
2135e0e1689SAdrien Destugues 					format->u.raw_audio.format
2145e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_DOUBLE;
2155e0e1689SAdrien Destugues 					break;
2165e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_S32:
2175e0e1689SAdrien Destugues 					format->u.raw_audio.format
2185e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_INT;
2195e0e1689SAdrien Destugues 					break;
2205e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_S16:
2215e0e1689SAdrien Destugues 					format->u.raw_audio.format
2225e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_SHORT;
2235e0e1689SAdrien Destugues 					break;
2245e0e1689SAdrien Destugues 				case AV_SAMPLE_FMT_U8:
2255e0e1689SAdrien Destugues 					format->u.raw_audio.format
2265e0e1689SAdrien Destugues 						= media_raw_audio_format::B_AUDIO_UCHAR;
2275e0e1689SAdrien Destugues 					break;
2285e0e1689SAdrien Destugues 				default:
2295e0e1689SAdrien Destugues 					return B_MEDIA_BAD_FORMAT;
2305e0e1689SAdrien Destugues 					break;
2315e0e1689SAdrien Destugues 			}
2325e0e1689SAdrien Destugues 		}
2335e0e1689SAdrien Destugues 
2343ca4a7b1SStephan Aßmus 		if (format->u.raw_audio.channel_mask == 0) {
2353ca4a7b1SStephan Aßmus 			// guess the channel mask...
2363ca4a7b1SStephan Aßmus 			switch (format->u.raw_audio.channel_count) {
2373ca4a7b1SStephan Aßmus 				default:
2383ca4a7b1SStephan Aßmus 				case 2:
239428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
2403ca4a7b1SStephan Aßmus 					break;
2413ca4a7b1SStephan Aßmus 				case 1:
242428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
2433ca4a7b1SStephan Aßmus 					break;
2443ca4a7b1SStephan Aßmus 				case 3:
245428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_SURROUND;
2463ca4a7b1SStephan Aßmus 					break;
2473ca4a7b1SStephan Aßmus 				case 4:
248428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_QUAD;
2493ca4a7b1SStephan Aßmus 					break;
2503ca4a7b1SStephan Aßmus 				case 5:
251428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT0;
2523ca4a7b1SStephan Aßmus 					break;
2533ca4a7b1SStephan Aßmus 				case 6:
254428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_5POINT1;
2553ca4a7b1SStephan Aßmus 					break;
2563ca4a7b1SStephan Aßmus 				case 8:
257428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1;
2583ca4a7b1SStephan Aßmus 					break;
2593ca4a7b1SStephan Aßmus 				case 10:
260428d87c5SBarrett17 					fStream->codecpar->channel_layout = AV_CH_LAYOUT_7POINT1_WIDE;
2613ca4a7b1SStephan Aßmus 					break;
2623ca4a7b1SStephan Aßmus 			}
2633ca4a7b1SStephan Aßmus 		} else {
2643ca4a7b1SStephan Aßmus 			// The bits match 1:1 for media_multi_channels and FFmpeg defines.
265428d87c5SBarrett17 			fStream->codecpar->channel_layout = format->u.raw_audio.channel_mask;
2663ca4a7b1SStephan Aßmus 		}
2673ca4a7b1SStephan Aßmus 	}
2683ca4a7b1SStephan Aßmus 
2693ca4a7b1SStephan Aßmus 	TRACE("  stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n",
2703ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
271428d87c5SBarrett17 		fStream->codecpar->time_base.num, fStream->codecpar->time_base.den);
27254897d5cSStephan Aßmus 
273657983b8SStephan Aßmus #if 0
274657983b8SStephan Aßmus 	// Write the AVCodecContext pointer to the user data section of the
275657983b8SStephan Aßmus 	// media_format. For some encoders, it seems to be necessary to use
276657983b8SStephan Aßmus 	// the AVCodecContext of the AVStream in order to successfully encode
277657983b8SStephan Aßmus 	// anything and write valid media files. For example some codecs need
278657983b8SStephan Aßmus 	// to store meta data or global data in the container.
279657983b8SStephan Aßmus 	app_info appInfo;
280657983b8SStephan Aßmus 	if (be_app->GetAppInfo(&appInfo) == B_OK) {
281657983b8SStephan Aßmus 		uchar* userData = format->user_data;
282657983b8SStephan Aßmus 		*(uint32*)userData = 'ffmp';
283657983b8SStephan Aßmus 		userData += sizeof(uint32);
284657983b8SStephan Aßmus 		*(team_id*)userData = appInfo.team;
285657983b8SStephan Aßmus 		userData += sizeof(team_id);
286657983b8SStephan Aßmus 		*(AVCodecContext**)userData = fStream->codec;
287657983b8SStephan Aßmus 	}
288657983b8SStephan Aßmus #endif
289657983b8SStephan Aßmus 
2904384acf6SStephan Aßmus 	return B_OK;
2914384acf6SStephan Aßmus }
2924384acf6SStephan Aßmus 
2934384acf6SStephan Aßmus 
2944384acf6SStephan Aßmus status_t
2954384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
2964384acf6SStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
2974384acf6SStephan Aßmus {
29896590b5bSStephan Aßmus 	TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, "
29996590b5bSStephan Aßmus 		"start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize,
30096590b5bSStephan Aßmus 		encodeInfo->start_time);
3014384acf6SStephan Aßmus 
3024384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
3034384acf6SStephan Aßmus 
30454897d5cSStephan Aßmus 	fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
30554897d5cSStephan Aßmus 	fPacket.size = chunkSize;
30654897d5cSStephan Aßmus 
30796590b5bSStephan Aßmus 	fPacket.pts = int64_t((double)encodeInfo->start_time
30896590b5bSStephan Aßmus 		* fStream->time_base.den / (1000000.0 * fStream->time_base.num)
30996590b5bSStephan Aßmus 		+ 0.5);
31069372b55SStephan Aßmus 
311*e93fce62SBarrett17 	fPacket.dts = fPacket.pts;
312*e93fce62SBarrett17 
31369372b55SStephan Aßmus 	fPacket.flags = 0;
31469372b55SStephan Aßmus 	if ((encodeInfo->flags & B_MEDIA_KEY_FRAME) != 0)
31569372b55SStephan Aßmus 		fPacket.flags |= AV_PKT_FLAG_KEY;
31669372b55SStephan Aßmus 
3173ca4a7b1SStephan Aßmus 	TRACE_PACKET("  PTS: %lld (stream->time_base: (%d/%d), "
3183ca4a7b1SStephan Aßmus 		"codec->time_base: (%d/%d))\n", fPacket.pts,
3193ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
320428d87c5SBarrett17 		fStream->codecpar->time_base.num, fStream->codecpar->time_base.den);
3213ca4a7b1SStephan Aßmus 
32254897d5cSStephan Aßmus #if 0
32354897d5cSStephan Aßmus 	// TODO: Eventually, we need to write interleaved packets, but
32454897d5cSStephan Aßmus 	// maybe we are only supposed to use this if we have actually
32554897d5cSStephan Aßmus 	// more than one stream. For the moment, this crashes in AVPacket
32654897d5cSStephan Aßmus 	// shuffling inside libavformat. Maybe if we want to use this, we
32754897d5cSStephan Aßmus 	// need to allocate a separate AVPacket and copy the chunk buffer.
328ee9d0e02SBarrett17 	int result = av_interleaved_write_frame(fFormatContext, &fPacket);
32954897d5cSStephan Aßmus 	if (result < 0)
33054897d5cSStephan Aßmus 		TRACE("  av_interleaved_write_frame(): %d\n", result);
33154897d5cSStephan Aßmus #else
332ee9d0e02SBarrett17 	int result = av_write_frame(fFormatContext, &fPacket);
33354897d5cSStephan Aßmus 	if (result < 0)
33454897d5cSStephan Aßmus 		TRACE("  av_write_frame(): %d\n", result);
33554897d5cSStephan Aßmus #endif
33654897d5cSStephan Aßmus 
33754897d5cSStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
3384384acf6SStephan Aßmus }
3394384acf6SStephan Aßmus 
3404384acf6SStephan Aßmus 
3414384acf6SStephan Aßmus status_t
3424384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code,
3434384acf6SStephan Aßmus 	const void* data, size_t size, uint32 flags)
3444384acf6SStephan Aßmus {
3454384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n",
3464384acf6SStephan Aßmus 		code, data, size, flags);
3474384acf6SStephan Aßmus 
3484384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
3494384acf6SStephan Aßmus 
3504384acf6SStephan Aßmus 	return B_NOT_SUPPORTED;
3514384acf6SStephan Aßmus }
3524384acf6SStephan Aßmus 
3534384acf6SStephan Aßmus 
3546ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter
3556ac391b3SStephan Aßmus 
3566ac391b3SStephan Aßmus 
3576ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter()
3586ac391b3SStephan Aßmus 	:
359ee9d0e02SBarrett17 	fFormatContext(avformat_alloc_context()),
3606266cf35SDario Casalinuovo 	fCodecOpened(false),
361cbada661SDario Casalinuovo 	fHeaderError(-1),
362b95fa248SJérôme Duval 	fIOContext(NULL),
3636ac391b3SStephan Aßmus 	fStreamLock("stream lock")
3646ac391b3SStephan Aßmus {
3656ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AVFormatWriter\n");
3666ac391b3SStephan Aßmus }
3676ac391b3SStephan Aßmus 
3686ac391b3SStephan Aßmus 
3696ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter()
3706ac391b3SStephan Aßmus {
3716ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::~AVFormatWriter\n");
3724384acf6SStephan Aßmus 
3730c72a8aeSStephan Aßmus 	// Free the streams and close the AVCodecContexts
374ee9d0e02SBarrett17     for(unsigned i = 0; i < fFormatContext->nb_streams; i++) {
375ee9d0e02SBarrett17 		av_freep(&fFormatContext->streams[i]->codecpar);
376ee9d0e02SBarrett17 		av_freep(&fFormatContext->streams[i]);
3770c72a8aeSStephan Aßmus     }
3780c72a8aeSStephan Aßmus 
379ee9d0e02SBarrett17 	av_free(fFormatContext);
380b95fa248SJérôme Duval 	av_free(fIOContext->buffer);
381b95fa248SJérôme Duval 	av_free(fIOContext);
3826ac391b3SStephan Aßmus }
3836ac391b3SStephan Aßmus 
3846ac391b3SStephan Aßmus 
3856ac391b3SStephan Aßmus // #pragma mark -
3866ac391b3SStephan Aßmus 
3876ac391b3SStephan Aßmus 
3886ac391b3SStephan Aßmus status_t
3894384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat)
3904384acf6SStephan Aßmus {
3914384acf6SStephan Aßmus 	TRACE("AVFormatWriter::Init()\n");
3924384acf6SStephan Aßmus 
39396590b5bSStephan Aßmus 	uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize));
39496590b5bSStephan Aßmus 	if (buffer == NULL)
3954384acf6SStephan Aßmus 		return B_NO_MEMORY;
3964384acf6SStephan Aßmus 
3977e75e564SStefano Ceccherini 	// Allocate I/O context and initialize it with buffer
3987e75e564SStefano Ceccherini 	// and hook functions, pass ourself as cookie.
39984e70401SAdrien Destugues 	fIOContext = avio_alloc_context(buffer, kIOBufferSize, 1, this,
4007e75e564SStefano Ceccherini 			0, _Write, _Seek);
4017e75e564SStefano Ceccherini 	if (fIOContext == NULL) {
4027e75e564SStefano Ceccherini 		TRACE("av_alloc_put_byte() failed!\n");
4034384acf6SStephan Aßmus 		return B_ERROR;
4044384acf6SStephan Aßmus 	}
4054384acf6SStephan Aßmus 
40654897d5cSStephan Aßmus 	// Setup I/O hooks. This seems to be enough.
407ee9d0e02SBarrett17 	fFormatContext->pb = fIOContext;
4084384acf6SStephan Aßmus 
40954897d5cSStephan Aßmus 	// Set the AVOutputFormat according to fileFormat...
410ee9d0e02SBarrett17 	fFormatContext->oformat = av_guess_format(fileFormat->short_name,
4114384acf6SStephan Aßmus 		fileFormat->file_extension, fileFormat->mime_type);
412ee9d0e02SBarrett17 	if (fFormatContext->oformat == NULL) {
4134384acf6SStephan Aßmus 		TRACE("  failed to find AVOuputFormat for %s\n",
4144384acf6SStephan Aßmus 			fileFormat->short_name);
4154384acf6SStephan Aßmus 		return B_NOT_SUPPORTED;
4164384acf6SStephan Aßmus 	}
4174384acf6SStephan Aßmus 
4184384acf6SStephan Aßmus 	TRACE("  found AVOuputFormat for %s: %s\n", fileFormat->short_name,
419ee9d0e02SBarrett17 		fFormatContext->oformat->name);
4204384acf6SStephan Aßmus 
4214384acf6SStephan Aßmus 	return B_OK;
4224384acf6SStephan Aßmus }
4234384acf6SStephan Aßmus 
4244384acf6SStephan Aßmus 
4254384acf6SStephan Aßmus status_t
4266ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright)
4276ac391b3SStephan Aßmus {
4286ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright);
4296ac391b3SStephan Aßmus 
4306ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
4316ac391b3SStephan Aßmus }
4326ac391b3SStephan Aßmus 
4336ac391b3SStephan Aßmus 
4346ac391b3SStephan Aßmus status_t
4356ac391b3SStephan Aßmus AVFormatWriter::CommitHeader()
4366ac391b3SStephan Aßmus {
4376ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::CommitHeader\n");
4386ac391b3SStephan Aßmus 
439ee9d0e02SBarrett17 	if (fFormatContext == NULL)
4404384acf6SStephan Aßmus 		return B_NO_INIT;
4414384acf6SStephan Aßmus 
4426266cf35SDario Casalinuovo 	if (fCodecOpened)
4434384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
4444384acf6SStephan Aßmus 
4456266cf35SDario Casalinuovo 	// We need to close the codecs we opened, even in case of failure.
4466266cf35SDario Casalinuovo 	fCodecOpened = true;
4476266cf35SDario Casalinuovo 
448ee9d0e02SBarrett17 	fHeaderError = avformat_write_header(fFormatContext, NULL);
449cbada661SDario Casalinuovo 	if (fHeaderError < 0)
450cbada661SDario Casalinuovo 		TRACE("  avformat_write_header(): %d\n", fHeaderError);
45196590b5bSStephan Aßmus 
45296590b5bSStephan Aßmus 	#ifdef TRACE_AVFORMAT_WRITER
4533ca4a7b1SStephan Aßmus 	TRACE("  wrote header\n");
454ee9d0e02SBarrett17 	for (unsigned i = 0; i < fFormatContext->nb_streams; i++) {
455ee9d0e02SBarrett17 		AVStream* stream = fFormatContext->streams[i];
4563ca4a7b1SStephan Aßmus 		TRACE("  stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n",
4573ca4a7b1SStephan Aßmus 			i, stream->time_base.num, stream->time_base.den,
4583ca4a7b1SStephan Aßmus 			stream->codec->time_base.num, stream->codec->time_base.den);
4593ca4a7b1SStephan Aßmus 	}
460eb01f516SStephan Aßmus 	#endif // TRACE_AVFORMAT_WRITER
4613ca4a7b1SStephan Aßmus 
462cbada661SDario Casalinuovo 	return fHeaderError == 0 ? B_OK : B_ERROR;
4636ac391b3SStephan Aßmus }
4646ac391b3SStephan Aßmus 
4656ac391b3SStephan Aßmus 
4666ac391b3SStephan Aßmus status_t
4676ac391b3SStephan Aßmus AVFormatWriter::Flush()
4686ac391b3SStephan Aßmus {
4696ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Flush\n");
4706ac391b3SStephan Aßmus 
4716ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
4726ac391b3SStephan Aßmus }
4736ac391b3SStephan Aßmus 
4746ac391b3SStephan Aßmus 
4756ac391b3SStephan Aßmus status_t
4766ac391b3SStephan Aßmus AVFormatWriter::Close()
4776ac391b3SStephan Aßmus {
4786ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Close\n");
4796ac391b3SStephan Aßmus 
480ee9d0e02SBarrett17 	if (fFormatContext == NULL)
4814384acf6SStephan Aßmus 		return B_NO_INIT;
4824384acf6SStephan Aßmus 
4836266cf35SDario Casalinuovo 	if (!fCodecOpened)
4844384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
4854384acf6SStephan Aßmus 
486cbada661SDario Casalinuovo 	// From ffmpeg documentation: [av_write_trailer] may only be called
487cbada661SDario Casalinuovo 	// after a successful call to avformat_write_header.
4886266cf35SDario Casalinuovo 	if (fHeaderError != 0)
4896266cf35SDario Casalinuovo 		return B_ERROR;
4906266cf35SDario Casalinuovo 
491ee9d0e02SBarrett17 	int result = av_write_trailer(fFormatContext);
4924384acf6SStephan Aßmus 	if (result < 0)
4934384acf6SStephan Aßmus 		TRACE("  av_write_trailer(): %d\n", result);
4944384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
4956ac391b3SStephan Aßmus }
4966ac391b3SStephan Aßmus 
4976ac391b3SStephan Aßmus 
4986ac391b3SStephan Aßmus status_t
49969372b55SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, media_format* format,
50054897d5cSStephan Aßmus 	const media_codec_info* codecInfo)
5016ac391b3SStephan Aßmus {
5026ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AllocateCookie()\n");
5036ac391b3SStephan Aßmus 
5046266cf35SDario Casalinuovo 	if (fCodecOpened)
5050c72a8aeSStephan Aßmus 		return B_NOT_ALLOWED;
5060c72a8aeSStephan Aßmus 
5076ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
5086ac391b3SStephan Aßmus 
5096ac391b3SStephan Aßmus 	if (_cookie == NULL)
5106ac391b3SStephan Aßmus 		return B_BAD_VALUE;
5116ac391b3SStephan Aßmus 
512ee9d0e02SBarrett17 	StreamCookie* cookie = new(std::nothrow) StreamCookie(fFormatContext,
5134384acf6SStephan Aßmus 		&fStreamLock);
5144384acf6SStephan Aßmus 
51554897d5cSStephan Aßmus 	status_t ret = cookie->Init(format, codecInfo);
51654897d5cSStephan Aßmus 	if (ret != B_OK) {
51754897d5cSStephan Aßmus 		delete cookie;
51854897d5cSStephan Aßmus 		return ret;
51954897d5cSStephan Aßmus 	}
52054897d5cSStephan Aßmus 
52154897d5cSStephan Aßmus 	*_cookie = cookie;
52254897d5cSStephan Aßmus 	return B_OK;
5236ac391b3SStephan Aßmus }
5246ac391b3SStephan Aßmus 
5256ac391b3SStephan Aßmus 
5266ac391b3SStephan Aßmus status_t
5276ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie)
5286ac391b3SStephan Aßmus {
5296ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
5306ac391b3SStephan Aßmus 
5316ac391b3SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5326ac391b3SStephan Aßmus 	delete cookie;
5336ac391b3SStephan Aßmus 
5346ac391b3SStephan Aßmus 	return B_OK;
5356ac391b3SStephan Aßmus }
5366ac391b3SStephan Aßmus 
5376ac391b3SStephan Aßmus 
5386ac391b3SStephan Aßmus // #pragma mark -
5396ac391b3SStephan Aßmus 
5406ac391b3SStephan Aßmus 
5416ac391b3SStephan Aßmus status_t
542fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright)
543fa770e4cSStephan Aßmus {
544fa770e4cSStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright);
545fa770e4cSStephan Aßmus 
546fa770e4cSStephan Aßmus 	return B_NOT_SUPPORTED;
547fa770e4cSStephan Aßmus }
548fa770e4cSStephan Aßmus 
549fa770e4cSStephan Aßmus 
550fa770e4cSStephan Aßmus status_t
5514384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code,
5526ac391b3SStephan Aßmus 	const void* data, size_t size, uint32 flags)
5536ac391b3SStephan Aßmus {
5546ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n",
5556ac391b3SStephan Aßmus 		code, data, size, flags);
5566ac391b3SStephan Aßmus 
557ec2c5619SDario Casalinuovo 	if (fHeaderError != 0)
558ec2c5619SDario Casalinuovo 		return B_ERROR;
559ec2c5619SDario Casalinuovo 
5604384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5614384acf6SStephan Aßmus 	return cookie->AddTrackInfo(code, data, size, flags);
5626ac391b3SStephan Aßmus }
5636ac391b3SStephan Aßmus 
5646ac391b3SStephan Aßmus 
5656ac391b3SStephan Aßmus status_t
5664384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
567fa770e4cSStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
5686ac391b3SStephan Aßmus {
5693ca4a7b1SStephan Aßmus 	TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer,
5703ca4a7b1SStephan Aßmus 		chunkSize, encodeInfo);
5716ac391b3SStephan Aßmus 
572ec2c5619SDario Casalinuovo 	if (fHeaderError != 0)
573ec2c5619SDario Casalinuovo 		return B_ERROR;
574ec2c5619SDario Casalinuovo 
5754384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5764384acf6SStephan Aßmus 	return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo);
5776ac391b3SStephan Aßmus }
5786ac391b3SStephan Aßmus 
5796ac391b3SStephan Aßmus 
58054897d5cSStephan Aßmus // #pragma mark - I/O hooks
5812e9d65abSStephan Aßmus 
5822e9d65abSStephan Aßmus 
5832e9d65abSStephan Aßmus /*static*/ int
5847a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize)
5852e9d65abSStephan Aßmus {
5862e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n",
5872e9d65abSStephan Aßmus 		cookie, buffer, bufferSize);
5882e9d65abSStephan Aßmus 
5892e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
5902e9d65abSStephan Aßmus 
5912e9d65abSStephan Aßmus 	ssize_t written = writer->fTarget->Write(buffer, bufferSize);
5922e9d65abSStephan Aßmus 
5932e9d65abSStephan Aßmus 	TRACE_IO("  written: %ld\n", written);
5942e9d65abSStephan Aßmus 	return (int)written;
5952e9d65abSStephan Aßmus 
5962e9d65abSStephan Aßmus }
5972e9d65abSStephan Aßmus 
5982e9d65abSStephan Aßmus 
5992e9d65abSStephan Aßmus /*static*/ off_t
6002e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence)
6012e9d65abSStephan Aßmus {
6022e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n",
6032e9d65abSStephan Aßmus 		cookie, offset, whence);
6042e9d65abSStephan Aßmus 
6052e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
6062e9d65abSStephan Aßmus 
607a6b34a8cSDario Casalinuovo 	BMediaIO* mediaIO = dynamic_cast<BMediaIO*>(writer->fTarget);
608a6b34a8cSDario Casalinuovo 	if (mediaIO == NULL)
6092e9d65abSStephan Aßmus 		return -1;
6102e9d65abSStephan Aßmus 
6114384acf6SStephan Aßmus 	// Support for special file size retrieval API without seeking anywhere:
6122e9d65abSStephan Aßmus 	if (whence == AVSEEK_SIZE) {
6132e9d65abSStephan Aßmus 		off_t size;
614a6b34a8cSDario Casalinuovo 		if (mediaIO->GetSize(&size) == B_OK)
6152e9d65abSStephan Aßmus 			return size;
616a6b34a8cSDario Casalinuovo 
6172e9d65abSStephan Aßmus 		return -1;
6182e9d65abSStephan Aßmus 	}
6192e9d65abSStephan Aßmus 
620a6b34a8cSDario Casalinuovo 	off_t position = mediaIO->Seek(offset, whence);
6212e9d65abSStephan Aßmus 	TRACE_IO("  position: %lld\n", position);
6222e9d65abSStephan Aßmus 	if (position < 0)
6232e9d65abSStephan Aßmus 		return -1;
6242e9d65abSStephan Aßmus 
6252e9d65abSStephan Aßmus 	return position;
6262e9d65abSStephan Aßmus }
6272e9d65abSStephan Aßmus 
6282e9d65abSStephan Aßmus 
629