From 2b514992952e3bd29424ceeaac7a1babaf433b92 Mon Sep 17 00:00:00 2001 From: Dario Casalinuovo Date: Wed, 2 Dec 2015 15:24:17 +0100 Subject: [PATCH] Initial implementation of BMediaEncoder * People interested, please review! * This is based on the patch from Fredrik Moden which was based on the Oleg Krysenkov one. * The original patch has been reworked by myself. * Adapted the code to work with the new PluginManager API which differently than before doesn't need to contact the media_server. --- headers/os/media/MediaEncoder.h | 15 +- headers/private/media/EncoderPlugin.h | 5 +- headers/private/media/PluginManager.h | 10 +- .../media/plugins/ffmpeg/AVCodecEncoder.cpp | 32 ++- .../media/plugins/ffmpeg/AVCodecEncoder.h | 3 + .../media/plugins/ffmpeg/FFmpegPlugin.cpp | 7 + .../media/plugins/ffmpeg/FFmpegPlugin.h | 3 + src/kits/media/MediaEncoder.cpp | 243 ++++++++++++++---- src/kits/media/PluginManager.cpp | 82 +++++- 9 files changed, 339 insertions(+), 61 deletions(-) diff --git a/headers/os/media/MediaEncoder.h b/headers/os/media/MediaEncoder.h index 834bc738ea..047ec688b8 100644 --- a/headers/os/media/MediaEncoder.h +++ b/headers/os/media/MediaEncoder.h @@ -8,6 +8,15 @@ #include +namespace BPrivate { + namespace media { + class Encoder; + class EncoderPlugin; + } +} + +using namespace BPrivate::media; + class BMediaEncoder { public: @@ -65,6 +74,8 @@ private: BMediaEncoder& operator=(const BMediaEncoder& other); private: + status_t _AttachToEncoder(); + static status_t write_chunk(void* classPtr, const void* buffer, size_t size, media_encode_info* info); @@ -73,7 +84,7 @@ private: void ReleaseEncoder(); uint32 _reserved_was_fEncoderMgr; - uint32 _reserved_was_fEncoder; + Encoder* fEncoder; int32 fEncoderID; bool fFormatValid; @@ -84,7 +95,7 @@ private: }; -class BMediaBufferEncoder: public BMediaEncoder { +class BMediaBufferEncoder : public BMediaEncoder { public: BMediaBufferEncoder(); BMediaBufferEncoder( diff --git a/headers/private/media/EncoderPlugin.h b/headers/private/media/EncoderPlugin.h index dabcdf1c1d..8aa655a33d 100644 --- a/headers/private/media/EncoderPlugin.h +++ b/headers/private/media/EncoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright 2009, Haiku Inc. All rights reserved. + * Copyright 2009-2015, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT license. */ #ifndef _ENCODER_PLUGIN_H @@ -122,6 +122,9 @@ public: virtual Encoder* NewEncoder( const media_codec_info& codecInfo) = 0; + virtual Encoder* NewEncoder( + const media_format& format) = 0; + virtual status_t RegisterNextEncoder(int32* cookie, media_codec_info* codecInfo, media_format_family* formatFamily, diff --git a/headers/private/media/PluginManager.h b/headers/private/media/PluginManager.h index d74cba326b..edd92217e9 100644 --- a/headers/private/media/PluginManager.h +++ b/headers/private/media/PluginManager.h @@ -1,6 +1,8 @@ -/* +/* + * Copyright 2015, Dario Casalinuovo. All rights reserved. + * Copyright 2012, Fredrik Modéen. All rights reserved. * Copyright 2004-2007, Marcus Overhagen. All rights reserved. - * Distributed under the terms of the OpenBeOS License. + * Distributed under the terms of the MIT License. */ #ifndef _PLUGIN_MANAGER_H #define _PLUGIN_MANAGER_H @@ -51,6 +53,10 @@ public: status_t CreateEncoder(Encoder** encoder, const media_codec_info* codecInfo, uint32 flags); + + status_t CreateEncoder(Encoder** encoder, + const media_format& format); + void DestroyEncoder(Encoder* encoder); private: diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp index b30de87542..3f313d6d07 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp @@ -45,16 +45,38 @@ AVCodecEncoder::AVCodecEncoder(uint32 codecID, int bitRateScale) fOwnContext(avcodec_alloc_context3(NULL)), fContext(fOwnContext), fCodecInitStatus(CODEC_INIT_NEEDED), - fFrame(avcodec_alloc_frame()), fSwsContext(NULL), - - fFramesWritten(0), - - fChunkBuffer(new(std::nothrow) uint8[kDefaultChunkBufferSize]) + fFramesWritten(0) { TRACE("AVCodecEncoder::AVCodecEncoder()\n"); + _Init(); +} + +AVCodecEncoder::AVCodecEncoder(const media_format& format) + : + Encoder(), + fBitRateScale(1), // TODO: is it OK? + fCodecID(CodecID(-1)), + fCodec(NULL), + fOwnContext(avcodec_alloc_context3(NULL)), + fContext(fOwnContext), + fCodecInitStatus(CODEC_INIT_NEEDED), + fFrame(avcodec_alloc_frame()), + fSwsContext(NULL), + fFramesWritten(0) +{ + TRACE("AVCodecEncoder::AVCodecEncoder()\n"); + _Init(); + SetUp(&format); +} + + +void +AVCodecEncoder::_Init() +{ + fChunkBuffer = new(std::nothrow) uint8[kDefaultChunkBufferSize]; if (fCodecID > 0) { fCodec = avcodec_find_encoder(fCodecID); TRACE(" found AVCodec for %u: %p\n", fCodecID, fCodec); diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h index bf23ecb19a..8205cd6506 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h @@ -27,6 +27,8 @@ public: AVCodecEncoder(uint32 codecID, int bitRateScale); + AVCodecEncoder(const media_format& format); + virtual ~AVCodecEncoder(); virtual status_t AcceptedFormat( @@ -48,6 +50,7 @@ public: // codec buffer size. private: + void _Init(); status_t _Setup(); bool _OpenCodecIfNeeded(); diff --git a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp index e3ec9873b0..e307f8154e 100644 --- a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp +++ b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp @@ -156,6 +156,13 @@ FFmpegPlugin::NewEncoder(const media_codec_info& codecInfo) } +Encoder* +FFmpegPlugin::NewEncoder(const media_format& format) +{ + return new(std::nothrow)AVCodecEncoder(format); +} + + status_t FFmpegPlugin::RegisterNextEncoder(int32* cookie, media_codec_info* _codecInfo, media_format_family* _formatFamily, media_format* _inputFormat, diff --git a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.h b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.h index ef37a5fd75..3013472319 100644 --- a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.h +++ b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.h @@ -34,6 +34,9 @@ public: virtual Encoder* NewEncoder( const media_codec_info& codecInfo); + + virtual Encoder* NewEncoder(const media_format& format); + virtual status_t RegisterNextEncoder(int32* cookie, media_codec_info* codecInfo, media_format_family* formatFamily, diff --git a/src/kits/media/MediaEncoder.cpp b/src/kits/media/MediaEncoder.cpp index 60849205ed..5f1484db03 100644 --- a/src/kits/media/MediaEncoder.cpp +++ b/src/kits/media/MediaEncoder.cpp @@ -1,96 +1,185 @@ -/*********************************************************************** - * AUTHOR: Marcus Overhagen - * FILE: MediaEncoder.cpp - * DESCR: - ***********************************************************************/ +/* + * Copyright 2015, Dario Casalinuovo + * Copyright 2010, Oleg Krysenkov, beos344@mail.ru. + * Copyright 2012, Fredrik Modéen, [firstname]@[lastname].se. + * Copyright 2004-2007, Marcus Overhagen. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + #include + +#include +#include + +#include + #include "debug.h" +//TODO: SetTo, SetFormat, Enode Class /************************************************************* * public BMediaEncoder *************************************************************/ BMediaEncoder::BMediaEncoder() + : + fEncoder(NULL), + fInitStatus(B_NO_INIT) { - UNIMPLEMENTED(); + CALLED(); } BMediaEncoder::BMediaEncoder(const media_format *output_format) + : + fEncoder(NULL), + fInitStatus(B_NO_INIT) { - UNIMPLEMENTED(); + CALLED(); + SetTo(output_format); } BMediaEncoder::BMediaEncoder(const media_codec_info *mci) + : + fEncoder(NULL), + fInitStatus(B_NO_INIT) { - UNIMPLEMENTED(); + CALLED(); + SetTo(mci); } /* virtual */ BMediaEncoder::~BMediaEncoder() { - UNIMPLEMENTED(); + CALLED(); + gPluginManager.DestroyEncoder(fEncoder); + fEncoder = NULL; } status_t BMediaEncoder::InitCheck() const { - UNIMPLEMENTED(); - - return B_OK; + return fInitStatus; } + status_t BMediaEncoder::SetTo(const media_format *output_format) { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + + gPluginManager.DestroyEncoder(fEncoder); + fEncoder = NULL; + fInitStatus = B_OK; + status_t err = B_ERROR; + if (output_format != NULL) { + media_format format = *output_format; + + status_t err = gPluginManager.CreateEncoder(&fEncoder, format); + if (err != B_OK) + goto fail; + + err = _AttachToEncoder(); + if (err != B_OK) + goto fail; + + err = SetFormat(NULL, &format); + if (err != B_OK) + goto fail; + + return B_OK; + } + +fail: + fInitStatus = err; + gPluginManager.DestroyEncoder(fEncoder); + fEncoder = NULL; + fInitStatus = B_NO_INIT; + return err; } + status_t BMediaEncoder::SetTo(const media_codec_info *mci) { - UNIMPLEMENTED(); + CALLED(); + + gPluginManager.DestroyEncoder(fEncoder); + fEncoder = NULL; + + status_t err = gPluginManager.CreateEncoder(&fEncoder, mci, 0); + if (err < B_OK) + goto fail; + + err = _AttachToEncoder(); + if (err < B_OK) + goto fail; + + fInitStatus = B_OK; return B_OK; + +fail: + gPluginManager.DestroyEncoder(fEncoder); + fEncoder = NULL; + fInitStatus = B_NO_INIT; + return err; } status_t BMediaEncoder::SetFormat(media_format *input_format, - media_format *output_format, - media_file_format *mfi) + media_format *output_format, media_file_format *mfi) { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + TRACE("BMediaEncoder::SetFormat. Input = %d, Output = %d\n", + input_format->type, output_format->type); + + if (!fEncoder) + return B_NO_INIT; + + //TODO: How we support output_format and mfi? + return fEncoder->SetUp(input_format); } + status_t BMediaEncoder::Encode(const void *buffer, - int64 frame_count, - media_encode_info *info) + int64 frame_count, media_encode_info *info) { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + + if (!fEncoder) + return B_NO_INIT; + + return fEncoder->Encode(buffer, frame_count, info); } status_t BMediaEncoder::GetEncodeParameters(encode_parameters *parameters) const { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + + if (fEncoder == NULL) + return B_NO_INIT; + else + return fEncoder->GetEncodeParameters(parameters); } status_t BMediaEncoder::SetEncodeParameters(encode_parameters *parameters) { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + + if (fEncoder == NULL) + return B_NO_INIT; + else + return fEncoder->SetEncodeParameters(parameters); } @@ -101,8 +190,12 @@ BMediaEncoder::SetEncodeParameters(encode_parameters *parameters) /* virtual */ status_t BMediaEncoder::AddTrackInfo(uint32 code, const char *data, size_t size) { - UNIMPLEMENTED(); - return B_OK; + CALLED(); + + if (fEncoder == NULL) + return B_NO_INIT; + else + return fEncoder->AddTrackInfo(code, data, size); } @@ -117,10 +210,8 @@ BMediaEncoder::BMediaEncoder & operator=(const BMediaEncoder &); */ /* static */ status_t -BMediaEncoder::write_chunk(void *classptr, - const void *chunk_data, - size_t chunk_len, - media_encode_info *info) +BMediaEncoder::write_chunk(void* classptr, const void* chunk_data, + size_t chunk_len, media_encode_info* info) { UNIMPLEMENTED(); return B_OK; @@ -140,6 +231,33 @@ BMediaEncoder::ReleaseEncoder() UNIMPLEMENTED(); } + +status_t +BMediaEncoder::_AttachToEncoder() +{ + class MediaEncoderChunkWriter : public ChunkWriter { + public: + MediaEncoderChunkWriter(BMediaEncoder* encoder) + { + fEncoder = encoder; + } + virtual status_t WriteChunk(const void* chunkBuffer, + size_t chunkSize, media_encode_info* encodeInfo) + { + return fEncoder->WriteChunk(chunkBuffer, chunkSize, encodeInfo); + } + private: + BMediaEncoder* fEncoder; + } *writer = new(std::nothrow) MediaEncoderChunkWriter(this); + + if (!writer) + return B_NO_MEMORY; + + fEncoder->SetChunkWriter(writer); + return B_OK; +} + + status_t BMediaEncoder::_Reserved_BMediaEncoder_0(int32 arg, ...) { return B_ERROR; } status_t BMediaEncoder::_Reserved_BMediaEncoder_1(int32 arg, ...) { return B_ERROR; } status_t BMediaEncoder::_Reserved_BMediaEncoder_2(int32 arg, ...) { return B_ERROR; } @@ -162,33 +280,50 @@ status_t BMediaEncoder::_Reserved_BMediaEncoder_15(int32 arg, ...) { return B_ER *************************************************************/ BMediaBufferEncoder::BMediaBufferEncoder() + : + BMediaEncoder(), + fBuffer(NULL) { - UNIMPLEMENTED(); + CALLED(); } BMediaBufferEncoder::BMediaBufferEncoder(const media_format *output_format) + : + BMediaEncoder(output_format), + fBuffer(NULL) { - UNIMPLEMENTED(); + CALLED(); } BMediaBufferEncoder::BMediaBufferEncoder(const media_codec_info *mci) + : + BMediaEncoder(mci), + fBuffer(NULL) { - UNIMPLEMENTED(); + CALLED(); } status_t BMediaBufferEncoder::EncodeToBuffer(void *output_buffer, - size_t *output_size, - const void *input_buffer, - int64 frame_count, - media_encode_info *info) + size_t *output_size, const void *input_buffer, + int64 frame_count, media_encode_info *info) { - UNIMPLEMENTED(); + CALLED(); - return B_ERROR; + status_t error; + fBuffer = output_buffer; + fBufferSize = *output_size; + error = Encode(input_buffer, frame_count, info); + if (fBuffer) { + fBuffer = NULL; + *output_size = 0; + } else { + *output_size = fBufferSize; + } + return error; } @@ -198,12 +333,24 @@ BMediaBufferEncoder::EncodeToBuffer(void *output_buffer, status_t BMediaBufferEncoder::WriteChunk(const void *chunk_data, - size_t chunk_len, - media_encode_info *info) + size_t chunk_len, media_encode_info *info) { - UNIMPLEMENTED(); + CALLED(); - return B_ERROR; + if (fBuffer == NULL) + return B_ENTRY_NOT_FOUND; + + if (chunk_len < 0) + return B_ERROR; + + if (chunk_len > (size_t)fBufferSize) { + memcpy(fBuffer, chunk_data, fBufferSize); + fBuffer = NULL; + return B_DEVICE_FULL; + } + + memcpy(fBuffer, chunk_data, chunk_len); + fBufferSize = chunk_len; + fBuffer = NULL; + return B_NO_ERROR; } - - diff --git a/src/kits/media/PluginManager.cpp b/src/kits/media/PluginManager.cpp index 367607f4ec..333399d1fe 100644 --- a/src/kits/media/PluginManager.cpp +++ b/src/kits/media/PluginManager.cpp @@ -151,9 +151,40 @@ PluginManager::CreateDecoder(Decoder** _decoder, const media_format& format) status_t PluginManager::CreateDecoder(Decoder** decoder, const media_codec_info& mci) { - // TODO - debugger("not implemented"); - return B_ERROR; + TRACE("PluginManager::CreateDecoder enter\n"); + entry_ref ref; + status_t status = AddOnManager::GetInstance()->GetEncoder(&ref, mci.id); + if (status != B_OK) + return status; + + MediaPlugin* plugin = GetPlugin(ref); + if (plugin == NULL) { + ERROR("PluginManager::CreateDecoder: GetPlugin failed\n"); + return B_ERROR; + } + + DecoderPlugin* decoderPlugin = dynamic_cast(plugin); + if (decoderPlugin == NULL) { + ERROR("PluginManager::CreateDecoder: dynamic_cast failed\n"); + PutPlugin(plugin); + return B_ERROR; + } + + // TODO: In theory, one DecoderPlugin could support multiple Decoders, + // but this is not yet handled (passing "0" as index/ID). + *decoder = decoderPlugin->NewDecoder(0); + if (*decoder == NULL) { + ERROR("PluginManager::CreateDecoder: NewDecoder() failed\n"); + PutPlugin(plugin); + return B_ERROR; + } + TRACE(" created decoder: %p\n", *decoder); + (*decoder)->fMediaPlugin = plugin; + + TRACE("PluginManager::CreateDecoder leave\n"); + + return B_OK; + } @@ -294,6 +325,51 @@ PluginManager::CreateEncoder(Encoder** _encoder, } +status_t +PluginManager::CreateEncoder(Encoder** encoder, const media_format& format) +{ + TRACE("PluginManager::CreateEncoder enter nr2\n"); + + entry_ref ref; + + status_t ret = AddOnManager::GetInstance()->GetDecoderForFormat( + &ref, format); + + if (ret != B_OK) { + ERROR("PluginManager::CreateEncoder: can't get decoder for format: " + "%s\n", strerror(ret)); + return ret; + } + + MediaPlugin* plugin = GetPlugin(ref); + if (plugin == NULL) { + ERROR("PluginManager::CreateEncoder: GetPlugin failed\n"); + return B_ERROR; + } + + EncoderPlugin* encoderPlugin = dynamic_cast(plugin); + if (encoderPlugin == NULL) { + ERROR("PluginManager::CreateEncoder: dynamic_cast failed\n"); + PutPlugin(plugin); + return B_ERROR; + } + + + *encoder = encoderPlugin->NewEncoder(format); + if (*encoder == NULL) { + ERROR("PluginManager::CreateEncoder: NewEncoder() failed\n"); + PutPlugin(plugin); + return B_ERROR; + } + TRACE(" created encoder: %p\n", *encoder); + (*encoder)->fMediaPlugin = plugin; + + TRACE("PluginManager::CreateEncoder leave nr2\n"); + + return B_OK; +} + + void PluginManager::DestroyEncoder(Encoder* encoder) {