From 6e567425c6a93bcaa7dc7f6c8afa8a497007d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Mon, 3 Aug 2009 22:53:52 +0000 Subject: [PATCH] Now supports encoded audio. Added Dolby Digital (AC-3) encoding to test this. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32105 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../media/plugins/ffmpeg/AVCodecEncoder.cpp | 133 ++++++++++-------- .../media/plugins/ffmpeg/AVCodecEncoder.h | 12 ++ .../media/plugins/ffmpeg/AVFormatWriter.cpp | 2 +- .../media/plugins/ffmpeg/EncoderTable.cpp | 12 ++ src/add-ons/media/plugins/ffmpeg/config.h | 2 +- .../media/plugins/ffmpeg/libavcodec/Jamfile | 1 + 6 files changed, 105 insertions(+), 57 deletions(-) diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp index 197c65c276..da3ccb847a 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.cpp @@ -9,6 +9,7 @@ #include #include +#include extern "C" { #include "rational.h" @@ -46,9 +47,11 @@ AVCodecEncoder::AVCodecEncoder(uint32 codecID) TRACE("AVCodecEncoder::AVCodecEncoder()\n"); fCodec = avcodec_find_encoder((enum CodecID)codecID); - TRACE(" found AVCodec: %p\n", fCodec); + TRACE(" found AVCodec for %lu: %p\n", codecID, fCodec); memset(&fInputFormat, 0, sizeof(media_format)); + + av_fifo_init(&fAudioFifo, 0); } @@ -337,66 +340,84 @@ AVCodecEncoder::_EncodeAudio(const void* _buffer, int64 frameCount, size_t bufferSize = frameCount * inputFrameSize; bufferSize = min_c(bufferSize, kDefaultChunkBufferSize); - while (frameCount > 0) { - if (frameCount < fContext->frame_size) { - TRACE(" ERROR: too few frames left! (left: %lld, needed: %d)\n", - frameCount, fContext->frame_size); - // TODO: Handle this some way. Maybe use an av_fifo to buffer data? - return B_ERROR; + if (fContext->frame_size > 1) { + // Encoded audio. Things work differently from raw audio. We need + // the fAudioFifo to pipe data. + if (av_fifo_realloc2(&fAudioFifo, + av_fifo_size(&fAudioFifo) + bufferSize) < 0) { + TRACE(" av_fifo_realloc2() failed\n"); + return B_NO_MEMORY; + } + av_fifo_generic_write(&fAudioFifo, const_cast(buffer), + bufferSize, NULL); + + int frameBytes = fContext->frame_size * inputFrameSize; + uint8* tempBuffer = new(std::nothrow) uint8[frameBytes]; + if (tempBuffer == NULL) + return B_NO_MEMORY; + + // Encode as many chunks as can be read from the FIFO. + while (av_fifo_size(&fAudioFifo) >= frameBytes) { + av_fifo_read(&fAudioFifo, tempBuffer, frameBytes); + + ret = _EncodeAudio(tempBuffer, frameBytes, fContext->frame_size, + info); + if (ret != B_OK) + break; } - - int chunkFrames = fContext->frame_size; - - TRACE(" frames left: %lld, chunk frames: %d\n", - frameCount, chunkFrames); - - // Encode one audio chunk/frame. - int usedBytes = avcodec_encode_audio(fContext, fChunkBuffer, - bufferSize, reinterpret_cast(buffer)); - - if (usedBytes < 0) { - TRACE(" avcodec_encode_video() failed: %d\n", usedBytes); - return B_ERROR; - } - - // Maybe we need to use this PTS to calculate start_time: - if (fContext->coded_frame->pts != kNoPTSValue) { - TRACE(" codec frame PTS: %lld (codec time_base: %d/%d)\n", - fContext->coded_frame->pts, fContext->time_base.num, - fContext->time_base.den); - } else { - TRACE(" codec frame PTS: N/A (codec time_base: %d/%d)\n", - fContext->time_base.num, fContext->time_base.den); - } - - // Setup media_encode_info, most important is the time stamp. - info->start_time = (bigtime_t)(fFramesWritten * 1000000LL - / fInputFormat.u.raw_audio.frame_rate); - - // Write the chunk - ret = WriteChunk(fChunkBuffer, usedBytes, info); - if (ret != B_OK) - break; - - size_t framesWritten = usedBytes / inputFrameSize; - if (chunkFrames == 1) { - // For PCM data: - framesWritten = usedBytes / inputFrameSize; - } else { - // For encoded audio: - framesWritten = chunkFrames * inputFrameSize; - } - - // Skip to next chunk of buffer. - fFramesWritten += framesWritten; - frameCount -= framesWritten; - buffer += usedBytes; + } else { + // Raw audio. The number of bytes returned from avcodec_encode_audio() + // is always the same as the number of input bytes. + return _EncodeAudio(buffer, bufferSize, frameCount, + info); } return ret; } +status_t +AVCodecEncoder::_EncodeAudio(const uint8* buffer, size_t bufferSize, + int64 frameCount, media_encode_info* info) +{ + // Encode one audio chunk/frame. The bufferSize has already been adapted + // to the needed size for fContext->frame_size, or we are writing raw + // audio. + int usedBytes = avcodec_encode_audio(fContext, fChunkBuffer, + bufferSize, reinterpret_cast(buffer)); + + if (usedBytes < 0) { + TRACE(" avcodec_encode_video() failed: %d\n", usedBytes); + return B_ERROR; + } + + // Maybe we need to use this PTS to calculate start_time: + if (fContext->coded_frame->pts != kNoPTSValue) { + TRACE(" codec frame PTS: %lld (codec time_base: %d/%d)\n", + fContext->coded_frame->pts, fContext->time_base.num, + fContext->time_base.den); + } else { + TRACE(" codec frame PTS: N/A (codec time_base: %d/%d)\n", + fContext->time_base.num, fContext->time_base.den); + } + + // Setup media_encode_info, most important is the time stamp. + info->start_time = (bigtime_t)(fFramesWritten * 1000000LL + / fInputFormat.u.raw_audio.frame_rate); + + // Write the chunk + status_t ret = WriteChunk(fChunkBuffer, usedBytes, info); + if (ret != B_OK) { + TRACE(" error writing chunk: %s\n", strerror(ret)); + return ret; + } + + fFramesWritten += frameCount; + + return B_OK; +} + + status_t AVCodecEncoder::_EncodeVideo(const void* buffer, int64 frameCount, media_encode_info* info) @@ -450,8 +471,10 @@ AVCodecEncoder::_EncodeVideo(const void* buffer, int64 frameCount, // Write the chunk ret = WriteChunk(fChunkBuffer, usedBytes, info); - if (ret != B_OK) + if (ret != B_OK) { + TRACE(" error writing chunk: %s\n", strerror(ret)); break; + } // Skip to the next frame (but usually, there is only one to encode // for video). diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h index fe7b94eb90..3936eb77db 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecEncoder.h @@ -11,6 +11,7 @@ extern "C" { #include "avcodec.h" #include "swscale.h" + #include "libavutil/fifo.h" } #include "EncoderPlugin.h" @@ -36,10 +37,17 @@ public: virtual status_t Encode(const void* buffer, int64 frameCount, media_encode_info* info); + // TODO: Turns out we need Flush() after all. We buffer encoded audio + // in a FIFO, since the user suggested buffer size may not fit the + // codec buffer size. + private: status_t _EncodeAudio(const void* buffer, int64 frameCount, media_encode_info* info); + status_t _EncodeAudio(const uint8* buffer, + size_t bufferSize, int64 frameCount, + media_encode_info* info); status_t _EncodeVideo(const void* buffer, int64 frameCount, @@ -54,11 +62,15 @@ private: AVCodecContext* fContext; bool fCodecInitDone; + // For video (color space conversion): AVPicture fSrcFrame; AVPicture fDstFrame; AVFrame* fFrame; SwsContext* fSwsContext; + // For encoded audio: + AVFifoBuffer fAudioFifo; + int64 fFramesWritten; uint8* fChunkBuffer; diff --git a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp index bbcf53d907..8974a9fe5e 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVFormatWriter.cpp @@ -103,7 +103,7 @@ status_t AVFormatWriter::StreamCookie::Init(const media_format* format, const media_codec_info* codecInfo) { - TRACE("AVFormatWriter::StreamCookie::Init() (Yes, New)\n"); + TRACE("AVFormatWriter::StreamCookie::Init()\n"); BAutolock _(fStreamLock); diff --git a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp index be7db21cad..5bdf8aae14 100644 --- a/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp +++ b/src/add-ons/media/plugins/ffmpeg/EncoderTable.cpp @@ -59,6 +59,18 @@ const EncoderDescription gEncoderTable[] = { B_ANY_FORMAT_FAMILY, B_MEDIA_RAW_AUDIO, B_MEDIA_ENCODED_AUDIO + }, + { + { + "Dolby Digital (AC-3)", + "ac3", + 0, + CODEC_ID_AC3, + { 0 } + }, + B_ANY_FORMAT_FAMILY, + B_MEDIA_RAW_AUDIO, + B_MEDIA_ENCODED_AUDIO } }; diff --git a/src/add-ons/media/plugins/ffmpeg/config.h b/src/add-ons/media/plugins/ffmpeg/config.h index aa54a933c8..21a53b6f84 100644 --- a/src/add-ons/media/plugins/ffmpeg/config.h +++ b/src/add-ons/media/plugins/ffmpeg/config.h @@ -431,7 +431,7 @@ #define CONFIG_WMV2_ENCODER 0 #define CONFIG_ZLIB_ENCODER 0 #define CONFIG_ZMBV_ENCODER 0 -#define CONFIG_AC3_ENCODER 0 +#define CONFIG_AC3_ENCODER 1 #define CONFIG_ALAC_ENCODER 0 #define CONFIG_FLAC_ENCODER 0 #define CONFIG_MP2_ENCODER 0 diff --git a/src/add-ons/media/plugins/ffmpeg/libavcodec/Jamfile b/src/add-ons/media/plugins/ffmpeg/libavcodec/Jamfile index e4195fa59c..508ffb36f3 100644 --- a/src/add-ons/media/plugins/ffmpeg/libavcodec/Jamfile +++ b/src/add-ons/media/plugins/ffmpeg/libavcodec/Jamfile @@ -29,6 +29,7 @@ StaticLibrary libavcodec.a : ac3_parser.c ac3dec.c ac3dec_data.c + ac3enc.c ac3tab.c acelp_filters.c acelp_pitch_delay.c