diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp index 8c4a5b091e..655e98c2ed 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp @@ -6,8 +6,14 @@ #include "AVCodecEncoder.h" +#include + #include +extern "C" { + #include "rational.h" +} + #undef TRACE #define TRACE_AV_CODEC_ENCODER @@ -18,17 +24,40 @@ #endif -AVCodecEncoder::AVCodecEncoder(const char* shortName) +static const size_t kDefaultChunkBufferSize = FF_MIN_BUFFER_SIZE; + + +AVCodecEncoder::AVCodecEncoder(uint32 codecID) : - Encoder() + Encoder(), + fCodec(NULL), + fContext(avcodec_alloc_context()), + fInputPicture(avcodec_alloc_frame()), +// fOutputPicture(avcodec_alloc_frame()), + fCodecInitDone(false), + fChunkBuffer(new(std::nothrow) uint8[kDefaultChunkBufferSize]) { TRACE("AVCodecEncoder::AVCodecEncoder()\n"); + + fCodec = avcodec_find_encoder((enum CodecID)codecID); + TRACE(" found AVCodec: %p\n", fCodec); + + memset(&fInputFormat, 0, sizeof(media_format)); } AVCodecEncoder::~AVCodecEncoder() { TRACE("AVCodecEncoder::~AVCodecEncoder()\n"); + + if (fCodecInitDone) + avcodec_close(fContext); + +// free(fOutputPicture); + free(fInputPicture); + free(fContext); + + delete[] fChunkBuffer; } @@ -56,10 +85,48 @@ AVCodecEncoder::SetUp(const media_format* inputFormat) { TRACE("AVCodecEncoder::SetUp()\n"); + if (fContext == NULL || fCodec == NULL) + return B_NO_INIT; + if (inputFormat == NULL) return B_BAD_VALUE; - return B_NOT_SUPPORTED; + fInputFormat = *inputFormat; + + if (fInputFormat.type == B_MEDIA_RAW_VIDEO) { + fContext->width = fInputFormat.u.raw_video.display.line_width; + fContext->height = fInputFormat.u.raw_video.display.line_count; +// fContext->gop_size = 12; + fContext->pix_fmt = PIX_FMT_BGR32; +// fContext->rate_emu = 0; + // TODO: Setup rate control: +// fContext->rc_eq = NULL; +// fContext->rc_max_rate = 0; +// fContext->rc_min_rate = 0; + fContext->sample_aspect_ratio.num + = fInputFormat.u.raw_video.pixel_width_aspect; + fContext->sample_aspect_ratio.den + = fInputFormat.u.raw_video.pixel_height_aspect; + if (fContext->sample_aspect_ratio.num == 0 + || fContext->sample_aspect_ratio.den == 0) { + av_reduce(&fContext->sample_aspect_ratio.num, + &fContext->sample_aspect_ratio.den, fContext->width, + fContext->height, 256); + } + + // TODO: This should already happen in AcceptFormat() + fInputFormat.u.raw_video.display.bytes_per_row = fContext->width * 4; + } else { + return B_NOT_SUPPORTED; + } + + // Open the codec + int result = avcodec_open(fContext, fCodec); + fCodecInitDone = (result >= 0); + + TRACE(" avcodec_open(): %d\n", result); + + return fCodecInitDone ? B_OK : B_ERROR; } @@ -87,5 +154,67 @@ AVCodecEncoder::Encode(const void* buffer, int64 frameCount, { TRACE("AVCodecEncoder::Encode(%p, %lld, %p)\n", buffer, frameCount, info); + if (fInputFormat.type == B_MEDIA_RAW_AUDIO) + return _EncodeAudio(buffer, frameCount, info); + else if (fInputFormat.type == B_MEDIA_RAW_VIDEO) + return _EncodeVideo(buffer, frameCount, info); + else + return B_NO_INIT; +} + + +// #pragma mark - + + +status_t +AVCodecEncoder::_EncodeAudio(const void* buffer, int64 frameCount, + media_encode_info* info) +{ + TRACE("AVCodecEncoder::_EncodeAudio(%p, %lld, %p)\n", buffer, frameCount, + info); + return B_NOT_SUPPORTED; } + + +status_t +AVCodecEncoder::_EncodeVideo(const void* buffer, int64 frameCount, + media_encode_info* info) +{ + TRACE("AVCodecEncoder::_EncodeVideo(%p, %lld, %p)\n", buffer, frameCount, + info); + + if (fChunkBuffer == NULL) + return B_NO_MEMORY; + + status_t ret = B_OK; + + while (frameCount > 0) { + size_t bpr = fInputFormat.u.raw_video.display.bytes_per_row; + size_t bufferSize = fInputFormat.u.raw_video.display.line_count * bpr; + + fInputPicture->data[0] = (uint8_t*)buffer; + fInputPicture->linesize[0] = bpr; + + int usedBytes = avcodec_encode_video(fContext, fChunkBuffer, + kDefaultChunkBufferSize, fInputPicture); + + if (usedBytes < 0) { + TRACE(" avcodec_encode_video() failed: %d\n", usedBytes); + return B_ERROR; + } + + // Write the chunk + ret = WriteChunk(fChunkBuffer, usedBytes, info); + if (ret != B_OK) + break; + + // Skip to the next frame (but usually, there is only one to encode + // for video). + frameCount--; + buffer = (const void*)((const uint8*)buffer + bufferSize); + } + + return ret; +} + diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h index 9a0e99208c..b86a416fae 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h @@ -8,12 +8,16 @@ #include +extern "C" { + #include "avcodec.h" +} + #include "EncoderPlugin.h" class AVCodecEncoder : public Encoder { public: - AVCodecEncoder(const char* shortName); + AVCodecEncoder(uint32 codecID); virtual ~AVCodecEncoder(); @@ -32,6 +36,29 @@ public: media_encode_info* info); private: + status_t _EncodeAudio(const void* buffer, + int64 frameCount, + media_encode_info* info); + + status_t _EncodeVideo(const void* buffer, + int64 frameCount, + media_encode_info* info); + +private: + media_format fInputFormat; + + // FFmpeg related members + // TODO: Refactor common base class from AVCodec[De|En]Coder! + AVCodec* fCodec; + AVCodecContext* fContext; + AVFrame* fInputPicture; +// AVFrame* fOutputPicture; + + uint32 fAVCodecID; + + bool fCodecInitDone; + + uint8* fChunkBuffer; }; #endif // AVCODEC_ENCODER_H diff --git a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp index 2b18ac2931..4f313d1798 100644 --- a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp +++ b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp @@ -6,14 +6,18 @@ #include "EncoderTable.h" +extern "C" { + #include "avcodec.h" +} + const EncoderDescription gEncoderTable[] = { { { - "MPEG2 Video", - "mpeg2video", - 0, + "MPEG4 Video", + "mpeg4", 0, + CODEC_ID_MPEG4, { 0 } }, B_ANY_FORMAT_FAMILY, @@ -22,10 +26,10 @@ const EncoderDescription gEncoderTable[] = { }, { { - "WAV", - "wav", - 0, + "MP3 Audio", + "mp3", 0, + CODEC_ID_MP3, { 0 } }, B_ANY_FORMAT_FAMILY, diff --git a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp index cf531998cb..39cedb7741 100644 --- a/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp +++ b/src/add-ons/media/plugins/ffmpeg/FFmpegPlugin.cpp @@ -134,7 +134,7 @@ FFmpegPlugin::GetSupportedFileFormats(const media_file_format** _fileFormats, Encoder* FFmpegPlugin::NewEncoder(const media_codec_info& codecInfo) { - return new(std::nothrow)AVCodecEncoder(codecInfo.short_name); + return new(std::nothrow)AVCodecEncoder(codecInfo.sub_id); } diff --git a/src/servers/media/AddOnManager.cpp b/src/servers/media/AddOnManager.cpp index a9c18086d9..cd1e692905 100644 --- a/src/servers/media/AddOnManager.cpp +++ b/src/servers/media/AddOnManager.cpp @@ -120,9 +120,9 @@ AddOnManager::GetDecoderForFormat(xfer_entry_ref* _decoderRef, return B_OK; } } - return B_ENTRY_NOT_FOUND; + return B_ENTRY_NOT_FOUND; } - + status_t AddOnManager::GetReaders(xfer_entry_ref* outRefs, int32* outCount, @@ -152,7 +152,7 @@ AddOnManager::GetEncoder(xfer_entry_ref* _encoderRef, int32 id) if (info->internalID == (uint32)id) { printf("AddOnManager::GetEncoderForFormat: found encoder %s for " "id %ld\n", info->ref.name, id); - + *_encoderRef = info->ref; return B_OK; } @@ -161,9 +161,9 @@ AddOnManager::GetEncoder(xfer_entry_ref* _encoderRef, int32 id) printf("AddOnManager::GetEncoderForFormat: failed to find encoder for id " "%ld\n", id); - return B_ENTRY_NOT_FOUND; + return B_ENTRY_NOT_FOUND; } - + status_t AddOnManager::GetWriter(xfer_entry_ref* _ref, uint32 internalID) @@ -275,7 +275,7 @@ AddOnManager::_RegisterAddOn(BEntry& entry) _RegisterEncoder(encoder, ref); delete plugin; - + return B_OK; } @@ -347,7 +347,7 @@ AddOnManager::_RegisterAddOns() if (find_directory(directories[i], &path) == B_OK && path.Append("media/plugins") == B_OK - && directory.SetTo(path.Path()) == B_OK + && directory.SetTo(path.Path()) == B_OK && directory.GetNodeRef(&nref) == B_OK) { fHandler->AddDirectory(&nref); } @@ -475,7 +475,6 @@ AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) info.internalID = fNextEncoderCodecInfoID++; int32 cookie = 0; - int32 subID = 0; while (true) { memset(&info.codecInfo, 0, sizeof(media_codec_info)); @@ -487,7 +486,9 @@ AddOnManager::_RegisterEncoder(EncoderPlugin* plugin, const entry_ref& ref) break; } info.codecInfo.id = info.internalID; - info.codecInfo.sub_id = subID++; + // NOTE: info.codecInfo.sub_id is for private use by the Encoder, + // we don't touch it, but it is maintained and passed back to the + // EncoderPlugin in NewEncoder(media_codec_info). if (!fEncoderList.Insert(info)) break;