xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp (revision 96590b5bf77bcd3bbda4890e7f471c1e8751a95d)
16ac391b3SStephan Aßmus /*
26ac391b3SStephan Aßmus  * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
36ac391b3SStephan Aßmus  * All rights reserved. Distributed under the terms of the GNU L-GPL license.
46ac391b3SStephan Aßmus  */
56ac391b3SStephan Aßmus 
66ac391b3SStephan Aßmus #include "AVFormatWriter.h"
76ac391b3SStephan Aßmus 
86ac391b3SStephan Aßmus #include <stdio.h>
96ac391b3SStephan Aßmus #include <string.h>
106ac391b3SStephan Aßmus #include <stdlib.h>
116ac391b3SStephan Aßmus 
126ac391b3SStephan Aßmus #include <new>
136ac391b3SStephan Aßmus 
146ac391b3SStephan Aßmus #include <AutoDeleter.h>
156ac391b3SStephan Aßmus #include <Autolock.h>
166ac391b3SStephan Aßmus #include <ByteOrder.h>
176ac391b3SStephan Aßmus #include <DataIO.h>
186ac391b3SStephan Aßmus #include <MediaDefs.h>
196ac391b3SStephan Aßmus #include <MediaFormats.h>
206ac391b3SStephan Aßmus 
216ac391b3SStephan Aßmus extern "C" {
226ac391b3SStephan Aßmus 	#include "avformat.h"
236ac391b3SStephan Aßmus }
246ac391b3SStephan Aßmus 
256ac391b3SStephan Aßmus #include "DemuxerTable.h"
26eb01f516SStephan Aßmus #include "EncoderTable.h"
276ac391b3SStephan Aßmus #include "gfx_util.h"
286ac391b3SStephan Aßmus 
296ac391b3SStephan Aßmus 
30856d0bbfSStephan Aßmus //#define TRACE_AVFORMAT_WRITER
316ac391b3SStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER
326ac391b3SStephan Aßmus #	define TRACE printf
336ac391b3SStephan Aßmus #	define TRACE_IO(a...)
343ca4a7b1SStephan Aßmus #	define TRACE_PACKET printf
356ac391b3SStephan Aßmus #else
366ac391b3SStephan Aßmus #	define TRACE(a...)
376ac391b3SStephan Aßmus #	define TRACE_IO(a...)
386ac391b3SStephan Aßmus #	define TRACE_PACKET(a...)
396ac391b3SStephan Aßmus #endif
406ac391b3SStephan Aßmus 
416ac391b3SStephan Aßmus #define ERROR(a...) fprintf(stderr, a)
426ac391b3SStephan Aßmus 
436ac391b3SStephan Aßmus 
444384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024;
454384acf6SStephan Aßmus 	// TODO: This could depend on the BMediaFile creation flags, IIRC,
464384acf6SStephan Aßmus 	// they allow to specify a buffering mode.
474384acf6SStephan Aßmus 
480c72a8aeSStephan Aßmus // NOTE: The following works around some weird bug in libavformat. We
490c72a8aeSStephan Aßmus // have to open the AVFormatContext->AVStream->AVCodecContext, even though
500c72a8aeSStephan Aßmus // we are not interested in donig any encoding here!!
510c72a8aeSStephan Aßmus #define OPEN_CODEC_CONTEXT 1
520c72a8aeSStephan Aßmus #define GET_CONTEXT_DEFAULTS 0
534384acf6SStephan Aßmus 
544384acf6SStephan Aßmus 
556ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter::StreamCookie
566ac391b3SStephan Aßmus 
576ac391b3SStephan Aßmus 
586ac391b3SStephan Aßmus class AVFormatWriter::StreamCookie {
596ac391b3SStephan Aßmus public:
604384acf6SStephan Aßmus 								StreamCookie(AVFormatContext* context,
616ac391b3SStephan Aßmus 									BLocker* streamLock);
626ac391b3SStephan Aßmus 	virtual						~StreamCookie();
636ac391b3SStephan Aßmus 
6454897d5cSStephan Aßmus 			status_t			Init(const media_format* format,
6554897d5cSStephan Aßmus 									const media_codec_info* codecInfo);
664384acf6SStephan Aßmus 
674384acf6SStephan Aßmus 			status_t			WriteChunk(const void* chunkBuffer,
684384acf6SStephan Aßmus 									size_t chunkSize,
694384acf6SStephan Aßmus 									media_encode_info* encodeInfo);
704384acf6SStephan Aßmus 
714384acf6SStephan Aßmus 			status_t			AddTrackInfo(uint32 code, const void* data,
724384acf6SStephan Aßmus 									size_t size, uint32 flags);
734384acf6SStephan Aßmus 
746ac391b3SStephan Aßmus private:
754384acf6SStephan Aßmus 			AVFormatContext*	fContext;
764384acf6SStephan Aßmus 			AVStream*			fStream;
7754897d5cSStephan Aßmus 			AVPacket			fPacket;
7854897d5cSStephan Aßmus 			// Since different threads may write to the target,
796ac391b3SStephan Aßmus 			// we need to protect the file position and I/O by a lock.
806ac391b3SStephan Aßmus 			BLocker*			fStreamLock;
816ac391b3SStephan Aßmus };
826ac391b3SStephan Aßmus 
836ac391b3SStephan Aßmus 
846ac391b3SStephan Aßmus 
854384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context,
866ac391b3SStephan Aßmus 		BLocker* streamLock)
876ac391b3SStephan Aßmus 	:
884384acf6SStephan Aßmus 	fContext(context),
894384acf6SStephan Aßmus 	fStream(NULL),
900c72a8aeSStephan Aßmus 	fStreamLock(streamLock)
916ac391b3SStephan Aßmus {
920c72a8aeSStephan Aßmus 	av_init_packet(&fPacket);
936ac391b3SStephan Aßmus }
946ac391b3SStephan Aßmus 
956ac391b3SStephan Aßmus 
966ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie()
976ac391b3SStephan Aßmus {
986ac391b3SStephan Aßmus }
996ac391b3SStephan Aßmus 
1006ac391b3SStephan Aßmus 
1014384acf6SStephan Aßmus status_t
10254897d5cSStephan Aßmus AVFormatWriter::StreamCookie::Init(const media_format* format,
10354897d5cSStephan Aßmus 	const media_codec_info* codecInfo)
1044384acf6SStephan Aßmus {
1056e567425SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::Init()\n");
1064384acf6SStephan Aßmus 
1074384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
1084384acf6SStephan Aßmus 
10954897d5cSStephan Aßmus 	fPacket.stream_index = fContext->nb_streams;
11054897d5cSStephan Aßmus 	fStream = av_new_stream(fContext, fPacket.stream_index);
1114384acf6SStephan Aßmus 
1124384acf6SStephan Aßmus 	if (fStream == NULL) {
1134384acf6SStephan Aßmus 		TRACE("  failed to add new stream\n");
1144384acf6SStephan Aßmus 		return B_ERROR;
1154384acf6SStephan Aßmus 	}
1164384acf6SStephan Aßmus 
1170c72a8aeSStephan Aßmus //	TRACE("  fStream->codec: %p\n", fStream->codec);
1180c72a8aeSStephan Aßmus 	// TODO: This is a hack for now! Use avcodec_find_encoder_by_name()
1190c72a8aeSStephan Aßmus 	// or something similar...
1200c72a8aeSStephan Aßmus 	fStream->codec->codec_id = (CodecID)codecInfo->sub_id;
121eb01f516SStephan Aßmus 	if (fStream->codec->codec_id == CODEC_ID_NONE)
122eb01f516SStephan Aßmus 		fStream->codec->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) {
1260c72a8aeSStephan Aßmus 		fStream->codec->codec_type = CODEC_TYPE_VIDEO;
1270c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS
1280c72a8aeSStephan Aßmus // NOTE: API example does not do this:
1290c72a8aeSStephan Aßmus 		avcodec_get_context_defaults(fStream->codec);
1300c72a8aeSStephan Aßmus #endif
13154897d5cSStephan Aßmus 		// frame rate
13254897d5cSStephan Aßmus 		fStream->codec->time_base.den = (int)format->u.raw_video.field_rate;
13354897d5cSStephan Aßmus 		fStream->codec->time_base.num = 1;
1340c72a8aeSStephan Aßmus // NOTE: API example does not do this:
1350c72a8aeSStephan Aßmus //		fStream->r_frame_rate.den = (int)format->u.raw_video.field_rate;
1360c72a8aeSStephan Aßmus //		fStream->r_frame_rate.num = 1;
1370c72a8aeSStephan Aßmus //		fStream->time_base.den = (int)format->u.raw_video.field_rate;
1380c72a8aeSStephan Aßmus //		fStream->time_base.num = 1;
13954897d5cSStephan Aßmus 		// video size
14054897d5cSStephan Aßmus 		fStream->codec->width = format->u.raw_video.display.line_width;
14154897d5cSStephan Aßmus 		fStream->codec->height = format->u.raw_video.display.line_count;
14254897d5cSStephan Aßmus 		// pixel aspect ratio
14354897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.num
14454897d5cSStephan Aßmus 			= format->u.raw_video.pixel_width_aspect;
14554897d5cSStephan Aßmus 		fStream->sample_aspect_ratio.den
14654897d5cSStephan Aßmus 			= format->u.raw_video.pixel_height_aspect;
14754897d5cSStephan Aßmus 		if (fStream->sample_aspect_ratio.num == 0
14854897d5cSStephan Aßmus 			|| fStream->sample_aspect_ratio.den == 0) {
14954897d5cSStephan Aßmus 			av_reduce(&fStream->sample_aspect_ratio.num,
15054897d5cSStephan Aßmus 				&fStream->sample_aspect_ratio.den, fStream->codec->width,
15154897d5cSStephan Aßmus 				fStream->codec->height, 255);
15254897d5cSStephan Aßmus 		}
15354897d5cSStephan Aßmus 
15454897d5cSStephan Aßmus 		fStream->codec->sample_aspect_ratio = fStream->sample_aspect_ratio;
15554897d5cSStephan Aßmus 		// TODO: Don't hard code this...
15654897d5cSStephan Aßmus 		fStream->codec->pix_fmt = PIX_FMT_YUV420P;
1570c72a8aeSStephan Aßmus 
1580c72a8aeSStephan Aßmus 		// Some formats want stream headers to be separate
1590c72a8aeSStephan Aßmus 		if ((fContext->oformat->flags & AVFMT_GLOBALHEADER) != 0)
1600c72a8aeSStephan Aßmus 			fStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
16154897d5cSStephan Aßmus 	} else if (format->type == B_MEDIA_RAW_AUDIO) {
1620c72a8aeSStephan Aßmus 		fStream->codec->codec_type = CODEC_TYPE_AUDIO;
1630c72a8aeSStephan Aßmus #if GET_CONTEXT_DEFAULTS
1640c72a8aeSStephan Aßmus // NOTE: API example does not do this:
1650c72a8aeSStephan Aßmus 		avcodec_get_context_defaults(fStream->codec);
1660c72a8aeSStephan Aßmus #endif
1670c72a8aeSStephan Aßmus 		// frame rate
1680c72a8aeSStephan Aßmus 		fStream->codec->sample_rate = (int)format->u.raw_audio.frame_rate;
1690c72a8aeSStephan Aßmus 
1703ca4a7b1SStephan Aßmus 		// channels
1713ca4a7b1SStephan Aßmus 		fStream->codec->channels = format->u.raw_audio.channel_count;
1723ca4a7b1SStephan Aßmus 		switch (format->u.raw_audio.format) {
1733ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_FLOAT:
1743ca4a7b1SStephan Aßmus 				fStream->codec->sample_fmt = SAMPLE_FMT_FLT;
1753ca4a7b1SStephan Aßmus 				break;
1763ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_DOUBLE:
1773ca4a7b1SStephan Aßmus 				fStream->codec->sample_fmt = SAMPLE_FMT_DBL;
1783ca4a7b1SStephan Aßmus 				break;
1793ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_INT:
1803ca4a7b1SStephan Aßmus 				fStream->codec->sample_fmt = SAMPLE_FMT_S32;
1813ca4a7b1SStephan Aßmus 				break;
1823ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_SHORT:
1833ca4a7b1SStephan Aßmus 				fStream->codec->sample_fmt = SAMPLE_FMT_S16;
1843ca4a7b1SStephan Aßmus 				break;
1853ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_UCHAR:
1863ca4a7b1SStephan Aßmus 				fStream->codec->sample_fmt = SAMPLE_FMT_U8;
1873ca4a7b1SStephan Aßmus 				break;
1883ca4a7b1SStephan Aßmus 
1893ca4a7b1SStephan Aßmus 			case media_raw_audio_format::B_AUDIO_CHAR:
1903ca4a7b1SStephan Aßmus 			default:
1913ca4a7b1SStephan Aßmus 				return B_MEDIA_BAD_FORMAT;
1923ca4a7b1SStephan Aßmus 				break;
19354897d5cSStephan Aßmus 		}
1943ca4a7b1SStephan Aßmus 		if (format->u.raw_audio.channel_mask == 0) {
1953ca4a7b1SStephan Aßmus 			// guess the channel mask...
1963ca4a7b1SStephan Aßmus 			switch (format->u.raw_audio.channel_count) {
1973ca4a7b1SStephan Aßmus 				default:
1983ca4a7b1SStephan Aßmus 				case 2:
1993ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_STEREO;
2003ca4a7b1SStephan Aßmus 					break;
2013ca4a7b1SStephan Aßmus 				case 1:
2023ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_MONO;
2033ca4a7b1SStephan Aßmus 					break;
2043ca4a7b1SStephan Aßmus 				case 3:
2053ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_SURROUND;
2063ca4a7b1SStephan Aßmus 					break;
2073ca4a7b1SStephan Aßmus 				case 4:
2083ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_QUAD;
2093ca4a7b1SStephan Aßmus 					break;
2103ca4a7b1SStephan Aßmus 				case 5:
2113ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_5POINT0;
2123ca4a7b1SStephan Aßmus 					break;
2133ca4a7b1SStephan Aßmus 				case 6:
2143ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_5POINT1;
2153ca4a7b1SStephan Aßmus 					break;
2163ca4a7b1SStephan Aßmus 				case 8:
2173ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_7POINT1;
2183ca4a7b1SStephan Aßmus 					break;
2193ca4a7b1SStephan Aßmus 				case 10:
2203ca4a7b1SStephan Aßmus 					fStream->codec->channel_layout = CH_LAYOUT_7POINT1_WIDE;
2213ca4a7b1SStephan Aßmus 					break;
2223ca4a7b1SStephan Aßmus 			}
2233ca4a7b1SStephan Aßmus 		} else {
2243ca4a7b1SStephan Aßmus 			// The bits match 1:1 for media_multi_channels and FFmpeg defines.
2253ca4a7b1SStephan Aßmus 			fStream->codec->channel_layout = format->u.raw_audio.channel_mask;
2263ca4a7b1SStephan Aßmus 		}
2273ca4a7b1SStephan Aßmus 	}
2283ca4a7b1SStephan Aßmus 
2293ca4a7b1SStephan Aßmus 	TRACE("  stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n",
2303ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
2313ca4a7b1SStephan Aßmus 		fStream->codec->time_base.num, fStream->codec->time_base.den);
23254897d5cSStephan Aßmus 
2334384acf6SStephan Aßmus 	return B_OK;
2344384acf6SStephan Aßmus }
2354384acf6SStephan Aßmus 
2364384acf6SStephan Aßmus 
2374384acf6SStephan Aßmus status_t
2384384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
2394384acf6SStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
2404384acf6SStephan Aßmus {
241*96590b5bSStephan Aßmus 	TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, "
242*96590b5bSStephan Aßmus 		"start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize,
243*96590b5bSStephan Aßmus 		encodeInfo->start_time);
2444384acf6SStephan Aßmus 
2454384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
2464384acf6SStephan Aßmus 
24754897d5cSStephan Aßmus 	// TODO: Probably the AVCodecEncoder needs to pass packet data
24854897d5cSStephan Aßmus 	// in encodeInfo...
24954897d5cSStephan Aßmus 
25054897d5cSStephan Aßmus 	fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
25154897d5cSStephan Aßmus 	fPacket.size = chunkSize;
25254897d5cSStephan Aßmus 
253*96590b5bSStephan Aßmus 	fPacket.pts = int64_t((double)encodeInfo->start_time
254*96590b5bSStephan Aßmus 		* fStream->time_base.den / (1000000.0 * fStream->time_base.num)
255*96590b5bSStephan Aßmus 		+ 0.5);
2563ca4a7b1SStephan Aßmus 	TRACE_PACKET("  PTS: %lld  (stream->time_base: (%d/%d), "
2573ca4a7b1SStephan Aßmus 		"codec->time_base: (%d/%d))\n", fPacket.pts,
2583ca4a7b1SStephan Aßmus 		fStream->time_base.num, fStream->time_base.den,
2593ca4a7b1SStephan Aßmus 		fStream->codec->time_base.num, fStream->codec->time_base.den);
2603ca4a7b1SStephan Aßmus 
2613ca4a7b1SStephan Aßmus // From ffmpeg.c::do_audio_out():
2620c72a8aeSStephan Aßmus // TODO:
2633ca4a7b1SStephan Aßmus //	if (enc->coded_frame && enc->coded_frame->pts != AV_NOPTS_VALUE)
2643ca4a7b1SStephan Aßmus //		fPacket.pts = av_rescale_q(enc->coded_frame->pts,
2653ca4a7b1SStephan Aßmus //		enc->time_base, ost->st->time_base);
2663ca4a7b1SStephan Aßmus 
2673ca4a7b1SStephan Aßmus 
26854897d5cSStephan Aßmus #if 0
26954897d5cSStephan Aßmus 	// TODO: Eventually, we need to write interleaved packets, but
27054897d5cSStephan Aßmus 	// maybe we are only supposed to use this if we have actually
27154897d5cSStephan Aßmus 	// more than one stream. For the moment, this crashes in AVPacket
27254897d5cSStephan Aßmus 	// shuffling inside libavformat. Maybe if we want to use this, we
27354897d5cSStephan Aßmus 	// need to allocate a separate AVPacket and copy the chunk buffer.
27454897d5cSStephan Aßmus 	int result = av_interleaved_write_frame(fContext, &fPacket);
27554897d5cSStephan Aßmus 	if (result < 0)
27654897d5cSStephan Aßmus 		TRACE("  av_interleaved_write_frame(): %d\n", result);
27754897d5cSStephan Aßmus #else
27854897d5cSStephan Aßmus 	int result = av_write_frame(fContext, &fPacket);
27954897d5cSStephan Aßmus 	if (result < 0)
28054897d5cSStephan Aßmus 		TRACE("  av_write_frame(): %d\n", result);
28154897d5cSStephan Aßmus #endif
28254897d5cSStephan Aßmus 
28354897d5cSStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
2844384acf6SStephan Aßmus }
2854384acf6SStephan Aßmus 
2864384acf6SStephan Aßmus 
2874384acf6SStephan Aßmus status_t
2884384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code,
2894384acf6SStephan Aßmus 	const void* data, size_t size, uint32 flags)
2904384acf6SStephan Aßmus {
2914384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n",
2924384acf6SStephan Aßmus 		code, data, size, flags);
2934384acf6SStephan Aßmus 
2944384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
2954384acf6SStephan Aßmus 
2964384acf6SStephan Aßmus 	return B_NOT_SUPPORTED;
2974384acf6SStephan Aßmus }
2984384acf6SStephan Aßmus 
2994384acf6SStephan Aßmus 
3006ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter
3016ac391b3SStephan Aßmus 
3026ac391b3SStephan Aßmus 
3036ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter()
3046ac391b3SStephan Aßmus 	:
3054384acf6SStephan Aßmus 	fContext(avformat_alloc_context()),
3064384acf6SStephan Aßmus 	fHeaderWritten(false),
3076ac391b3SStephan Aßmus 	fStreamLock("stream lock")
3086ac391b3SStephan Aßmus {
3096ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AVFormatWriter\n");
3106ac391b3SStephan Aßmus }
3116ac391b3SStephan Aßmus 
3126ac391b3SStephan Aßmus 
3136ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter()
3146ac391b3SStephan Aßmus {
3156ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::~AVFormatWriter\n");
3164384acf6SStephan Aßmus 
3170c72a8aeSStephan Aßmus 	// Free the streams and close the AVCodecContexts
3180c72a8aeSStephan Aßmus     for(unsigned i = 0; i < fContext->nb_streams; i++) {
3190c72a8aeSStephan Aßmus #if OPEN_CODEC_CONTEXT
3200c72a8aeSStephan Aßmus 		// We only need to close the AVCodecContext when we opened it.
321fac8f830SStefano Ceccherini 		// This is experimental, see CommitHeader().
322fac8f830SStefano Ceccherini 		if (fHeaderWritten)
3230c72a8aeSStephan Aßmus 			avcodec_close(fContext->streams[i]->codec);
3240c72a8aeSStephan Aßmus #endif
3250c72a8aeSStephan Aßmus 		av_freep(&fContext->streams[i]->codec);
3260c72a8aeSStephan Aßmus 		av_freep(&fContext->streams[i]);
3270c72a8aeSStephan Aßmus     }
3280c72a8aeSStephan Aßmus 
3294384acf6SStephan Aßmus 	av_free(fContext);
330*96590b5bSStephan Aßmus 	av_free(fIOContext.buffer);
3316ac391b3SStephan Aßmus }
3326ac391b3SStephan Aßmus 
3336ac391b3SStephan Aßmus 
3346ac391b3SStephan Aßmus // #pragma mark -
3356ac391b3SStephan Aßmus 
3366ac391b3SStephan Aßmus 
3376ac391b3SStephan Aßmus status_t
3384384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat)
3394384acf6SStephan Aßmus {
3404384acf6SStephan Aßmus 	TRACE("AVFormatWriter::Init()\n");
3414384acf6SStephan Aßmus 
342*96590b5bSStephan Aßmus 	uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize));
343*96590b5bSStephan Aßmus 	if (buffer == NULL)
3444384acf6SStephan Aßmus 		return B_NO_MEMORY;
3454384acf6SStephan Aßmus 
3464384acf6SStephan Aßmus 	// Init I/O context with buffer and hook functions, pass ourself as
3474384acf6SStephan Aßmus 	// cookie.
348*96590b5bSStephan Aßmus 	if (init_put_byte(&fIOContext, buffer, kIOBufferSize, 1, this,
3494384acf6SStephan Aßmus 			0, _Write, _Seek) != 0) {
3504384acf6SStephan Aßmus 		TRACE("  init_put_byte() failed!\n");
3514384acf6SStephan Aßmus 		return B_ERROR;
3524384acf6SStephan Aßmus 	}
3534384acf6SStephan Aßmus 
35454897d5cSStephan Aßmus 	// Setup I/O hooks. This seems to be enough.
3554384acf6SStephan Aßmus 	fContext->pb = &fIOContext;
3564384acf6SStephan Aßmus 
35754897d5cSStephan Aßmus 	// Set the AVOutputFormat according to fileFormat...
358*96590b5bSStephan Aßmus 	fContext->oformat = av_guess_format(fileFormat->short_name,
3594384acf6SStephan Aßmus 		fileFormat->file_extension, fileFormat->mime_type);
3604384acf6SStephan Aßmus 	if (fContext->oformat == NULL) {
3614384acf6SStephan Aßmus 		TRACE("  failed to find AVOuputFormat for %s\n",
3624384acf6SStephan Aßmus 			fileFormat->short_name);
3634384acf6SStephan Aßmus 		return B_NOT_SUPPORTED;
3644384acf6SStephan Aßmus 	}
3654384acf6SStephan Aßmus 
3664384acf6SStephan Aßmus 	TRACE("  found AVOuputFormat for %s: %s\n", fileFormat->short_name,
3674384acf6SStephan Aßmus 		fContext->oformat->name);
3684384acf6SStephan Aßmus 
3694384acf6SStephan Aßmus 	return B_OK;
3704384acf6SStephan Aßmus }
3714384acf6SStephan Aßmus 
3724384acf6SStephan Aßmus 
3734384acf6SStephan Aßmus status_t
3746ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright)
3756ac391b3SStephan Aßmus {
3766ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright);
3776ac391b3SStephan Aßmus 
3786ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
3796ac391b3SStephan Aßmus }
3806ac391b3SStephan Aßmus 
3816ac391b3SStephan Aßmus 
3826ac391b3SStephan Aßmus status_t
3836ac391b3SStephan Aßmus AVFormatWriter::CommitHeader()
3846ac391b3SStephan Aßmus {
3856ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::CommitHeader\n");
3866ac391b3SStephan Aßmus 
3874384acf6SStephan Aßmus 	if (fContext == NULL)
3884384acf6SStephan Aßmus 		return B_NO_INIT;
3894384acf6SStephan Aßmus 
3904384acf6SStephan Aßmus 	if (fHeaderWritten)
3914384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
3924384acf6SStephan Aßmus 
3930c72a8aeSStephan Aßmus 	// According to output_example.c, the output parameters must be set even
3940c72a8aeSStephan Aßmus 	// if none are specified. In the example, this call is used after the
3950c72a8aeSStephan Aßmus 	// streams have been created.
3960c72a8aeSStephan Aßmus 	if (av_set_parameters(fContext, NULL) < 0)
3970c72a8aeSStephan Aßmus 		return B_ERROR;
3980c72a8aeSStephan Aßmus 
3993ca4a7b1SStephan Aßmus 	for (unsigned i = 0; i < fContext->nb_streams; i++) {
4003ca4a7b1SStephan Aßmus 		AVStream* stream = fContext->streams[i];
4010c72a8aeSStephan Aßmus #if OPEN_CODEC_CONTEXT
4020c72a8aeSStephan Aßmus 		// NOTE: Experimental, this should not be needed. Especially, since
4030c72a8aeSStephan Aßmus 		// we have no idea (in the future) what CodecID some encoder uses,
4040c72a8aeSStephan Aßmus 		// it may be an encoder from a different plugin.
4050c72a8aeSStephan Aßmus 		AVCodecContext* codecContext = stream->codec;
4060c72a8aeSStephan Aßmus 		AVCodec* codec = avcodec_find_encoder(codecContext->codec_id);
4070c72a8aeSStephan Aßmus 		if (codec == NULL || avcodec_open(codecContext, codec) < 0) {
4080c72a8aeSStephan Aßmus 			TRACE("  stream[%u] - failed to open AVCodecContext\n", i);
4090c72a8aeSStephan Aßmus 		}
4100c72a8aeSStephan Aßmus #endif
4113ca4a7b1SStephan Aßmus 		TRACE("  stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n",
4123ca4a7b1SStephan Aßmus 			i, stream->time_base.num, stream->time_base.den,
4133ca4a7b1SStephan Aßmus 			stream->codec->time_base.num, stream->codec->time_base.den);
4143ca4a7b1SStephan Aßmus 	}
4153ca4a7b1SStephan Aßmus 
4164384acf6SStephan Aßmus 	int result = av_write_header(fContext);
4174384acf6SStephan Aßmus 	if (result < 0)
4184384acf6SStephan Aßmus 		TRACE("  av_write_header(): %d\n", result);
419*96590b5bSStephan Aßmus 
420*96590b5bSStephan Aßmus 	// We need to close the codecs we opened, even in case of failure.
4214384acf6SStephan Aßmus 	fHeaderWritten = true;
4224384acf6SStephan Aßmus 
423*96590b5bSStephan Aßmus 	#ifdef TRACE_AVFORMAT_WRITER
4243ca4a7b1SStephan Aßmus 	TRACE("  wrote header\n");
4253ca4a7b1SStephan Aßmus 	for (unsigned i = 0; i < fContext->nb_streams; i++) {
4263ca4a7b1SStephan Aßmus 		AVStream* stream = fContext->streams[i];
4273ca4a7b1SStephan Aßmus 		TRACE("  stream[%u] time_base: (%d/%d), codec->time_base: (%d/%d)\n",
4283ca4a7b1SStephan Aßmus 			i, stream->time_base.num, stream->time_base.den,
4293ca4a7b1SStephan Aßmus 			stream->codec->time_base.num, stream->codec->time_base.den);
4303ca4a7b1SStephan Aßmus 	}
431eb01f516SStephan Aßmus 	#endif // TRACE_AVFORMAT_WRITER
4323ca4a7b1SStephan Aßmus 
4334384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
4346ac391b3SStephan Aßmus }
4356ac391b3SStephan Aßmus 
4366ac391b3SStephan Aßmus 
4376ac391b3SStephan Aßmus status_t
4386ac391b3SStephan Aßmus AVFormatWriter::Flush()
4396ac391b3SStephan Aßmus {
4406ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Flush\n");
4416ac391b3SStephan Aßmus 
4426ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
4436ac391b3SStephan Aßmus }
4446ac391b3SStephan Aßmus 
4456ac391b3SStephan Aßmus 
4466ac391b3SStephan Aßmus status_t
4476ac391b3SStephan Aßmus AVFormatWriter::Close()
4486ac391b3SStephan Aßmus {
4496ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Close\n");
4506ac391b3SStephan Aßmus 
4514384acf6SStephan Aßmus 	if (fContext == NULL)
4524384acf6SStephan Aßmus 		return B_NO_INIT;
4534384acf6SStephan Aßmus 
4544384acf6SStephan Aßmus 	if (!fHeaderWritten)
4554384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
4564384acf6SStephan Aßmus 
4574384acf6SStephan Aßmus 	int result = av_write_trailer(fContext);
4584384acf6SStephan Aßmus 	if (result < 0)
4594384acf6SStephan Aßmus 		TRACE("  av_write_trailer(): %d\n", result);
4604384acf6SStephan Aßmus 
4614384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
4626ac391b3SStephan Aßmus }
4636ac391b3SStephan Aßmus 
4646ac391b3SStephan Aßmus 
4656ac391b3SStephan Aßmus status_t
46654897d5cSStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format,
46754897d5cSStephan Aßmus 	const media_codec_info* codecInfo)
4686ac391b3SStephan Aßmus {
4696ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AllocateCookie()\n");
4706ac391b3SStephan Aßmus 
4710c72a8aeSStephan Aßmus 	if (fHeaderWritten)
4720c72a8aeSStephan Aßmus 		return B_NOT_ALLOWED;
4730c72a8aeSStephan Aßmus 
4746ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
4756ac391b3SStephan Aßmus 
4766ac391b3SStephan Aßmus 	if (_cookie == NULL)
4776ac391b3SStephan Aßmus 		return B_BAD_VALUE;
4786ac391b3SStephan Aßmus 
4794384acf6SStephan Aßmus 	StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext,
4804384acf6SStephan Aßmus 		&fStreamLock);
4814384acf6SStephan Aßmus 
48254897d5cSStephan Aßmus 	status_t ret = cookie->Init(format, codecInfo);
48354897d5cSStephan Aßmus 	if (ret != B_OK) {
48454897d5cSStephan Aßmus 		delete cookie;
48554897d5cSStephan Aßmus 		return ret;
48654897d5cSStephan Aßmus 	}
48754897d5cSStephan Aßmus 
48854897d5cSStephan Aßmus 	*_cookie = cookie;
48954897d5cSStephan Aßmus 	return B_OK;
4906ac391b3SStephan Aßmus }
4916ac391b3SStephan Aßmus 
4926ac391b3SStephan Aßmus 
4936ac391b3SStephan Aßmus status_t
4946ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie)
4956ac391b3SStephan Aßmus {
4966ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
4976ac391b3SStephan Aßmus 
4986ac391b3SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
4996ac391b3SStephan Aßmus 	delete cookie;
5006ac391b3SStephan Aßmus 
5016ac391b3SStephan Aßmus 	return B_OK;
5026ac391b3SStephan Aßmus }
5036ac391b3SStephan Aßmus 
5046ac391b3SStephan Aßmus 
5056ac391b3SStephan Aßmus // #pragma mark -
5066ac391b3SStephan Aßmus 
5076ac391b3SStephan Aßmus 
5086ac391b3SStephan Aßmus status_t
509fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright)
510fa770e4cSStephan Aßmus {
511fa770e4cSStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright);
512fa770e4cSStephan Aßmus 
513fa770e4cSStephan Aßmus 	return B_NOT_SUPPORTED;
514fa770e4cSStephan Aßmus }
515fa770e4cSStephan Aßmus 
516fa770e4cSStephan Aßmus 
517fa770e4cSStephan Aßmus status_t
5184384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code,
5196ac391b3SStephan Aßmus 	const void* data, size_t size, uint32 flags)
5206ac391b3SStephan Aßmus {
5216ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n",
5226ac391b3SStephan Aßmus 		code, data, size, flags);
5236ac391b3SStephan Aßmus 
5244384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5254384acf6SStephan Aßmus 	return cookie->AddTrackInfo(code, data, size, flags);
5266ac391b3SStephan Aßmus }
5276ac391b3SStephan Aßmus 
5286ac391b3SStephan Aßmus 
5296ac391b3SStephan Aßmus status_t
5304384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
531fa770e4cSStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
5326ac391b3SStephan Aßmus {
5333ca4a7b1SStephan Aßmus 	TRACE_PACKET("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer,
5343ca4a7b1SStephan Aßmus 		chunkSize, encodeInfo);
5356ac391b3SStephan Aßmus 
5364384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
5374384acf6SStephan Aßmus 	return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo);
5386ac391b3SStephan Aßmus }
5396ac391b3SStephan Aßmus 
5406ac391b3SStephan Aßmus 
54154897d5cSStephan Aßmus // #pragma mark - I/O hooks
5422e9d65abSStephan Aßmus 
5432e9d65abSStephan Aßmus 
5442e9d65abSStephan Aßmus /*static*/ int
5457a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize)
5462e9d65abSStephan Aßmus {
5472e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n",
5482e9d65abSStephan Aßmus 		cookie, buffer, bufferSize);
5492e9d65abSStephan Aßmus 
5502e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
5512e9d65abSStephan Aßmus 
5522e9d65abSStephan Aßmus 	ssize_t written = writer->fTarget->Write(buffer, bufferSize);
5532e9d65abSStephan Aßmus 
5542e9d65abSStephan Aßmus 	TRACE_IO("  written: %ld\n", written);
5552e9d65abSStephan Aßmus 	return (int)written;
5562e9d65abSStephan Aßmus 
5572e9d65abSStephan Aßmus }
5582e9d65abSStephan Aßmus 
5592e9d65abSStephan Aßmus 
5602e9d65abSStephan Aßmus /*static*/ off_t
5612e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence)
5622e9d65abSStephan Aßmus {
5632e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n",
5642e9d65abSStephan Aßmus 		cookie, offset, whence);
5652e9d65abSStephan Aßmus 
5662e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
5672e9d65abSStephan Aßmus 
5682e9d65abSStephan Aßmus 	BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget);
5692e9d65abSStephan Aßmus 	if (positionIO == NULL)
5702e9d65abSStephan Aßmus 		return -1;
5712e9d65abSStephan Aßmus 
5724384acf6SStephan Aßmus 	// Support for special file size retrieval API without seeking anywhere:
5732e9d65abSStephan Aßmus 	if (whence == AVSEEK_SIZE) {
5742e9d65abSStephan Aßmus 		off_t size;
5752e9d65abSStephan Aßmus 		if (positionIO->GetSize(&size) == B_OK)
5762e9d65abSStephan Aßmus 			return size;
5772e9d65abSStephan Aßmus 		return -1;
5782e9d65abSStephan Aßmus 	}
5792e9d65abSStephan Aßmus 
5802e9d65abSStephan Aßmus 	off_t position = positionIO->Seek(offset, whence);
5812e9d65abSStephan Aßmus 	TRACE_IO("  position: %lld\n", position);
5822e9d65abSStephan Aßmus 	if (position < 0)
5832e9d65abSStephan Aßmus 		return -1;
5842e9d65abSStephan Aßmus 
5852e9d65abSStephan Aßmus 	return position;
5862e9d65abSStephan Aßmus }
5872e9d65abSStephan Aßmus 
5882e9d65abSStephan Aßmus 
589