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 43*4384acf6SStephan Aßmus static const size_t kIOBufferSize = 64 * 1024; 44*4384acf6SStephan Aßmus // TODO: This could depend on the BMediaFile creation flags, IIRC, 45*4384acf6SStephan Aßmus // they allow to specify a buffering mode. 46*4384acf6SStephan Aßmus 47*4384acf6SStephan Aßmus 48*4384acf6SStephan Aßmus // #pragma mark - URLProtocol 49*4384acf6SStephan Aßmus // TODO: Do we need to write an URLProtocol? 50*4384acf6SStephan Aßmus 51*4384acf6SStephan 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: 57*4384acf6SStephan Aßmus StreamCookie(AVFormatContext* context, 586ac391b3SStephan Aßmus BLocker* streamLock); 596ac391b3SStephan Aßmus virtual ~StreamCookie(); 606ac391b3SStephan Aßmus 61*4384acf6SStephan Aßmus status_t Init(const media_format* format); 62*4384acf6SStephan Aßmus 63*4384acf6SStephan Aßmus status_t WriteChunk(const void* chunkBuffer, 64*4384acf6SStephan Aßmus size_t chunkSize, 65*4384acf6SStephan Aßmus media_encode_info* encodeInfo); 66*4384acf6SStephan Aßmus 67*4384acf6SStephan Aßmus status_t AddTrackInfo(uint32 code, const void* data, 68*4384acf6SStephan Aßmus size_t size, uint32 flags); 69*4384acf6SStephan Aßmus 706ac391b3SStephan Aßmus private: 71*4384acf6SStephan Aßmus AVFormatContext* fContext; 72*4384acf6SStephan 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 80*4384acf6SStephan Aßmus AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context, 816ac391b3SStephan Aßmus BLocker* streamLock) 826ac391b3SStephan Aßmus : 83*4384acf6SStephan Aßmus fContext(context), 84*4384acf6SStephan 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 95*4384acf6SStephan Aßmus status_t 96*4384acf6SStephan Aßmus AVFormatWriter::StreamCookie::Init(const media_format* format) 97*4384acf6SStephan Aßmus { 98*4384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::Init()\n"); 99*4384acf6SStephan Aßmus 100*4384acf6SStephan Aßmus BAutolock _(fStreamLock); 101*4384acf6SStephan Aßmus 102*4384acf6SStephan Aßmus fStream = av_new_stream(fContext, fContext->nb_streams); 103*4384acf6SStephan Aßmus 104*4384acf6SStephan Aßmus if (fStream == NULL) { 105*4384acf6SStephan Aßmus TRACE(" failed to add new stream\n"); 106*4384acf6SStephan Aßmus return B_ERROR; 107*4384acf6SStephan Aßmus } 108*4384acf6SStephan Aßmus 109*4384acf6SStephan Aßmus // TODO: Setup the stream according to the media format... 110*4384acf6SStephan Aßmus 111*4384acf6SStephan Aßmus return B_OK; 112*4384acf6SStephan Aßmus } 113*4384acf6SStephan Aßmus 114*4384acf6SStephan Aßmus 115*4384acf6SStephan Aßmus status_t 116*4384acf6SStephan Aßmus AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer, 117*4384acf6SStephan Aßmus size_t chunkSize, media_encode_info* encodeInfo) 118*4384acf6SStephan Aßmus { 119*4384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld)\n", 120*4384acf6SStephan Aßmus chunkBuffer, chunkSize); 121*4384acf6SStephan Aßmus 122*4384acf6SStephan Aßmus BAutolock _(fStreamLock); 123*4384acf6SStephan Aßmus 124*4384acf6SStephan Aßmus return B_ERROR; 125*4384acf6SStephan Aßmus } 126*4384acf6SStephan Aßmus 127*4384acf6SStephan Aßmus 128*4384acf6SStephan Aßmus status_t 129*4384acf6SStephan Aßmus AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code, 130*4384acf6SStephan Aßmus const void* data, size_t size, uint32 flags) 131*4384acf6SStephan Aßmus { 132*4384acf6SStephan Aßmus TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n", 133*4384acf6SStephan Aßmus code, data, size, flags); 134*4384acf6SStephan Aßmus 135*4384acf6SStephan Aßmus BAutolock _(fStreamLock); 136*4384acf6SStephan Aßmus 137*4384acf6SStephan Aßmus return B_NOT_SUPPORTED; 138*4384acf6SStephan Aßmus } 139*4384acf6SStephan Aßmus 140*4384acf6SStephan Aßmus 1416ac391b3SStephan Aßmus // #pragma mark - AVFormatWriter 1426ac391b3SStephan Aßmus 1436ac391b3SStephan Aßmus 1446ac391b3SStephan Aßmus AVFormatWriter::AVFormatWriter() 1456ac391b3SStephan Aßmus : 146*4384acf6SStephan Aßmus fContext(avformat_alloc_context()), 147*4384acf6SStephan Aßmus fHeaderWritten(false), 148*4384acf6SStephan 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"); 158*4384acf6SStephan Aßmus 159*4384acf6SStephan Aßmus av_free(fContext); 160*4384acf6SStephan Aßmus 161*4384acf6SStephan 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 169*4384acf6SStephan Aßmus AVFormatWriter::Init(const media_file_format* fileFormat) 170*4384acf6SStephan Aßmus { 171*4384acf6SStephan Aßmus TRACE("AVFormatWriter::Init()\n"); 172*4384acf6SStephan Aßmus 173*4384acf6SStephan Aßmus delete[] fIOBuffer; 174*4384acf6SStephan Aßmus fIOBuffer = new(std::nothrow) uint8[kIOBufferSize]; 175*4384acf6SStephan Aßmus if (fIOBuffer == NULL) 176*4384acf6SStephan Aßmus return B_NO_MEMORY; 177*4384acf6SStephan Aßmus 178*4384acf6SStephan Aßmus // Init I/O context with buffer and hook functions, pass ourself as 179*4384acf6SStephan Aßmus // cookie. 180*4384acf6SStephan Aßmus if (init_put_byte(&fIOContext, fIOBuffer, kIOBufferSize, 0, this, 181*4384acf6SStephan Aßmus 0, _Write, _Seek) != 0) { 182*4384acf6SStephan Aßmus TRACE(" init_put_byte() failed!\n"); 183*4384acf6SStephan Aßmus return B_ERROR; 184*4384acf6SStephan Aßmus } 185*4384acf6SStephan Aßmus 186*4384acf6SStephan Aßmus // TODO: Is this how it works? 187*4384acf6SStephan Aßmus // TODO: Is the cookie stored in ByteIOContext? Or does it need to be 188*4384acf6SStephan Aßmus // stored in fContext->priv_data? 189*4384acf6SStephan Aßmus fContext->pb = &fIOContext; 190*4384acf6SStephan Aßmus 191*4384acf6SStephan Aßmus // TODO: Set the AVOutputFormat according to fileFormat... 192*4384acf6SStephan Aßmus fContext->oformat = guess_format(fileFormat->short_name, 193*4384acf6SStephan Aßmus fileFormat->file_extension, fileFormat->mime_type); 194*4384acf6SStephan Aßmus if (fContext->oformat == NULL) { 195*4384acf6SStephan Aßmus TRACE(" failed to find AVOuputFormat for %s\n", 196*4384acf6SStephan Aßmus fileFormat->short_name); 197*4384acf6SStephan Aßmus return B_NOT_SUPPORTED; 198*4384acf6SStephan Aßmus } 199*4384acf6SStephan Aßmus 200*4384acf6SStephan Aßmus TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name, 201*4384acf6SStephan Aßmus fContext->oformat->name); 202*4384acf6SStephan Aßmus 203*4384acf6SStephan Aßmus return B_OK; 204*4384acf6SStephan Aßmus } 205*4384acf6SStephan Aßmus 206*4384acf6SStephan Aßmus 207*4384acf6SStephan 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 221*4384acf6SStephan Aßmus if (fContext == NULL) 222*4384acf6SStephan Aßmus return B_NO_INIT; 223*4384acf6SStephan Aßmus 224*4384acf6SStephan Aßmus if (fHeaderWritten) 225*4384acf6SStephan Aßmus return B_NOT_ALLOWED; 226*4384acf6SStephan Aßmus 227*4384acf6SStephan Aßmus int result = av_write_header(fContext); 228*4384acf6SStephan Aßmus if (result < 0) 229*4384acf6SStephan Aßmus TRACE(" av_write_header(): %d\n", result); 230*4384acf6SStephan Aßmus else 231*4384acf6SStephan Aßmus fHeaderWritten = true; 232*4384acf6SStephan Aßmus 233*4384acf6SStephan 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 251*4384acf6SStephan Aßmus if (fContext == NULL) 252*4384acf6SStephan Aßmus return B_NO_INIT; 253*4384acf6SStephan Aßmus 254*4384acf6SStephan Aßmus if (!fHeaderWritten) 255*4384acf6SStephan Aßmus return B_NOT_ALLOWED; 256*4384acf6SStephan Aßmus 257*4384acf6SStephan Aßmus int result = av_write_trailer(fContext); 258*4384acf6SStephan Aßmus if (result < 0) 259*4384acf6SStephan Aßmus TRACE(" av_write_trailer(): %d\n", result); 260*4384acf6SStephan Aßmus 261*4384acf6SStephan Aßmus return result == 0 ? B_OK : B_ERROR; 2626ac391b3SStephan Aßmus } 2636ac391b3SStephan Aßmus 2646ac391b3SStephan Aßmus 2656ac391b3SStephan Aßmus status_t 266*4384acf6SStephan 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 275*4384acf6SStephan Aßmus StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext, 276*4384acf6SStephan Aßmus &fStreamLock); 277*4384acf6SStephan Aßmus 278*4384acf6SStephan 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 307*4384acf6SStephan 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 313*4384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 314*4384acf6SStephan Aßmus return cookie->AddTrackInfo(code, data, size, flags); 3156ac391b3SStephan Aßmus } 3166ac391b3SStephan Aßmus 3176ac391b3SStephan Aßmus 3186ac391b3SStephan Aßmus status_t 319*4384acf6SStephan 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 325*4384acf6SStephan Aßmus StreamCookie* cookie = reinterpret_cast<StreamCookie*>(_cookie); 326*4384acf6SStephan 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 3342e9d65abSStephan Aßmus AVFormatWriter::_Write(void* cookie, const 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 361*4384acf6SStephan 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