From 12a9eb5d934906ac3d1ce00c96290b44ffeeaa18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Wed, 8 Jul 2009 09:43:06 +0000 Subject: [PATCH] * Coding style cleanup, some removal of dead code. * Refactored NegotiateOutputFormat() and Decode() into two separate private methods each, one for video and one for audio. * Keep reading chunks when video decoding, until we have got a picture. This gets us scrambled video instead of a black picture for h264 in mpegts. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31457 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../media/plugins/ffmpeg/AVCodecDecoder.cpp | 879 ++++++++++-------- .../media/plugins/ffmpeg/AVCodecDecoder.h | 46 +- 2 files changed, 507 insertions(+), 418 deletions(-) diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp index b2a17bd504..74797cff13 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp @@ -12,12 +12,13 @@ #include "AVCodecDecoder.h" -#include -#include -#include +#include + #include -#define DO_PROFILING 0 +#include +#include + #undef TRACE //#define TRACE_AV_CODEC @@ -27,6 +28,7 @@ # define TRACE(x...) #endif + struct wave_format_ex { uint16 format_tag; uint16 channels; @@ -38,56 +40,68 @@ struct wave_format_ex { // extra_data[extra_size] } _PACKED; -static bigtime_t diff1 = 0, diff2 = 0; -static long prof_cnt = 0; -// uncommenting will make Decode() set the current thread priority to time -// sharing, so it won't totally freeze if you busy-loop in there (to help debug -// with CD Manager) -//#define UNREAL - +// profiling related globals +#define DO_PROFILING 0 + +static bigtime_t decodingTime = 0; +static bigtime_t conversionTime = 0; +static long profileCounter = 0; + + AVCodecDecoder::AVCodecDecoder() - : fHeader(), - fInfo(), - fInputFormat(), - fOutputVideoFormat(), - fFrame(0), - isAudio(false), - fCodec(NULL), - ffc(NULL), - fCodecInitDone(false), - conv_func(NULL), - fExtraData(NULL), - fExtraDataSize(0), - fBlockAlign(0), - fOutputBuffer(0) + : + fHeader(), + fInfo(), + fInputFormat(), + fOutputVideoFormat(), + fFrame(0), + fIsAudio(false), + fCodecIndexInTable(-1), + fCodec(NULL), + fContext(avcodec_alloc_context()), + fInputPicture(avcodec_alloc_frame()), + fOutputPicture(avcodec_alloc_frame()), + + fCodecInitDone(false), + + fFormatConversionFunc(NULL), + + fExtraData(NULL), + fExtraDataSize(0), + fBlockAlign(0), + + fStartTime(0), + fOutputFrameCount(0), + fOutputFrameRate(1.0), + fOutputFrameSize(0), + + fOutputBuffer(NULL), + fOutputBufferOffset(0), + fOutputBufferSize(0) { TRACE("AVCodecDecoder::AVCodecDecoder()\n"); - - ffc = avcodec_alloc_context(); - ffpicture = avcodec_alloc_frame(); - opicture = avcodec_alloc_frame(); } AVCodecDecoder::~AVCodecDecoder() { - TRACE("[%c] AVCodecDecoder::~AVCodecDecoder()\n", isAudio?('a'):('v')); + TRACE("[%c] AVCodecDecoder::~AVCodecDecoder()\n", fIsAudio?('a'):('v')); #ifdef DO_PROFILING - if (prof_cnt > 0) { + if (profileCounter > 0) { printf("[%c] profile: d1 = %lld, d2 = %lld (%Ld)\n", - isAudio?('a'):('v'), diff1/prof_cnt, diff2/prof_cnt, + fIsAudio?('a'):('v'), decodingTime / profileCounter, conversionTime / profileCounter, fFrame); } #endif if (fCodecInitDone) - avcodec_close(ffc); + avcodec_close(fContext); - free(opicture); - free(ffpicture); - free(ffc); + free(fOutputPicture); + free(fInputPicture); + free(fContext); delete [] fExtraData; delete [] fOutputBuffer; @@ -95,45 +109,47 @@ AVCodecDecoder::~AVCodecDecoder() void -AVCodecDecoder::GetCodecInfo(media_codec_info *mci) +AVCodecDecoder::GetCodecInfo(media_codec_info* mci) { sprintf(mci->short_name, "ff:%s", fCodec->name); sprintf(mci->pretty_name, "%s (libavcodec %s)", - gCodecTable[ffcodec_index_in_table].prettyname, fCodec->name); + gCodecTable[fCodecIndexInTable].prettyname, fCodec->name); } status_t -AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer, +AVCodecDecoder::Setup(media_format* ioEncodedFormat, const void* infoBuffer, size_t infoSize) { if (ioEncodedFormat->type != B_MEDIA_ENCODED_AUDIO && ioEncodedFormat->type != B_MEDIA_ENCODED_VIDEO) return B_ERROR; - isAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO); - TRACE("[%c] AVCodecDecoder::Setup()\n", isAudio?('a'):('v')); + fIsAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO); + TRACE("[%c] AVCodecDecoder::Setup()\n", fIsAudio?('a'):('v')); - if (isAudio && !fOutputBuffer) + if (fIsAudio && !fOutputBuffer) fOutputBuffer = new char[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -//#if DEBUG +#ifdef TRACE_AV_CODEC char buffer[1024]; string_for_format(*ioEncodedFormat, buffer, sizeof(buffer)); - TRACE("[%c] input_format=%s\n", isAudio?('a'):('v'), buffer); - TRACE("[%c] infoSize=%ld\n", isAudio?('a'):('v'), infoSize); - TRACE("[%c] user_data_type=%08lx\n", isAudio?('a'):('v'), ioEncodedFormat->user_data_type); - TRACE("[%c] meta_data_size=%ld\n", isAudio?('a'):('v'), ioEncodedFormat->MetaDataSize()); - TRACE("[%c] info_size=%ld\n", isAudio?('a'):('v'), infoSize); -//#endif + TRACE("[%c] input_format = %s\n", fIsAudio?('a'):('v'), buffer); + TRACE("[%c] infoSize = %ld\n", fIsAudio?('a'):('v'), infoSize); + TRACE("[%c] user_data_type = %08lx\n", fIsAudio?('a'):('v'), + ioEncodedFormat->user_data_type); + TRACE("[%c] meta_data_size = %ld\n", fIsAudio?('a'):('v'), + ioEncodedFormat->MetaDataSize()); + TRACE("[%c] info_size = %ld\n", fIsAudio?('a'):('v'), infoSize); +#endif media_format_description descr; for (int32 i = 0; gCodecTable[i].id; i++) { - ffcodec_index_in_table = i; + fCodecIndexInTable = i; uint64 cid; - if (BMediaFormats().GetCodeFor(*ioEncodedFormat, gCodecTable[i].family, - &descr) == B_OK + if (BMediaFormats().GetCodeFor(*ioEncodedFormat, + gCodecTable[i].family, &descr) == B_OK && gCodecTable[i].type == ioEncodedFormat->type) { switch(gCodecTable[i].family) { case B_WAV_FORMAT_FAMILY: @@ -159,21 +175,22 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer, puts("ERR family"); return B_ERROR; } - TRACE(" 0x%04lx codec id = \"%c%c%c%c\"\n", uint32(cid), (char)((cid >> 24) & 0xff), - (char)((cid >> 16) & 0xff), (char)((cid >> 8) & 0xff), - (char)(cid & 0xff)); + TRACE(" 0x%04lx codec id = \"%c%c%c%c\"\n", uint32(cid), + (char)((cid >> 24) & 0xff), (char)((cid >> 16) & 0xff), + (char)((cid >> 8) & 0xff), (char)(cid & 0xff)); if (gCodecTable[i].family == descr.family && gCodecTable[i].fourcc == cid) { fCodec = avcodec_find_decoder(gCodecTable[i].id); if (!fCodec) { - TRACE("AVCodecDecoder: unable to find the correct ffmpeg decoder " - "(id = %d)!!!\n",gCodecTable[i].id); + TRACE("AVCodecDecoder: unable to find the correct ffmpeg " + "decoder (id = %d)!!!\n",gCodecTable[i].id); return B_ERROR; } TRACE("AVCodecDecoder: found decoder %s\n",fCodec->name); - if (gCodecTable[i].family == B_WAV_FORMAT_FAMILY && infoSize >= sizeof(wave_format_ex)) { + if (gCodecTable[i].family == B_WAV_FORMAT_FAMILY + && infoSize >= sizeof(wave_format_ex)) { const wave_format_ex *wfmt_data = (const wave_format_ex *)infoBuffer; size_t wfmt_size = infoSize; @@ -186,12 +203,16 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer, } } } else { - fBlockAlign = ioEncodedFormat->u.encoded_audio.output.buffer_size; - TRACE("AVCodecDecoder: extra data size %ld\n",infoSize); + fBlockAlign + = ioEncodedFormat->u.encoded_audio.output.buffer_size; + TRACE("AVCodecDecoder: extra data size %ld\n", infoSize); fExtraDataSize = infoSize; if (fExtraDataSize) { - fExtraData = new char [fExtraDataSize]; - memcpy(fExtraData, infoBuffer, fExtraDataSize); + fExtraData = new(std::nothrow) char[fExtraDataSize]; + if (fExtraData != NULL) + memcpy(fExtraData, infoBuffer, fExtraDataSize); + else + fExtraDataSize = 0; } } @@ -206,27 +227,28 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer, status_t -AVCodecDecoder::Seek(uint32 seekTo, - int64 seekFrame, int64 *frame, - bigtime_t seekTime, bigtime_t *time) +AVCodecDecoder::Seek(uint32 seekTo, int64 seekFrame, int64* frame, + bigtime_t seekTime, bigtime_t* time) { - // reset the ffmpeg codec - // to flush buffers, so we keep the sync - if (isAudio && fCodecInitDone) { + // Reset the FFmpeg codec to flush buffers, so we keep the sync + // TODO: Maybe that should be done for video codecs, too? + if (fIsAudio && fCodecInitDone) { fCodecInitDone = false; - avcodec_close(ffc); - fCodecInitDone = (avcodec_open(ffc, fCodec) >= 0); + avcodec_close(fContext); + fCodecInitDone = (avcodec_open(fContext, fCodec) >= 0); } if (seekTo == B_MEDIA_SEEK_TO_TIME) { TRACE("AVCodecDecoder::Seek by time "); - TRACE("from frame %Ld and time %.6f TO Required Time %.6f. ", fFrame, fStartTime / 1000000.0, seekTime / 1000000.0); + TRACE("from frame %Ld and time %.6f TO Required Time %.6f. ", + fFrame, fStartTime / 1000000.0, seekTime / 1000000.0); *frame = (int64)(seekTime * fOutputFrameRate / 1000000LL); *time = seekTime; } else if (seekTo == B_MEDIA_SEEK_TO_FRAME) { TRACE("AVCodecDecoder::Seek by Frame "); - TRACE("from time %.6f and frame %Ld TO Required Frame %Ld. ", fStartTime / 1000000.0, fFrame, seekFrame); + TRACE("from time %.6f and frame %Ld TO Required Frame %Ld. ", + fStartTime / 1000000.0, fFrame, seekFrame); *time = (bigtime_t)(seekFrame * 1000000LL / fOutputFrameRate); *frame = seekFrame; @@ -241,363 +263,416 @@ AVCodecDecoder::Seek(uint32 seekTo, status_t -AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format) +AVCodecDecoder::NegotiateOutputFormat(media_format* inOutFormat) { - TRACE("[%c] AVCodecDecoder::NegotiateOutputFormat()\n", isAudio?('a'):('v')); + TRACE("AVCodecDecoder::NegotiateOutputFormat()\n", + fIsAudio?('a'):('v')); - int result; - -#if DEBUG +#if TRACE_AV_CODEC char buffer[1024]; - string_for_format(*inout_format, buffer, sizeof(buffer)); - TRACE("[%c] in_format=%s\n", isAudio?('a'):('v'), buffer); + string_for_format(*inOutFormat, buffer, sizeof(buffer)); + TRACE(" [%c] requested format = %s\n", fIsAudio?('a'):('v'), buffer); #endif - if (isAudio) { - media_multi_audio_format outputAudioFormat; - outputAudioFormat = media_raw_audio_format::wildcard; - outputAudioFormat.format = media_raw_audio_format::B_AUDIO_SHORT; - outputAudioFormat.byte_order = B_MEDIA_HOST_ENDIAN; - outputAudioFormat.frame_rate - = fInputFormat.u.encoded_audio.output.frame_rate; - outputAudioFormat.channel_count - = fInputFormat.u.encoded_audio.output.channel_count; - outputAudioFormat.buffer_size - = 1024 * fInputFormat.u.encoded_audio.output.channel_count; - inout_format->type = B_MEDIA_RAW_AUDIO; - inout_format->u.raw_audio = outputAudioFormat; - - ffc->bit_rate = (int) fInputFormat.u.encoded_audio.bit_rate; - ffc->sample_rate = (int) fInputFormat.u.encoded_audio.output.frame_rate; - ffc->channels = fInputFormat.u.encoded_audio.output.channel_count; - ffc->block_align = fBlockAlign; - ffc->extradata = (uint8_t *)fExtraData; - ffc->extradata_size = fExtraDataSize; - - TRACE("bit_rate %d, sample_rate %d, channels %d, block_align %d, " - "extradata_size %d\n", ffc->bit_rate, ffc->sample_rate, - ffc->channels, ffc->block_align, ffc->extradata_size); - - // close any previous instance - if (fCodecInitDone) { - fCodecInitDone = false; - avcodec_close(ffc); - } - - // open new - result = avcodec_open(ffc, fCodec); - fCodecInitDone = (result >= 0); - - TRACE("audio: bit_rate = %d, sample_rate = %d, chans = %d Init = %d\n", - ffc->bit_rate, ffc->sample_rate, ffc->channels, result); - - fStartTime = 0; - fOutputFrameSize = 2 * outputAudioFormat.channel_count; - fOutputFrameCount = outputAudioFormat.buffer_size / fOutputFrameSize; - fOutputFrameRate = outputAudioFormat.frame_rate; - fChunkBuffer = 0; - fChunkBufferOffset = 0; - fChunkBufferSize = 0; - fOutputBufferOffset = 0; - fOutputBufferSize = 0; - - inout_format->require_flags = 0; - inout_format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; - - if (!fCodecInitDone) { - TRACE("avcodec_open() failed!\n"); - return B_ERROR; - } - - return B_OK; - - } else { // VIDEO - - fOutputVideoFormat = fInputFormat.u.encoded_video.output; - - ffc->width = fOutputVideoFormat.display.line_width; - ffc->height = fOutputVideoFormat.display.line_count; -// ffc->frame_rate = (int)(fOutputVideoFormat.field_rate -// * ffc->frame_rate_base); - - fOutputFrameRate = fOutputVideoFormat.field_rate; - - ffc->extradata = (uint8_t *)fExtraData; - ffc->extradata_size = fExtraDataSize; - -// if (fInputFormat.MetaDataSize() > 0) { -// ffc->extradata = (uint8_t *)fInputFormat.MetaData(); -// ffc->extradata_size = fInputFormat.MetaDataSize(); -// } - - TRACE("#### requested video format 0x%x\n", - inout_format->u.raw_video.display.format); - - // make MediaPlayer happy (if not in rgb32 screen depth and no overlay, - // it will only ask for YCbCr, which DrawBitmap doesn't handle, so the - // default colordepth is RGB32) - if (inout_format->u.raw_video.display.format == B_YCbCr422) - fOutputVideoFormat.display.format = B_YCbCr422; - else - fOutputVideoFormat.display.format = B_RGB32; - - // search for a pixel-format the codec handles - // XXX We should try this a couple of times until it succeeds, each time - // XXX using another format pixel-format that is supported by the - // XXX decoder. But libavcodec doesn't seem to offer any way to tell the - // XXX decoder which format it should use. - conv_func = 0; - for (int i = 0; i < 1; i++) { // iterate over supported codec formats - // close any previous instance - if (fCodecInitDone) { - fCodecInitDone = false; - avcodec_close(ffc); - } - // XXX set n-th ffc->pix_fmt here - if (avcodec_open(ffc, fCodec) >= 0) { - fCodecInitDone = true; - - conv_func = resolve_colorspace(fOutputVideoFormat.display.format, ffc->pix_fmt); - } - if (conv_func != 0) - break; - } - - if (!fCodecInitDone) { - TRACE("avcodec_open() failed to init codec!\n"); - return B_ERROR; - } - - if (!conv_func) { - TRACE("no conv_func found or decoder has not set the pixel format yet!\n"); - } - - if (fOutputVideoFormat.display.format == B_YCbCr422) { - fOutputVideoFormat.display.bytes_per_row - = 2 * fOutputVideoFormat.display.line_width; - } else { - fOutputVideoFormat.display.bytes_per_row - = 4 * fOutputVideoFormat.display.line_width; - } - - inout_format->type = B_MEDIA_RAW_VIDEO; - inout_format->u.raw_video = fOutputVideoFormat; - - inout_format->require_flags = 0; - inout_format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; - - #if DEBUG - string_for_format(*inout_format, buffer, sizeof(buffer)); - TRACE("[%c] out_format=%s\n", isAudio?('a'):('v'), buffer); - #endif - - TRACE("#### returned video format 0x%x\n", - inout_format->u.raw_video.display.format); - - return B_OK; - } + if (fIsAudio) + return _NegotiateAudioOutputFormat(inOutFormat); + else + return _NegotiateVideoOutputFormat(inOutFormat); } status_t -AVCodecDecoder::Decode(void *out_buffer, int64 *out_frameCount, - media_header *mh, media_decode_info *info) +AVCodecDecoder::Decode(void* outBuffer, int64* outFrameCount, + media_header* mediaHeader, media_decode_info* info) { - const void *data; - if (!fCodecInitDone) - return B_BAD_VALUE; + return B_NO_INIT; -#ifdef DO_PROFILING - bigtime_t prof_t1, prof_t2, prof_t3; -#endif +// TRACE("[%c] AVCodecDecoder::Decode() for time %Ld\n", fIsAudio?('a'):('v'), +// fStartTime); -#ifdef UNREAL - set_thread_priority(find_thread(NULL), B_NORMAL_PRIORITY); -#endif + mediaHeader->start_time = fStartTime; -// TRACE("[%c] AVCodecDecoder::Decode() for time %Ld\n", isAudio?('a'):('v'), fStartTime); + status_t ret; + if (fIsAudio) + ret = _DecodeAudio(outBuffer, outFrameCount, mediaHeader, info); + else + ret = _DecodeVideo(outBuffer, outFrameCount, mediaHeader, info); - mh->start_time = fStartTime; + fStartTime = (bigtime_t)(1000000LL * fFrame / fOutputFrameRate); - if (isAudio) { + return ret; +} -// TRACE("audio start_time %.6f\n", mh->start_time / 1000000.0); - char *output_buffer = (char *)out_buffer; - *out_frameCount = 0; - while (*out_frameCount < fOutputFrameCount) { - if (fOutputBufferSize < 0) { - TRACE("############ fOutputBufferSize %ld\n", - fOutputBufferSize); - fOutputBufferSize = 0; - } - if (fChunkBufferSize < 0) { - TRACE("############ fChunkBufferSize %ld\n", - fChunkBufferSize); - fChunkBufferSize = 0; - } - - if (fOutputBufferSize > 0) { - int32 frames = min_c(fOutputFrameCount - *out_frameCount, - fOutputBufferSize / fOutputFrameSize); - memcpy(output_buffer, fOutputBuffer + fOutputBufferOffset, - frames * fOutputFrameSize); - fOutputBufferOffset += frames * fOutputFrameSize; - fOutputBufferSize -= frames * fOutputFrameSize; - output_buffer += frames * fOutputFrameSize; - *out_frameCount += frames; - fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate); - continue; - } - if (fChunkBufferSize == 0) { - media_header chunk_mh; - status_t err; - err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, &chunk_mh); - if (err == B_LAST_BUFFER_ERROR) { - TRACE("Last Chunk with chunk size %ld\n",fChunkBufferSize); - fChunkBufferSize = 0; - return err; - } - if (err != B_OK || fChunkBufferSize < 0) { - printf("GetNextChunk error %ld\n",fChunkBufferSize); - fChunkBufferSize = 0; - break; - } - fChunkBufferOffset = 0; - fStartTime = chunk_mh.start_time; - if (*out_frameCount == 0) - mh->start_time = chunk_mh.start_time; - continue; - } - if (fOutputBufferSize == 0) { - int len; - int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - len = avcodec_decode_audio2(ffc, (short *)fOutputBuffer, - &out_size, (uint8_t*)fChunkBuffer + fChunkBufferOffset, - fChunkBufferSize); - if (len < 0) { - TRACE("########### audio decode error, " - "fChunkBufferSize %ld, fChunkBufferOffset %ld\n", - fChunkBufferSize, fChunkBufferOffset); - out_size = 0; - len = 0; - fChunkBufferOffset = 0; - fChunkBufferSize = 0; -// } else { -// TRACE("audio decode: len %d, out_size %d\n", len, out_size); - } - fChunkBufferOffset += len; - fChunkBufferSize -= len; - fOutputBufferOffset = 0; - fOutputBufferSize = out_size; - } - } - fFrame += *out_frameCount; +// #pragma mark - -// TRACE("Played %Ld frames at time %Ld\n",*out_frameCount, mh->start_time); - } else { // Video +status_t +AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat) +{ + TRACE("AVCodecDecoder::_NegotiateAudioOutputFormat()\n"); - media_header chunk_mh; - status_t err; - size_t size; + media_multi_audio_format outputAudioFormat; + outputAudioFormat = media_raw_audio_format::wildcard; + outputAudioFormat.format = media_raw_audio_format::B_AUDIO_SHORT; + outputAudioFormat.byte_order = B_MEDIA_HOST_ENDIAN; + outputAudioFormat.frame_rate + = fInputFormat.u.encoded_audio.output.frame_rate; + outputAudioFormat.channel_count + = fInputFormat.u.encoded_audio.output.channel_count; + outputAudioFormat.buffer_size + = 1024 * fInputFormat.u.encoded_audio.output.channel_count; + inOutFormat->type = B_MEDIA_RAW_AUDIO; + inOutFormat->u.raw_audio = outputAudioFormat; - err = GetNextChunk(&data, &size, &chunk_mh); - if (err != B_OK) { - TRACE("AVCodecDecoder::Decode(): error 0x%08lx from GetNextChunk()\n", err); - return err; - } + fContext->bit_rate = (int)fInputFormat.u.encoded_audio.bit_rate; + fContext->sample_rate + = (int)fInputFormat.u.encoded_audio.output.frame_rate; + fContext->channels = fInputFormat.u.encoded_audio.output.channel_count; + fContext->block_align = fBlockAlign; + fContext->extradata = (uint8_t*)fExtraData; + fContext->extradata_size = fExtraDataSize; - mh->type = B_MEDIA_RAW_VIDEO; -// mh->start_time = chunk_mh.start_time; - mh->file_pos = 0; - mh->orig_size = 0; - mh->u.raw_video.field_gamma = 1.0; - mh->u.raw_video.field_sequence = fFrame; - mh->u.raw_video.field_number = 0; - mh->u.raw_video.pulldown_number = 0; - mh->u.raw_video.first_active_line = 1; - mh->u.raw_video.line_count = fOutputVideoFormat.display.line_count; + TRACE("bit_rate %d, sample_rate %d, channels %d, block_align %d, " + "extradata_size %d\n", fContext->bit_rate, fContext->sample_rate, + fContext->channels, fContext->block_align, fContext->extradata_size); - TRACE("[%c] start_time=%02d:%02d.%02d field_sequence=%lu\n", - isAudio ? ('a') : ('v'), - int((mh->start_time / 60000000) % 60), - int((mh->start_time / 1000000) % 60), - int((mh->start_time / 10000) % 100), - mh->u.raw_video.field_sequence); - -#ifdef DO_PROFILING - prof_t1 = system_time(); -#endif - - int got_picture = 0; - int len; - len = avcodec_decode_video(ffc, ffpicture, &got_picture, - (uint8_t *)data, size); - -//TRACE("FFDEC: PTS = %d:%d:%d.%d - ffc->frame_number = %ld " -// "ffc->frame_rate = %ld\n", (int)(ffc->pts / (60*60*1000000)), -// (int)(ffc->pts / (60*1000000)), (int)(ffc->pts / (1000000)), -// (int)(ffc->pts % 1000000), ffc->frame_number, ffc->frame_rate); -//TRACE("FFDEC: PTS = %d:%d:%d.%d - ffc->frame_number = %ld " -// "ffc->frame_rate = %ld\n", (int)(ffpicture->pts / (60*60*1000000)), -// (int)(ffpicture->pts / (60*1000000)), (int)(ffpicture->pts / (1000000)), -// (int)(ffpicture->pts % 1000000), ffc->frame_number, ffc->frame_rate); - - if (len < 0) { - printf("[%c] AVCodecDecoder: error in decoding frame %lld\n", - isAudio?('a'):('v'), *out_frameCount); - } - - if (got_picture) { -#ifdef DO_PROFILING - prof_t2 = system_time(); -#endif -// TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size, -// pixfmt_to_string(ffc->pix_fmt)); - - // Some decoders do not set pix_fmt until they have decoded 1 frame - if (conv_func == 0) { - conv_func = resolve_colorspace(fOutputVideoFormat.display.format, ffc->pix_fmt); - } - opicture->data[0] = (uint8_t *)out_buffer; - opicture->linesize[0] = fOutputVideoFormat.display.bytes_per_row; - - if (conv_func) { - (*conv_func)(ffpicture, opicture, - fOutputVideoFormat.display.line_width, - fOutputVideoFormat.display.line_count); - } -#ifdef DEBUG - dump_ffframe(ffpicture, "ffpict"); -// dump_ffframe(opicture, "opict"); -#endif - *out_frameCount = 1; - fFrame++; - -#ifdef DO_PROFILING - prof_t3 = system_time(); - diff1 += prof_t2 - prof_t1; - diff2 += prof_t3 - prof_t2; - prof_cnt++; - if (!(fFrame % 10)) { - if (info) { - TRACE("[%c] profile: d1 = %lld, d2 = %lld (%Ld) required %Ld\n", - isAudio?('a'):('v'), diff1/prof_cnt, diff2/prof_cnt, - fFrame, info->time_to_decode); - } else { - TRACE("[%c] profile: d1 = %lld, d2 = %lld (%Ld) required %Ld\n", - isAudio?('a'):('v'), diff1/prof_cnt, diff2/prof_cnt, - fFrame, bigtime_t(1000000LL / fOutputFrameRate)); - } - } -#endif - } + // close any previous instance + if (fCodecInitDone) { + fCodecInitDone = false; + avcodec_close(fContext); } - fStartTime = (bigtime_t) (1000000LL * fFrame / fOutputFrameRate); + // open new + int result = avcodec_open(fContext, fCodec); + fCodecInitDone = (result >= 0); + + TRACE("audio: bit_rate = %d, sample_rate = %d, chans = %d Init = %d\n", + fContext->bit_rate, fContext->sample_rate, fContext->channels, + result); + + fStartTime = 0; + fOutputFrameSize = 2 * outputAudioFormat.channel_count; + fOutputFrameCount = outputAudioFormat.buffer_size / fOutputFrameSize; + fOutputFrameRate = outputAudioFormat.frame_rate; + fChunkBuffer = 0; + fChunkBufferOffset = 0; + fChunkBufferSize = 0; + fOutputBufferOffset = 0; + fOutputBufferSize = 0; + + inOutFormat->require_flags = 0; + inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + + if (!fCodecInitDone) { + TRACE("avcodec_open() failed!\n"); + return B_ERROR; + } return B_OK; } + +status_t +AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat) +{ + TRACE("AVCodecDecoder::_NegotiateVideoOutputFormat()\n"); + + fOutputVideoFormat = fInputFormat.u.encoded_video.output; + + fContext->width = fOutputVideoFormat.display.line_width; + fContext->height = fOutputVideoFormat.display.line_count; +// fContext->frame_rate = (int)(fOutputVideoFormat.field_rate +// * fContext->frame_rate_base); + + fOutputFrameRate = fOutputVideoFormat.field_rate; + + fContext->extradata = (uint8_t *)fExtraData; + fContext->extradata_size = fExtraDataSize; + +// if (fInputFormat.MetaDataSize() > 0) { +// fContext->extradata = (uint8_t *)fInputFormat.MetaData(); +// fContext->extradata_size = fInputFormat.MetaDataSize(); +// } + + TRACE(" requested video format 0x%x\n", + inOutFormat->u.raw_video.display.format); + + // Make MediaPlayer happy (if not in rgb32 screen depth and no overlay, + // it will only ask for YCbCr, which DrawBitmap doesn't handle, so the + // default colordepth is RGB32). + if (inOutFormat->u.raw_video.display.format == B_YCbCr422) + fOutputVideoFormat.display.format = B_YCbCr422; + else + fOutputVideoFormat.display.format = B_RGB32; + + // Search for a pixel-format the codec handles + // TODO: We should try this a couple of times until it succeeds, each + // time using another pixel-format that is supported by the decoder. + // But libavcodec doesn't seem to offer any way to tell the decoder + // which format it should use. + fFormatConversionFunc = 0; + // Iterate over supported codec formats + for (int i = 0; i < 1; i++) { + // close any previous instance + if (fCodecInitDone) { + fCodecInitDone = false; + avcodec_close(fContext); + } + // TODO: Set n-th fContext->pix_fmt here + if (avcodec_open(fContext, fCodec) >= 0) { + fCodecInitDone = true; + + fFormatConversionFunc = resolve_colorspace( + fOutputVideoFormat.display.format, fContext->pix_fmt); + } + if (fFormatConversionFunc != NULL) + break; + } + + if (!fCodecInitDone) { + TRACE("avcodec_open() failed to init codec!\n"); + return B_ERROR; + } + + if (fFormatConversionFunc == NULL) { + TRACE("no pixel format conversion function found or decoder has " + "not set the pixel format yet!\n"); + } + + if (fOutputVideoFormat.display.format == B_YCbCr422) { + fOutputVideoFormat.display.bytes_per_row + = 2 * fOutputVideoFormat.display.line_width; + } else { + fOutputVideoFormat.display.bytes_per_row + = 4 * fOutputVideoFormat.display.line_width; + } + + inOutFormat->type = B_MEDIA_RAW_VIDEO; + inOutFormat->u.raw_video = fOutputVideoFormat; + + inOutFormat->require_flags = 0; + inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; + +#if TRACE_AV_CODEC + char buffer[1024]; + string_for_format(*inOutFormat, buffer, sizeof(buffer)); + TRACE("[v] outFormat = %s\n", buffer); + TRACE(" returned video format 0x%x\n", + inOutFormat->u.raw_video.display.format); +#endif + + return B_OK; +} + + +status_t +AVCodecDecoder::_DecodeAudio(void* outBuffer, int64* outFrameCount, + media_header* mediaHeader, media_decode_info* info) +{ +// TRACE("audio start_time %.6f\n", mediaHeader->start_time / 1000000.0); + + char* output_buffer = (char*)outBuffer; + *outFrameCount = 0; + while (*outFrameCount < fOutputFrameCount) { + if (fOutputBufferSize < 0) { + TRACE("############ fOutputBufferSize %ld\n", + fOutputBufferSize); + fOutputBufferSize = 0; + } + if (fChunkBufferSize < 0) { + TRACE("############ fChunkBufferSize %ld\n", + fChunkBufferSize); + fChunkBufferSize = 0; + } + + if (fOutputBufferSize > 0) { + int32 frames = min_c(fOutputFrameCount - *outFrameCount, + fOutputBufferSize / fOutputFrameSize); + memcpy(output_buffer, fOutputBuffer + fOutputBufferOffset, + frames * fOutputFrameSize); + fOutputBufferOffset += frames * fOutputFrameSize; + fOutputBufferSize -= frames * fOutputFrameSize; + output_buffer += frames * fOutputFrameSize; + *outFrameCount += frames; + fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate); + continue; + } + if (fChunkBufferSize == 0) { + media_header chunkMediaHeader; + status_t err; + err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, &chunkMediaHeader); + if (err == B_LAST_BUFFER_ERROR) { + TRACE("Last Chunk with chunk size %ld\n",fChunkBufferSize); + fChunkBufferSize = 0; + return err; + } + if (err != B_OK || fChunkBufferSize < 0) { + printf("GetNextChunk error %ld\n",fChunkBufferSize); + fChunkBufferSize = 0; + break; + } + fChunkBufferOffset = 0; + fStartTime = chunkMediaHeader.start_time; + if (*outFrameCount == 0) + mediaHeader->start_time = chunkMediaHeader.start_time; + continue; + } + if (fOutputBufferSize == 0) { + int len; + int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + len = avcodec_decode_audio2(fContext, (short *)fOutputBuffer, + &out_size, (uint8_t*)fChunkBuffer + fChunkBufferOffset, + fChunkBufferSize); + if (len < 0) { + TRACE("########### audio decode error, " + "fChunkBufferSize %ld, fChunkBufferOffset %ld\n", + fChunkBufferSize, fChunkBufferOffset); + out_size = 0; + len = 0; + fChunkBufferOffset = 0; + fChunkBufferSize = 0; +// } else { +// TRACE("audio decode: len %d, out_size %d\n", len, out_size); + } + fChunkBufferOffset += len; + fChunkBufferSize -= len; + fOutputBufferOffset = 0; + fOutputBufferSize = out_size; + } + } + fFrame += *outFrameCount; + +// TRACE("Played %Ld frames at time %Ld\n",*outFrameCount, mediaHeader->start_time); + return B_OK; +} + + +status_t +AVCodecDecoder::_DecodeVideo(void* outBuffer, int64* outFrameCount, + media_header* mediaHeader, media_decode_info* info) +{ + bool firstRun = true; + while (true) { + const void* data; + size_t size; + media_header chunkMediaHeader; + status_t err = GetNextChunk(&data, &size, &chunkMediaHeader); + if (err != B_OK) { + TRACE("AVCodecDecoder::_DecodeVideo(): error from " + "GetNextChunk(): %s\n", strerror(err)); + return err; + } + if (firstRun) { + firstRun = false; + + mediaHeader->type = B_MEDIA_RAW_VIDEO; +// mediaHeader->start_time = chunkMediaHeader.start_time; + mediaHeader->file_pos = 0; + mediaHeader->orig_size = 0; + mediaHeader->u.raw_video.field_gamma = 1.0; + mediaHeader->u.raw_video.field_sequence = fFrame; + mediaHeader->u.raw_video.field_number = 0; + mediaHeader->u.raw_video.pulldown_number = 0; + mediaHeader->u.raw_video.first_active_line = 1; + mediaHeader->u.raw_video.line_count + = fOutputVideoFormat.display.line_count; + + TRACE("[v] start_time=%02d:%02d.%02d field_sequence=%lu\n", + int((mediaHeader->start_time / 60000000) % 60), + int((mediaHeader->start_time / 1000000) % 60), + int((mediaHeader->start_time / 10000) % 100), + mediaHeader->u.raw_video.field_sequence); + } + +#if DO_PROFILING + bigtime_t startTime = system_time(); +#endif + + // NOTE: In the FFmpeg code example I've read, the length returned by + // avcodec_decode_video() is completely ignored. Furthermore, the + // packet buffers are supposed to contain complete frames only so we + // don't seem to be required to buffer any packets because not the + // complete packet has been read. + int gotPicture = 0; + int len = avcodec_decode_video(fContext, fInputPicture, &gotPicture, + (uint8_t*)data, size); + if (len < 0) { + TRACE("[v] AVCodecDecoder: error in decoding frame %lld: %d\n", + fFrame, len); +// return B_ERROR; + } + + +//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld " +// "fContext->frame_rate = %ld\n", (int)(fContext->pts / (60*60*1000000)), +// (int)(fContext->pts / (60*1000000)), (int)(fContext->pts / (1000000)), +// (int)(fContext->pts % 1000000), fContext->frame_number, +// fContext->frame_rate); +//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld " +// "fContext->frame_rate = %ld\n", +// (int)(fInputPicture->pts / (60*60*1000000)), +// (int)(fInputPicture->pts / (60*1000000)), +// (int)(fInputPicture->pts / (1000000)), +// (int)(fInputPicture->pts % 1000000), fContext->frame_number, +// fContext->frame_rate); + + if (gotPicture) { +#if DO_PROFILING + bigtime_t formatConversionStart = system_time(); +#endif +// TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size, +// pixfmt_to_string(fContext->pix_fmt)); + + // Some decoders do not set pix_fmt until they have decoded 1 frame + if (fFormatConversionFunc == NULL) { + fFormatConversionFunc = resolve_colorspace( + fOutputVideoFormat.display.format, fContext->pix_fmt); + } + fOutputPicture->data[0] = (uint8_t*)outBuffer; + fOutputPicture->linesize[0] + = fOutputVideoFormat.display.bytes_per_row; + + if (fFormatConversionFunc != NULL) { + (*fFormatConversionFunc)(fInputPicture, fOutputPicture, + fOutputVideoFormat.display.line_width, + fOutputVideoFormat.display.line_count); + } +#ifdef DEBUG + dump_ffframe(fInputPicture, "ffpict"); +// dump_ffframe(fOutputPicture, "opict"); +#endif + *outFrameCount = 1; + fFrame++; + +#if DO_PROFILING + bigtime_t doneTime = system_time(); + decodingTime += formatConversionStart - startTime; + conversionTime += doneTime - formatConversionStart; + profileCounter++; + if (!(fFrame % 10)) { + if (info) { + TRACE("[v] profile: d1 = %lld, d2 = %lld (%Ld) required " + "%Ld\n", + decodingTime / profileCounter, + conversionTime / profileCounter, + fFrame, info->time_to_decode); + } else { + TRACE("[v] profile: d1 = %lld, d2 = %lld (%Ld) required " + "%Ld\n", + decodingTime / profileCounter, + conversionTime / profileCounter, + fFrame, bigtime_t(1000000LL / fOutputFrameRate)); + } + } +#endif + return B_OK; + } else { + TRACE("frame %lld - no picture yet, len: %d, chunk size: %ld\n", + fFrame, len, size); + } + } +} + + diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h index 91eea7c7c9..62fee5daef 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h @@ -32,42 +32,55 @@ public: const void* infoBuffer, size_t infoSize); virtual status_t NegotiateOutputFormat( - media_format* outputFormat); + media_format* inOutFormat); virtual status_t Decode(void* outBuffer, int64* outFrameCount, - media_header* mediaHeader, - media_decode_info* info); + media_header* mediaHeader, + media_decode_info* info); virtual status_t Seek(uint32 seekTo, int64 seekFrame, int64* frame, bigtime_t seekTime, bigtime_t* time); -protected: +private: + status_t _NegotiateAudioOutputFormat( + media_format* inOutFormat); + + status_t _NegotiateVideoOutputFormat( + media_format* inOutFormat); + + status_t _DecodeAudio(void* outBuffer, + int64* outFrameCount, + media_header* mediaHeader, + media_decode_info* info); + + status_t _DecodeVideo(void* outBuffer, + int64* outFrameCount, + media_header* mediaHeader, + media_decode_info* info); + + media_header fHeader; media_decode_info fInfo; - -// friend class avCodecInputStream; - -private: media_format fInputFormat; media_raw_video_format fOutputVideoFormat; int64 fFrame; - bool isAudio; + bool fIsAudio; - int ffcodec_index_in_table; + int fCodecIndexInTable; // helps to find codecpretty - // ffmpeg related datas + // FFmpeg related members AVCodec* fCodec; - AVCodecContext* ffc; - AVFrame* ffpicture; - AVFrame* opicture; + AVCodecContext* fContext; + AVFrame* fInputPicture; + AVFrame* fOutputPicture; bool fCodecInitDone; - gfx_convert_func conv_func; // colorspace convert func + gfx_convert_func fFormatConversionFunc; char* fExtraData; int fExtraDataSize; @@ -76,7 +89,8 @@ private: bigtime_t fStartTime; int32 fOutputFrameCount; float fOutputFrameRate; - int fOutputFrameSize; // sample size * channel count + int fOutputFrameSize; + // sample size * channel count const void* fChunkBuffer; int32 fChunkBufferOffset;