xref: /haiku/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp (revision 7a97958b0b29312dc186ed286e0c945931d8d760)
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"
266ac391b3SStephan Aßmus #include "gfx_util.h"
276ac391b3SStephan Aßmus 
286ac391b3SStephan Aßmus 
296ac391b3SStephan Aßmus #define TRACE_AVFORMAT_WRITER
306ac391b3SStephan Aßmus #ifdef TRACE_AVFORMAT_WRITER
316ac391b3SStephan Aßmus #	define TRACE printf
326ac391b3SStephan Aßmus #	define TRACE_IO(a...)
336ac391b3SStephan Aßmus #	define TRACE_PACKET(a...)
346ac391b3SStephan Aßmus #else
356ac391b3SStephan Aßmus #	define TRACE(a...)
366ac391b3SStephan Aßmus #	define TRACE_IO(a...)
376ac391b3SStephan Aßmus #	define TRACE_PACKET(a...)
386ac391b3SStephan Aßmus #endif
396ac391b3SStephan Aßmus 
406ac391b3SStephan Aßmus #define ERROR(a...) fprintf(stderr, a)
416ac391b3SStephan Aßmus 
426ac391b3SStephan Aßmus 
434384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024;
444384acf6SStephan Aßmus 	// TODO: This could depend on the BMediaFile creation flags, IIRC,
454384acf6SStephan Aßmus 	// they allow to specify a buffering mode.
464384acf6SStephan Aßmus 
474384acf6SStephan Aßmus 
484384acf6SStephan Aßmus // #pragma mark - URLProtocol
494384acf6SStephan Aßmus // TODO: Do we need to write an URLProtocol?
504384acf6SStephan Aßmus 
514384acf6SStephan Aßmus 
526ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter::StreamCookie
536ac391b3SStephan Aßmus 
546ac391b3SStephan Aßmus 
556ac391b3SStephan Aßmus class AVFormatWriter::StreamCookie {
566ac391b3SStephan Aßmus public:
574384acf6SStephan Aßmus 								StreamCookie(AVFormatContext* context,
586ac391b3SStephan Aßmus 									BLocker* streamLock);
596ac391b3SStephan Aßmus 	virtual						~StreamCookie();
606ac391b3SStephan Aßmus 
614384acf6SStephan Aßmus 			status_t			Init(const media_format* format);
624384acf6SStephan Aßmus 
634384acf6SStephan Aßmus 			status_t			WriteChunk(const void* chunkBuffer,
644384acf6SStephan Aßmus 									size_t chunkSize,
654384acf6SStephan Aßmus 									media_encode_info* encodeInfo);
664384acf6SStephan Aßmus 
674384acf6SStephan Aßmus 			status_t			AddTrackInfo(uint32 code, const void* data,
684384acf6SStephan Aßmus 									size_t size, uint32 flags);
694384acf6SStephan Aßmus 
706ac391b3SStephan Aßmus private:
714384acf6SStephan Aßmus 			AVFormatContext*	fContext;
724384acf6SStephan Aßmus 			AVStream*			fStream;
736ac391b3SStephan Aßmus 			// Since different threads may read from the source,
746ac391b3SStephan Aßmus 			// we need to protect the file position and I/O by a lock.
756ac391b3SStephan Aßmus 			BLocker*			fStreamLock;
766ac391b3SStephan Aßmus };
776ac391b3SStephan Aßmus 
786ac391b3SStephan Aßmus 
796ac391b3SStephan Aßmus 
804384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context,
816ac391b3SStephan Aßmus 		BLocker* streamLock)
826ac391b3SStephan Aßmus 	:
834384acf6SStephan Aßmus 	fContext(context),
844384acf6SStephan Aßmus 	fStream(NULL),
856ac391b3SStephan Aßmus 	fStreamLock(streamLock)
866ac391b3SStephan Aßmus {
876ac391b3SStephan Aßmus }
886ac391b3SStephan Aßmus 
896ac391b3SStephan Aßmus 
906ac391b3SStephan Aßmus AVFormatWriter::StreamCookie::~StreamCookie()
916ac391b3SStephan Aßmus {
926ac391b3SStephan Aßmus }
936ac391b3SStephan Aßmus 
946ac391b3SStephan Aßmus 
954384acf6SStephan Aßmus status_t
964384acf6SStephan Aßmus AVFormatWriter::StreamCookie::Init(const media_format* format)
974384acf6SStephan Aßmus {
984384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::Init()\n");
994384acf6SStephan Aßmus 
1004384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
1014384acf6SStephan Aßmus 
1024384acf6SStephan Aßmus 	fStream = av_new_stream(fContext, fContext->nb_streams);
1034384acf6SStephan Aßmus 
1044384acf6SStephan Aßmus 	if (fStream == NULL) {
1054384acf6SStephan Aßmus 		TRACE("  failed to add new stream\n");
1064384acf6SStephan Aßmus 		return B_ERROR;
1074384acf6SStephan Aßmus 	}
1084384acf6SStephan Aßmus 
1094384acf6SStephan Aßmus 	// TODO: Setup the stream according to the media format...
1104384acf6SStephan Aßmus 
1114384acf6SStephan Aßmus 	return B_OK;
1124384acf6SStephan Aßmus }
1134384acf6SStephan Aßmus 
1144384acf6SStephan Aßmus 
1154384acf6SStephan Aßmus status_t
1164384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
1174384acf6SStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
1184384acf6SStephan Aßmus {
1194384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld)\n",
1204384acf6SStephan Aßmus 		chunkBuffer, chunkSize);
1214384acf6SStephan Aßmus 
1224384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
1234384acf6SStephan Aßmus 
1244384acf6SStephan Aßmus 	return B_ERROR;
1254384acf6SStephan Aßmus }
1264384acf6SStephan Aßmus 
1274384acf6SStephan Aßmus 
1284384acf6SStephan Aßmus status_t
1294384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code,
1304384acf6SStephan Aßmus 	const void* data, size_t size, uint32 flags)
1314384acf6SStephan Aßmus {
1324384acf6SStephan Aßmus 	TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n",
1334384acf6SStephan Aßmus 		code, data, size, flags);
1344384acf6SStephan Aßmus 
1354384acf6SStephan Aßmus 	BAutolock _(fStreamLock);
1364384acf6SStephan Aßmus 
1374384acf6SStephan Aßmus 	return B_NOT_SUPPORTED;
1384384acf6SStephan Aßmus }
1394384acf6SStephan Aßmus 
1404384acf6SStephan Aßmus 
1416ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter
1426ac391b3SStephan Aßmus 
1436ac391b3SStephan Aßmus 
1446ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter()
1456ac391b3SStephan Aßmus 	:
1464384acf6SStephan Aßmus 	fContext(avformat_alloc_context()),
1474384acf6SStephan Aßmus 	fHeaderWritten(false),
1484384acf6SStephan Aßmus 	fIOBuffer(NULL),
1496ac391b3SStephan Aßmus 	fStreamLock("stream lock")
1506ac391b3SStephan Aßmus {
1516ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AVFormatWriter\n");
1526ac391b3SStephan Aßmus }
1536ac391b3SStephan Aßmus 
1546ac391b3SStephan Aßmus 
1556ac391b3SStephan Aßmus AVFormatWriter::~AVFormatWriter()
1566ac391b3SStephan Aßmus {
1576ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::~AVFormatWriter\n");
1584384acf6SStephan Aßmus 
1594384acf6SStephan Aßmus 	av_free(fContext);
1604384acf6SStephan Aßmus 
1614384acf6SStephan Aßmus 	delete[] fIOBuffer;
1626ac391b3SStephan Aßmus }
1636ac391b3SStephan Aßmus 
1646ac391b3SStephan Aßmus 
1656ac391b3SStephan Aßmus // #pragma mark -
1666ac391b3SStephan Aßmus 
1676ac391b3SStephan Aßmus 
1686ac391b3SStephan Aßmus status_t
1694384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat)
1704384acf6SStephan Aßmus {
1714384acf6SStephan Aßmus 	TRACE("AVFormatWriter::Init()\n");
1724384acf6SStephan Aßmus 
1734384acf6SStephan Aßmus 	delete[] fIOBuffer;
1744384acf6SStephan Aßmus 	fIOBuffer = new(std::nothrow) uint8[kIOBufferSize];
1754384acf6SStephan Aßmus 	if (fIOBuffer == NULL)
1764384acf6SStephan Aßmus 		return B_NO_MEMORY;
1774384acf6SStephan Aßmus 
1784384acf6SStephan Aßmus 	// Init I/O context with buffer and hook functions, pass ourself as
1794384acf6SStephan Aßmus 	// cookie.
1804384acf6SStephan Aßmus 	if (init_put_byte(&fIOContext, fIOBuffer, kIOBufferSize, 0, this,
1814384acf6SStephan Aßmus 			0, _Write, _Seek) != 0) {
1824384acf6SStephan Aßmus 		TRACE("  init_put_byte() failed!\n");
1834384acf6SStephan Aßmus 		return B_ERROR;
1844384acf6SStephan Aßmus 	}
1854384acf6SStephan Aßmus 
1864384acf6SStephan Aßmus 	// TODO: Is this how it works?
1874384acf6SStephan Aßmus 	// TODO: Is the cookie stored in ByteIOContext? Or does it need to be
1884384acf6SStephan Aßmus 	// stored in fContext->priv_data?
1894384acf6SStephan Aßmus 	fContext->pb = &fIOContext;
1904384acf6SStephan Aßmus 
1914384acf6SStephan Aßmus 	// TODO: Set the AVOutputFormat according to fileFormat...
1924384acf6SStephan Aßmus 	fContext->oformat = guess_format(fileFormat->short_name,
1934384acf6SStephan Aßmus 		fileFormat->file_extension, fileFormat->mime_type);
1944384acf6SStephan Aßmus 	if (fContext->oformat == NULL) {
1954384acf6SStephan Aßmus 		TRACE("  failed to find AVOuputFormat for %s\n",
1964384acf6SStephan Aßmus 			fileFormat->short_name);
1974384acf6SStephan Aßmus 		return B_NOT_SUPPORTED;
1984384acf6SStephan Aßmus 	}
1994384acf6SStephan Aßmus 
2004384acf6SStephan Aßmus 	TRACE("  found AVOuputFormat for %s: %s\n", fileFormat->short_name,
2014384acf6SStephan Aßmus 		fContext->oformat->name);
2024384acf6SStephan Aßmus 
2034384acf6SStephan Aßmus 	return B_OK;
2044384acf6SStephan Aßmus }
2054384acf6SStephan Aßmus 
2064384acf6SStephan Aßmus 
2074384acf6SStephan Aßmus status_t
2086ac391b3SStephan Aßmus AVFormatWriter::SetCopyright(const char* copyright)
2096ac391b3SStephan Aßmus {
2106ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%s)\n", copyright);
2116ac391b3SStephan Aßmus 
2126ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
2136ac391b3SStephan Aßmus }
2146ac391b3SStephan Aßmus 
2156ac391b3SStephan Aßmus 
2166ac391b3SStephan Aßmus status_t
2176ac391b3SStephan Aßmus AVFormatWriter::CommitHeader()
2186ac391b3SStephan Aßmus {
2196ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::CommitHeader\n");
2206ac391b3SStephan Aßmus 
2214384acf6SStephan Aßmus 	if (fContext == NULL)
2224384acf6SStephan Aßmus 		return B_NO_INIT;
2234384acf6SStephan Aßmus 
2244384acf6SStephan Aßmus 	if (fHeaderWritten)
2254384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
2264384acf6SStephan Aßmus 
2274384acf6SStephan Aßmus 	int result = av_write_header(fContext);
2284384acf6SStephan Aßmus 	if (result < 0)
2294384acf6SStephan Aßmus 		TRACE("  av_write_header(): %d\n", result);
2304384acf6SStephan Aßmus 	else
2314384acf6SStephan Aßmus 		fHeaderWritten = true;
2324384acf6SStephan Aßmus 
2334384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
2346ac391b3SStephan Aßmus }
2356ac391b3SStephan Aßmus 
2366ac391b3SStephan Aßmus 
2376ac391b3SStephan Aßmus status_t
2386ac391b3SStephan Aßmus AVFormatWriter::Flush()
2396ac391b3SStephan Aßmus {
2406ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Flush\n");
2416ac391b3SStephan Aßmus 
2426ac391b3SStephan Aßmus 	return B_NOT_SUPPORTED;
2436ac391b3SStephan Aßmus }
2446ac391b3SStephan Aßmus 
2456ac391b3SStephan Aßmus 
2466ac391b3SStephan Aßmus status_t
2476ac391b3SStephan Aßmus AVFormatWriter::Close()
2486ac391b3SStephan Aßmus {
2496ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::Close\n");
2506ac391b3SStephan Aßmus 
2514384acf6SStephan Aßmus 	if (fContext == NULL)
2524384acf6SStephan Aßmus 		return B_NO_INIT;
2534384acf6SStephan Aßmus 
2544384acf6SStephan Aßmus 	if (!fHeaderWritten)
2554384acf6SStephan Aßmus 		return B_NOT_ALLOWED;
2564384acf6SStephan Aßmus 
2574384acf6SStephan Aßmus 	int result = av_write_trailer(fContext);
2584384acf6SStephan Aßmus 	if (result < 0)
2594384acf6SStephan Aßmus 		TRACE("  av_write_trailer(): %d\n", result);
2604384acf6SStephan Aßmus 
2614384acf6SStephan Aßmus 	return result == 0 ? B_OK : B_ERROR;
2626ac391b3SStephan Aßmus }
2636ac391b3SStephan Aßmus 
2646ac391b3SStephan Aßmus 
2656ac391b3SStephan Aßmus status_t
2664384acf6SStephan Aßmus AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format)
2676ac391b3SStephan Aßmus {
2686ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AllocateCookie()\n");
2696ac391b3SStephan Aßmus 
2706ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
2716ac391b3SStephan Aßmus 
2726ac391b3SStephan Aßmus 	if (_cookie == NULL)
2736ac391b3SStephan Aßmus 		return B_BAD_VALUE;
2746ac391b3SStephan Aßmus 
2754384acf6SStephan Aßmus 	StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext,
2764384acf6SStephan Aßmus 		&fStreamLock);
2774384acf6SStephan Aßmus 
2784384acf6SStephan Aßmus 	return cookie->Init(format);
2796ac391b3SStephan Aßmus }
2806ac391b3SStephan Aßmus 
2816ac391b3SStephan Aßmus 
2826ac391b3SStephan Aßmus status_t
2836ac391b3SStephan Aßmus AVFormatWriter::FreeCookie(void* _cookie)
2846ac391b3SStephan Aßmus {
2856ac391b3SStephan Aßmus 	BAutolock _(fStreamLock);
2866ac391b3SStephan Aßmus 
2876ac391b3SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
2886ac391b3SStephan Aßmus 	delete cookie;
2896ac391b3SStephan Aßmus 
2906ac391b3SStephan Aßmus 	return B_OK;
2916ac391b3SStephan Aßmus }
2926ac391b3SStephan Aßmus 
2936ac391b3SStephan Aßmus 
2946ac391b3SStephan Aßmus // #pragma mark -
2956ac391b3SStephan Aßmus 
2966ac391b3SStephan Aßmus 
2976ac391b3SStephan Aßmus status_t
298fa770e4cSStephan Aßmus AVFormatWriter::SetCopyright(void* cookie, const char* copyright)
299fa770e4cSStephan Aßmus {
300fa770e4cSStephan Aßmus 	TRACE("AVFormatWriter::SetCopyright(%p, %s)\n", cookie, copyright);
301fa770e4cSStephan Aßmus 
302fa770e4cSStephan Aßmus 	return B_NOT_SUPPORTED;
303fa770e4cSStephan Aßmus }
304fa770e4cSStephan Aßmus 
305fa770e4cSStephan Aßmus 
306fa770e4cSStephan Aßmus status_t
3074384acf6SStephan Aßmus AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code,
3086ac391b3SStephan Aßmus 	const void* data, size_t size, uint32 flags)
3096ac391b3SStephan Aßmus {
3106ac391b3SStephan Aßmus 	TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n",
3116ac391b3SStephan Aßmus 		code, data, size, flags);
3126ac391b3SStephan Aßmus 
3134384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
3144384acf6SStephan Aßmus 	return cookie->AddTrackInfo(code, data, size, flags);
3156ac391b3SStephan Aßmus }
3166ac391b3SStephan Aßmus 
3176ac391b3SStephan Aßmus 
3186ac391b3SStephan Aßmus status_t
3194384acf6SStephan Aßmus AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer,
320fa770e4cSStephan Aßmus 	size_t chunkSize, media_encode_info* encodeInfo)
3216ac391b3SStephan Aßmus {
322fa770e4cSStephan Aßmus 	TRACE("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer, chunkSize,
323fa770e4cSStephan Aßmus 		encodeInfo);
3246ac391b3SStephan Aßmus 
3254384acf6SStephan Aßmus 	StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie);
3264384acf6SStephan Aßmus 	return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo);
3276ac391b3SStephan Aßmus }
3286ac391b3SStephan Aßmus 
3296ac391b3SStephan Aßmus 
3302e9d65abSStephan Aßmus // #pragma mark -
3312e9d65abSStephan Aßmus 
3322e9d65abSStephan Aßmus 
3332e9d65abSStephan Aßmus /*static*/ int
334*7a97958bSStephan Aßmus AVFormatWriter::_Write(void* cookie, uint8* buffer, int bufferSize)
3352e9d65abSStephan Aßmus {
3362e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Write(%p, %p, %d)\n",
3372e9d65abSStephan Aßmus 		cookie, buffer, bufferSize);
3382e9d65abSStephan Aßmus 
3392e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
3402e9d65abSStephan Aßmus 
3412e9d65abSStephan Aßmus 	ssize_t written = writer->fTarget->Write(buffer, bufferSize);
3422e9d65abSStephan Aßmus 
3432e9d65abSStephan Aßmus 	TRACE_IO("  written: %ld\n", written);
3442e9d65abSStephan Aßmus 	return (int)written;
3452e9d65abSStephan Aßmus 
3462e9d65abSStephan Aßmus }
3472e9d65abSStephan Aßmus 
3482e9d65abSStephan Aßmus 
3492e9d65abSStephan Aßmus /*static*/ off_t
3502e9d65abSStephan Aßmus AVFormatWriter::_Seek(void* cookie, off_t offset, int whence)
3512e9d65abSStephan Aßmus {
3522e9d65abSStephan Aßmus 	TRACE_IO("AVFormatWriter::_Seek(%p, %lld, %d)\n",
3532e9d65abSStephan Aßmus 		cookie, offset, whence);
3542e9d65abSStephan Aßmus 
3552e9d65abSStephan Aßmus 	AVFormatWriter* writer = reinterpret_cast<AVFormatWriter*>(cookie);
3562e9d65abSStephan Aßmus 
3572e9d65abSStephan Aßmus 	BPositionIO* positionIO = dynamic_cast<BPositionIO*>(writer->fTarget);
3582e9d65abSStephan Aßmus 	if (positionIO == NULL)
3592e9d65abSStephan Aßmus 		return -1;
3602e9d65abSStephan Aßmus 
3614384acf6SStephan Aßmus 	// Support for special file size retrieval API without seeking anywhere:
3622e9d65abSStephan Aßmus 	if (whence == AVSEEK_SIZE) {
3632e9d65abSStephan Aßmus 		off_t size;
3642e9d65abSStephan Aßmus 		if (positionIO->GetSize(&size) == B_OK)
3652e9d65abSStephan Aßmus 			return size;
3662e9d65abSStephan Aßmus 		return -1;
3672e9d65abSStephan Aßmus 	}
3682e9d65abSStephan Aßmus 
3692e9d65abSStephan Aßmus 	off_t position = positionIO->Seek(offset, whence);
3702e9d65abSStephan Aßmus 	TRACE_IO("  position: %lld\n", position);
3712e9d65abSStephan Aßmus 	if (position < 0)
3722e9d65abSStephan Aßmus 		return -1;
3732e9d65abSStephan Aßmus 
3742e9d65abSStephan Aßmus 	return position;
3752e9d65abSStephan Aßmus }
3762e9d65abSStephan Aßmus 
3772e9d65abSStephan Aßmus 
378