diff --git a/headers/private/media/MediaWriter.h b/headers/private/media/MediaWriter.h index 31be2c319d..04788466f5 100644 --- a/headers/private/media/MediaWriter.h +++ b/headers/private/media/MediaWriter.h @@ -27,6 +27,7 @@ public: status_t CreateEncoder(Encoder** _encoder, const media_codec_info* codecInfo, + const media_format* format, uint32 flags = 0); status_t SetCopyright(int32 streamIndex, diff --git a/headers/private/media/WriterPlugin.h b/headers/private/media/WriterPlugin.h index 07ad8ac283..dafca5f7cc 100644 --- a/headers/private/media/WriterPlugin.h +++ b/headers/private/media/WriterPlugin.h @@ -13,12 +13,15 @@ public: Writer(); virtual ~Writer(); + virtual status_t Init(const media_file_format* fileFormat) = 0; + virtual status_t SetCopyright(const char* copyright) = 0; virtual status_t CommitHeader() = 0; virtual status_t Flush() = 0; virtual status_t Close() = 0; - virtual status_t AllocateCookie(void** cookie) = 0; + virtual status_t AllocateCookie(void** cookie, + const media_format* format) = 0; virtual status_t FreeCookie(void* cookie) = 0; virtual status_t SetCopyright(void* cookie, diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp index e57ee2e09b..f14d103976 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp @@ -40,18 +40,36 @@ extern "C" { #define ERROR(a...) fprintf(stderr, a) +static const size_t kIOBufferSize = 64 * 1024; + // TODO: This could depend on the BMediaFile creation flags, IIRC, + // they allow to specify a buffering mode. + + +// #pragma mark - URLProtocol +// TODO: Do we need to write an URLProtocol? + + // #pragma mark - AVFormatWriter::StreamCookie class AVFormatWriter::StreamCookie { public: - StreamCookie(BPositionIO* target, + StreamCookie(AVFormatContext* context, BLocker* streamLock); virtual ~StreamCookie(); + status_t Init(const media_format* format); + + status_t WriteChunk(const void* chunkBuffer, + size_t chunkSize, + media_encode_info* encodeInfo); + + status_t AddTrackInfo(uint32 code, const void* data, + size_t size, uint32 flags); + private: - BPositionIO* fTarget; - off_t fPosition; + AVFormatContext* fContext; + AVStream* fStream; // Since different threads may read from the source, // we need to protect the file position and I/O by a lock. BLocker* fStreamLock; @@ -59,11 +77,11 @@ private: -AVFormatWriter::StreamCookie::StreamCookie(BPositionIO* target, +AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context, BLocker* streamLock) : - fTarget(target), - fPosition(0), + fContext(context), + fStream(NULL), fStreamLock(streamLock) { } @@ -74,12 +92,60 @@ AVFormatWriter::StreamCookie::~StreamCookie() } +status_t +AVFormatWriter::StreamCookie::Init(const media_format* format) +{ + TRACE("AVFormatWriter::StreamCookie::Init()\n"); + + BAutolock _(fStreamLock); + + fStream = av_new_stream(fContext, fContext->nb_streams); + + if (fStream == NULL) { + TRACE(" failed to add new stream\n"); + return B_ERROR; + } + + // TODO: Setup the stream according to the media format... + + return B_OK; +} + + +status_t +AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer, + size_t chunkSize, media_encode_info* encodeInfo) +{ + TRACE("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld)\n", + chunkBuffer, chunkSize); + + BAutolock _(fStreamLock); + + return B_ERROR; +} + + +status_t +AVFormatWriter::StreamCookie::AddTrackInfo(uint32 code, + const void* data, size_t size, uint32 flags) +{ + TRACE("AVFormatWriter::StreamCookie::AddTrackInfo(%lu, %p, %ld, %lu)\n", + code, data, size, flags); + + BAutolock _(fStreamLock); + + return B_NOT_SUPPORTED; +} + + // #pragma mark - AVFormatWriter AVFormatWriter::AVFormatWriter() : - fStreams(NULL), + fContext(avformat_alloc_context()), + fHeaderWritten(false), + fIOBuffer(NULL), fStreamLock("stream lock") { TRACE("AVFormatWriter::AVFormatWriter\n"); @@ -89,16 +155,55 @@ AVFormatWriter::AVFormatWriter() AVFormatWriter::~AVFormatWriter() { TRACE("AVFormatWriter::~AVFormatWriter\n"); - if (fStreams != NULL) { - delete fStreams[0]; - delete[] fStreams; - } + + av_free(fContext); + + delete[] fIOBuffer; } // #pragma mark - +status_t +AVFormatWriter::Init(const media_file_format* fileFormat) +{ + TRACE("AVFormatWriter::Init()\n"); + + delete[] fIOBuffer; + fIOBuffer = new(std::nothrow) uint8[kIOBufferSize]; + if (fIOBuffer == NULL) + return B_NO_MEMORY; + + // Init I/O context with buffer and hook functions, pass ourself as + // cookie. + if (init_put_byte(&fIOContext, fIOBuffer, kIOBufferSize, 0, this, + 0, _Write, _Seek) != 0) { + TRACE(" init_put_byte() failed!\n"); + return B_ERROR; + } + + // TODO: Is this how it works? + // TODO: Is the cookie stored in ByteIOContext? Or does it need to be + // stored in fContext->priv_data? + fContext->pb = &fIOContext; + + // TODO: Set the AVOutputFormat according to fileFormat... + fContext->oformat = guess_format(fileFormat->short_name, + fileFormat->file_extension, fileFormat->mime_type); + if (fContext->oformat == NULL) { + TRACE(" failed to find AVOuputFormat for %s\n", + fileFormat->short_name); + return B_NOT_SUPPORTED; + } + + TRACE(" found AVOuputFormat for %s: %s\n", fileFormat->short_name, + fContext->oformat->name); + + return B_OK; +} + + status_t AVFormatWriter::SetCopyright(const char* copyright) { @@ -113,7 +218,19 @@ AVFormatWriter::CommitHeader() { TRACE("AVFormatWriter::CommitHeader\n"); - return B_NOT_SUPPORTED; + if (fContext == NULL) + return B_NO_INIT; + + if (fHeaderWritten) + return B_NOT_ALLOWED; + + int result = av_write_header(fContext); + if (result < 0) + TRACE(" av_write_header(): %d\n", result); + else + fHeaderWritten = true; + + return result == 0 ? B_OK : B_ERROR; } @@ -131,24 +248,34 @@ AVFormatWriter::Close() { TRACE("AVFormatWriter::Close\n"); - return B_NOT_SUPPORTED; + if (fContext == NULL) + return B_NO_INIT; + + if (!fHeaderWritten) + return B_NOT_ALLOWED; + + int result = av_write_trailer(fContext); + if (result < 0) + TRACE(" av_write_trailer(): %d\n", result); + + return result == 0 ? B_OK : B_ERROR; } status_t -AVFormatWriter::AllocateCookie(void** _cookie) +AVFormatWriter::AllocateCookie(void** _cookie, const media_format* format) { TRACE("AVFormatWriter::AllocateCookie()\n"); BAutolock _(fStreamLock); - if (fStreams == NULL) - return B_NO_INIT; - if (_cookie == NULL) return B_BAD_VALUE; - return B_NOT_SUPPORTED; + StreamCookie* cookie = new(std::nothrow) StreamCookie(fContext, + &fStreamLock); + + return cookie->Init(format); } @@ -158,12 +285,7 @@ AVFormatWriter::FreeCookie(void* _cookie) BAutolock _(fStreamLock); StreamCookie* cookie = reinterpret_cast(_cookie); - - if (cookie != NULL) { -// if (fStreams != NULL) -// fStreams[cookie->VirtualIndex()] = NULL; - delete cookie; - } + delete cookie; return B_OK; } @@ -182,24 +304,26 @@ AVFormatWriter::SetCopyright(void* cookie, const char* copyright) status_t -AVFormatWriter::AddTrackInfo(void* cookie, uint32 code, +AVFormatWriter::AddTrackInfo(void* _cookie, uint32 code, const void* data, size_t size, uint32 flags) { TRACE("AVFormatWriter::AddTrackInfo(%lu, %p, %ld, %lu)\n", code, data, size, flags); - return B_NOT_SUPPORTED; + StreamCookie* cookie = reinterpret_cast(_cookie); + return cookie->AddTrackInfo(code, data, size, flags); } status_t -AVFormatWriter::WriteChunk(void* cookie, const void* chunkBuffer, +AVFormatWriter::WriteChunk(void* _cookie, const void* chunkBuffer, size_t chunkSize, media_encode_info* encodeInfo) { TRACE("AVFormatWriter::WriteChunk(%p, %ld, %p)\n", chunkBuffer, chunkSize, encodeInfo); - return B_NOT_SUPPORTED; + StreamCookie* cookie = reinterpret_cast(_cookie); + return cookie->WriteChunk(chunkBuffer, chunkSize, encodeInfo); } @@ -234,8 +358,7 @@ AVFormatWriter::_Seek(void* cookie, off_t offset, int whence) if (positionIO == NULL) return -1; - // Support for special file size retrieval API without seeking - // anywhere: + // Support for special file size retrieval API without seeking anywhere: if (whence == AVSEEK_SIZE) { off_t size; if (positionIO->GetSize(&size) == B_OK) diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.h b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.h index d54140f1fb..d3d57b0cb4 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.h +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.h @@ -20,12 +20,15 @@ public: AVFormatWriter(); ~AVFormatWriter(); + virtual status_t Init(const media_file_format* fileFormat); + virtual status_t SetCopyright(const char* copyright); virtual status_t CommitHeader(); virtual status_t Flush(); virtual status_t Close(); - virtual status_t AllocateCookie(void** cookie); + virtual status_t AllocateCookie(void** cookie, + const media_format* format); virtual status_t FreeCookie(void* cookie); virtual status_t SetCopyright(void* cookie, @@ -49,6 +52,10 @@ private: class StreamCookie; AVFormatContext* fContext; + bool fHeaderWritten; + + ByteIOContext fIOContext; + uint8* fIOBuffer; StreamCookie** fStreams; BLocker fStreamLock; diff --git a/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp b/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp index e3446f8546..97bae99262 100644 --- a/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp +++ b/src/add-ons/media/plugins/ffmpeg/MuxerTable.cpp @@ -20,7 +20,7 @@ const media_file_format gMuxerTable[] = { { 0 }, "video/x-msvideo", "AVI (Audio Video Interleaved)", - "AVI", + "avi", "avi", { 0 } }, diff --git a/src/kits/media/MediaTrack.cpp b/src/kits/media/MediaTrack.cpp index 6171ab7c15..637758f6a8 100644 --- a/src/kits/media/MediaTrack.cpp +++ b/src/kits/media/MediaTrack.cpp @@ -732,7 +732,7 @@ BMediaTrack::BMediaTrack(BPrivate::media::MediaWriter* writer, SetupWorkaround(); if (codecInfo != NULL) { - status_t ret = fWriter->CreateEncoder(&fEncoder, codecInfo); + status_t ret = fWriter->CreateEncoder(&fEncoder, codecInfo, format); if (ret != B_OK) { TRACE("BMediaTrack::BMediaTrack: Error: creating decoder failed: " "%s\n", strerror(ret)); diff --git a/src/kits/media/MediaWriter.cpp b/src/kits/media/MediaWriter.cpp index 32d83fbc02..3f5bcb16e7 100644 --- a/src/kits/media/MediaWriter.cpp +++ b/src/kits/media/MediaWriter.cpp @@ -78,7 +78,7 @@ MediaWriter::InitCheck() { CALLED(); - return fWriter != NULL ? B_OK : B_NO_INIT; + return fWriter != NULL ? fWriter->Init(&fFileFormat) : B_NO_INIT; } @@ -94,7 +94,8 @@ MediaWriter::GetFileFormatInfo(media_file_format* _fileFormat) const status_t MediaWriter::CreateEncoder(Encoder** _encoder, - const media_codec_info* codecInfo, uint32 flags) + const media_codec_info* codecInfo, const media_format* format, + uint32 flags) { CALLED(); @@ -109,7 +110,7 @@ MediaWriter::CreateEncoder(Encoder** _encoder, } StreamInfo info; - ret = fWriter->AllocateCookie(&info.cookie); + ret = fWriter->AllocateCookie(&info.cookie, format); if (ret != B_OK) { _plugin_manager.DestroyEncoder(encoder); return ret;