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