* 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
This commit is contained in:
Stephan Aßmus
2009-07-08 09:43:06 +00:00
parent 0c4d88d353
commit 12a9eb5d93
2 changed files with 507 additions and 418 deletions
@@ -12,12 +12,13 @@
#include "AVCodecDecoder.h" #include "AVCodecDecoder.h"
#include <Debug.h> #include <new>
#include <OS.h>
#include <Bitmap.h>
#include <string.h> #include <string.h>
#define DO_PROFILING 0 #include <Bitmap.h>
#include <Debug.h>
#undef TRACE #undef TRACE
//#define TRACE_AV_CODEC //#define TRACE_AV_CODEC
@@ -27,6 +28,7 @@
# define TRACE(x...) # define TRACE(x...)
#endif #endif
struct wave_format_ex { struct wave_format_ex {
uint16 format_tag; uint16 format_tag;
uint16 channels; uint16 channels;
@@ -38,56 +40,68 @@ struct wave_format_ex {
// extra_data[extra_size] // extra_data[extra_size]
} _PACKED; } _PACKED;
static bigtime_t diff1 = 0, diff2 = 0;
static long prof_cnt = 0;
// uncommenting will make Decode() set the current thread priority to time // profiling related globals
// sharing, so it won't totally freeze if you busy-loop in there (to help debug #define DO_PROFILING 0
// with CD Manager)
//#define UNREAL static bigtime_t decodingTime = 0;
static bigtime_t conversionTime = 0;
static long profileCounter = 0;
AVCodecDecoder::AVCodecDecoder() AVCodecDecoder::AVCodecDecoder()
: fHeader(), :
fHeader(),
fInfo(), fInfo(),
fInputFormat(), fInputFormat(),
fOutputVideoFormat(), fOutputVideoFormat(),
fFrame(0), fFrame(0),
isAudio(false), fIsAudio(false),
fCodecIndexInTable(-1),
fCodec(NULL), fCodec(NULL),
ffc(NULL), fContext(avcodec_alloc_context()),
fInputPicture(avcodec_alloc_frame()),
fOutputPicture(avcodec_alloc_frame()),
fCodecInitDone(false), fCodecInitDone(false),
conv_func(NULL),
fFormatConversionFunc(NULL),
fExtraData(NULL), fExtraData(NULL),
fExtraDataSize(0), fExtraDataSize(0),
fBlockAlign(0), fBlockAlign(0),
fOutputBuffer(0)
fStartTime(0),
fOutputFrameCount(0),
fOutputFrameRate(1.0),
fOutputFrameSize(0),
fOutputBuffer(NULL),
fOutputBufferOffset(0),
fOutputBufferSize(0)
{ {
TRACE("AVCodecDecoder::AVCodecDecoder()\n"); TRACE("AVCodecDecoder::AVCodecDecoder()\n");
ffc = avcodec_alloc_context();
ffpicture = avcodec_alloc_frame();
opicture = avcodec_alloc_frame();
} }
AVCodecDecoder::~AVCodecDecoder() AVCodecDecoder::~AVCodecDecoder()
{ {
TRACE("[%c] AVCodecDecoder::~AVCodecDecoder()\n", isAudio?('a'):('v')); TRACE("[%c] AVCodecDecoder::~AVCodecDecoder()\n", fIsAudio?('a'):('v'));
#ifdef DO_PROFILING #ifdef DO_PROFILING
if (prof_cnt > 0) { if (profileCounter > 0) {
printf("[%c] profile: d1 = %lld, d2 = %lld (%Ld)\n", 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); fFrame);
} }
#endif #endif
if (fCodecInitDone) if (fCodecInitDone)
avcodec_close(ffc); avcodec_close(fContext);
free(opicture); free(fOutputPicture);
free(ffpicture); free(fInputPicture);
free(ffc); free(fContext);
delete [] fExtraData; delete [] fExtraData;
delete [] fOutputBuffer; delete [] fOutputBuffer;
@@ -95,45 +109,47 @@ AVCodecDecoder::~AVCodecDecoder()
void void
AVCodecDecoder::GetCodecInfo(media_codec_info *mci) AVCodecDecoder::GetCodecInfo(media_codec_info* mci)
{ {
sprintf(mci->short_name, "ff:%s", fCodec->name); sprintf(mci->short_name, "ff:%s", fCodec->name);
sprintf(mci->pretty_name, "%s (libavcodec %s)", sprintf(mci->pretty_name, "%s (libavcodec %s)",
gCodecTable[ffcodec_index_in_table].prettyname, fCodec->name); gCodecTable[fCodecIndexInTable].prettyname, fCodec->name);
} }
status_t status_t
AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer, AVCodecDecoder::Setup(media_format* ioEncodedFormat, const void* infoBuffer,
size_t infoSize) size_t infoSize)
{ {
if (ioEncodedFormat->type != B_MEDIA_ENCODED_AUDIO if (ioEncodedFormat->type != B_MEDIA_ENCODED_AUDIO
&& ioEncodedFormat->type != B_MEDIA_ENCODED_VIDEO) && ioEncodedFormat->type != B_MEDIA_ENCODED_VIDEO)
return B_ERROR; return B_ERROR;
isAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO); fIsAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO);
TRACE("[%c] AVCodecDecoder::Setup()\n", isAudio?('a'):('v')); TRACE("[%c] AVCodecDecoder::Setup()\n", fIsAudio?('a'):('v'));
if (isAudio && !fOutputBuffer) if (fIsAudio && !fOutputBuffer)
fOutputBuffer = new char[AVCODEC_MAX_AUDIO_FRAME_SIZE]; fOutputBuffer = new char[AVCODEC_MAX_AUDIO_FRAME_SIZE];
//#if DEBUG #ifdef TRACE_AV_CODEC
char buffer[1024]; char buffer[1024];
string_for_format(*ioEncodedFormat, buffer, sizeof(buffer)); string_for_format(*ioEncodedFormat, buffer, sizeof(buffer));
TRACE("[%c] input_format=%s\n", isAudio?('a'):('v'), buffer); TRACE("[%c] input_format = %s\n", fIsAudio?('a'):('v'), buffer);
TRACE("[%c] infoSize=%ld\n", isAudio?('a'):('v'), infoSize); TRACE("[%c] infoSize = %ld\n", fIsAudio?('a'):('v'), infoSize);
TRACE("[%c] user_data_type=%08lx\n", isAudio?('a'):('v'), ioEncodedFormat->user_data_type); TRACE("[%c] user_data_type = %08lx\n", fIsAudio?('a'):('v'),
TRACE("[%c] meta_data_size=%ld\n", isAudio?('a'):('v'), ioEncodedFormat->MetaDataSize()); ioEncodedFormat->user_data_type);
TRACE("[%c] info_size=%ld\n", isAudio?('a'):('v'), infoSize); TRACE("[%c] meta_data_size = %ld\n", fIsAudio?('a'):('v'),
//#endif ioEncodedFormat->MetaDataSize());
TRACE("[%c] info_size = %ld\n", fIsAudio?('a'):('v'), infoSize);
#endif
media_format_description descr; media_format_description descr;
for (int32 i = 0; gCodecTable[i].id; i++) { for (int32 i = 0; gCodecTable[i].id; i++) {
ffcodec_index_in_table = i; fCodecIndexInTable = i;
uint64 cid; uint64 cid;
if (BMediaFormats().GetCodeFor(*ioEncodedFormat, gCodecTable[i].family, if (BMediaFormats().GetCodeFor(*ioEncodedFormat,
&descr) == B_OK gCodecTable[i].family, &descr) == B_OK
&& gCodecTable[i].type == ioEncodedFormat->type) { && gCodecTable[i].type == ioEncodedFormat->type) {
switch(gCodecTable[i].family) { switch(gCodecTable[i].family) {
case B_WAV_FORMAT_FAMILY: case B_WAV_FORMAT_FAMILY:
@@ -159,21 +175,22 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer,
puts("ERR family"); puts("ERR family");
return B_ERROR; return B_ERROR;
} }
TRACE(" 0x%04lx codec id = \"%c%c%c%c\"\n", uint32(cid), (char)((cid >> 24) & 0xff), TRACE(" 0x%04lx codec id = \"%c%c%c%c\"\n", uint32(cid),
(char)((cid >> 16) & 0xff), (char)((cid >> 8) & 0xff), (char)((cid >> 24) & 0xff), (char)((cid >> 16) & 0xff),
(char)(cid & 0xff)); (char)((cid >> 8) & 0xff), (char)(cid & 0xff));
if (gCodecTable[i].family == descr.family if (gCodecTable[i].family == descr.family
&& gCodecTable[i].fourcc == cid) { && gCodecTable[i].fourcc == cid) {
fCodec = avcodec_find_decoder(gCodecTable[i].id); fCodec = avcodec_find_decoder(gCodecTable[i].id);
if (!fCodec) { if (!fCodec) {
TRACE("AVCodecDecoder: unable to find the correct ffmpeg decoder " TRACE("AVCodecDecoder: unable to find the correct ffmpeg "
"(id = %d)!!!\n",gCodecTable[i].id); "decoder (id = %d)!!!\n",gCodecTable[i].id);
return B_ERROR; return B_ERROR;
} }
TRACE("AVCodecDecoder: found decoder %s\n",fCodec->name); 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 *wfmt_data
= (const wave_format_ex *)infoBuffer; = (const wave_format_ex *)infoBuffer;
size_t wfmt_size = infoSize; size_t wfmt_size = infoSize;
@@ -186,12 +203,16 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer,
} }
} }
} else { } else {
fBlockAlign = ioEncodedFormat->u.encoded_audio.output.buffer_size; fBlockAlign
TRACE("AVCodecDecoder: extra data size %ld\n",infoSize); = ioEncodedFormat->u.encoded_audio.output.buffer_size;
TRACE("AVCodecDecoder: extra data size %ld\n", infoSize);
fExtraDataSize = infoSize; fExtraDataSize = infoSize;
if (fExtraDataSize) { if (fExtraDataSize) {
fExtraData = new char [fExtraDataSize]; fExtraData = new(std::nothrow) char[fExtraDataSize];
if (fExtraData != NULL)
memcpy(fExtraData, infoBuffer, fExtraDataSize); memcpy(fExtraData, infoBuffer, fExtraDataSize);
else
fExtraDataSize = 0;
} }
} }
@@ -206,27 +227,28 @@ AVCodecDecoder::Setup(media_format *ioEncodedFormat, const void *infoBuffer,
status_t status_t
AVCodecDecoder::Seek(uint32 seekTo, AVCodecDecoder::Seek(uint32 seekTo, int64 seekFrame, int64* frame,
int64 seekFrame, int64 *frame, bigtime_t seekTime, bigtime_t* time)
bigtime_t seekTime, bigtime_t *time)
{ {
// reset the ffmpeg codec // Reset the FFmpeg codec to flush buffers, so we keep the sync
// to flush buffers, so we keep the sync // TODO: Maybe that should be done for video codecs, too?
if (isAudio && fCodecInitDone) { if (fIsAudio && fCodecInitDone) {
fCodecInitDone = false; fCodecInitDone = false;
avcodec_close(ffc); avcodec_close(fContext);
fCodecInitDone = (avcodec_open(ffc, fCodec) >= 0); fCodecInitDone = (avcodec_open(fContext, fCodec) >= 0);
} }
if (seekTo == B_MEDIA_SEEK_TO_TIME) { if (seekTo == B_MEDIA_SEEK_TO_TIME) {
TRACE("AVCodecDecoder::Seek by 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); *frame = (int64)(seekTime * fOutputFrameRate / 1000000LL);
*time = seekTime; *time = seekTime;
} else if (seekTo == B_MEDIA_SEEK_TO_FRAME) { } else if (seekTo == B_MEDIA_SEEK_TO_FRAME) {
TRACE("AVCodecDecoder::Seek by 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); *time = (bigtime_t)(seekFrame * 1000000LL / fOutputFrameRate);
*frame = seekFrame; *frame = seekFrame;
@@ -241,19 +263,56 @@ AVCodecDecoder::Seek(uint32 seekTo,
status_t 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 TRACE_AV_CODEC
#if DEBUG
char buffer[1024]; char buffer[1024];
string_for_format(*inout_format, buffer, sizeof(buffer)); string_for_format(*inOutFormat, buffer, sizeof(buffer));
TRACE("[%c] in_format=%s\n", isAudio?('a'):('v'), buffer); TRACE(" [%c] requested format = %s\n", fIsAudio?('a'):('v'), buffer);
#endif #endif
if (isAudio) { if (fIsAudio)
return _NegotiateAudioOutputFormat(inOutFormat);
else
return _NegotiateVideoOutputFormat(inOutFormat);
}
status_t
AVCodecDecoder::Decode(void* outBuffer, int64* outFrameCount,
media_header* mediaHeader, media_decode_info* info)
{
if (!fCodecInitDone)
return B_NO_INIT;
// TRACE("[%c] AVCodecDecoder::Decode() for time %Ld\n", fIsAudio?('a'):('v'),
// fStartTime);
mediaHeader->start_time = fStartTime;
status_t ret;
if (fIsAudio)
ret = _DecodeAudio(outBuffer, outFrameCount, mediaHeader, info);
else
ret = _DecodeVideo(outBuffer, outFrameCount, mediaHeader, info);
fStartTime = (bigtime_t)(1000000LL * fFrame / fOutputFrameRate);
return ret;
}
// #pragma mark -
status_t
AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
{
TRACE("AVCodecDecoder::_NegotiateAudioOutputFormat()\n");
media_multi_audio_format outputAudioFormat; media_multi_audio_format outputAudioFormat;
outputAudioFormat = media_raw_audio_format::wildcard; outputAudioFormat = media_raw_audio_format::wildcard;
outputAudioFormat.format = media_raw_audio_format::B_AUDIO_SHORT; outputAudioFormat.format = media_raw_audio_format::B_AUDIO_SHORT;
@@ -264,32 +323,34 @@ AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format)
= fInputFormat.u.encoded_audio.output.channel_count; = fInputFormat.u.encoded_audio.output.channel_count;
outputAudioFormat.buffer_size outputAudioFormat.buffer_size
= 1024 * fInputFormat.u.encoded_audio.output.channel_count; = 1024 * fInputFormat.u.encoded_audio.output.channel_count;
inout_format->type = B_MEDIA_RAW_AUDIO; inOutFormat->type = B_MEDIA_RAW_AUDIO;
inout_format->u.raw_audio = outputAudioFormat; inOutFormat->u.raw_audio = outputAudioFormat;
ffc->bit_rate = (int) fInputFormat.u.encoded_audio.bit_rate; fContext->bit_rate = (int)fInputFormat.u.encoded_audio.bit_rate;
ffc->sample_rate = (int) fInputFormat.u.encoded_audio.output.frame_rate; fContext->sample_rate
ffc->channels = fInputFormat.u.encoded_audio.output.channel_count; = (int)fInputFormat.u.encoded_audio.output.frame_rate;
ffc->block_align = fBlockAlign; fContext->channels = fInputFormat.u.encoded_audio.output.channel_count;
ffc->extradata = (uint8_t *)fExtraData; fContext->block_align = fBlockAlign;
ffc->extradata_size = fExtraDataSize; fContext->extradata = (uint8_t*)fExtraData;
fContext->extradata_size = fExtraDataSize;
TRACE("bit_rate %d, sample_rate %d, channels %d, block_align %d, " TRACE("bit_rate %d, sample_rate %d, channels %d, block_align %d, "
"extradata_size %d\n", ffc->bit_rate, ffc->sample_rate, "extradata_size %d\n", fContext->bit_rate, fContext->sample_rate,
ffc->channels, ffc->block_align, ffc->extradata_size); fContext->channels, fContext->block_align, fContext->extradata_size);
// close any previous instance // close any previous instance
if (fCodecInitDone) { if (fCodecInitDone) {
fCodecInitDone = false; fCodecInitDone = false;
avcodec_close(ffc); avcodec_close(fContext);
} }
// open new // open new
result = avcodec_open(ffc, fCodec); int result = avcodec_open(fContext, fCodec);
fCodecInitDone = (result >= 0); fCodecInitDone = (result >= 0);
TRACE("audio: bit_rate = %d, sample_rate = %d, chans = %d Init = %d\n", TRACE("audio: bit_rate = %d, sample_rate = %d, chans = %d Init = %d\n",
ffc->bit_rate, ffc->sample_rate, ffc->channels, result); fContext->bit_rate, fContext->sample_rate, fContext->channels,
result);
fStartTime = 0; fStartTime = 0;
fOutputFrameSize = 2 * outputAudioFormat.channel_count; fOutputFrameSize = 2 * outputAudioFormat.channel_count;
@@ -301,8 +362,8 @@ AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format)
fOutputBufferOffset = 0; fOutputBufferOffset = 0;
fOutputBufferSize = 0; fOutputBufferSize = 0;
inout_format->require_flags = 0; inOutFormat->require_flags = 0;
inout_format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
if (!fCodecInitDone) { if (!fCodecInitDone) {
TRACE("avcodec_open() failed!\n"); TRACE("avcodec_open() failed!\n");
@@ -310,56 +371,63 @@ AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format)
} }
return B_OK; return B_OK;
}
} else { // VIDEO
status_t
AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
{
TRACE("AVCodecDecoder::_NegotiateVideoOutputFormat()\n");
fOutputVideoFormat = fInputFormat.u.encoded_video.output; fOutputVideoFormat = fInputFormat.u.encoded_video.output;
ffc->width = fOutputVideoFormat.display.line_width; fContext->width = fOutputVideoFormat.display.line_width;
ffc->height = fOutputVideoFormat.display.line_count; fContext->height = fOutputVideoFormat.display.line_count;
// ffc->frame_rate = (int)(fOutputVideoFormat.field_rate // fContext->frame_rate = (int)(fOutputVideoFormat.field_rate
// * ffc->frame_rate_base); // * fContext->frame_rate_base);
fOutputFrameRate = fOutputVideoFormat.field_rate; fOutputFrameRate = fOutputVideoFormat.field_rate;
ffc->extradata = (uint8_t *)fExtraData; fContext->extradata = (uint8_t *)fExtraData;
ffc->extradata_size = fExtraDataSize; fContext->extradata_size = fExtraDataSize;
// if (fInputFormat.MetaDataSize() > 0) { // if (fInputFormat.MetaDataSize() > 0) {
// ffc->extradata = (uint8_t *)fInputFormat.MetaData(); // fContext->extradata = (uint8_t *)fInputFormat.MetaData();
// ffc->extradata_size = fInputFormat.MetaDataSize(); // fContext->extradata_size = fInputFormat.MetaDataSize();
// } // }
TRACE("#### requested video format 0x%x\n", TRACE(" requested video format 0x%x\n",
inout_format->u.raw_video.display.format); inOutFormat->u.raw_video.display.format);
// make MediaPlayer happy (if not in rgb32 screen depth and no overlay, // 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 // it will only ask for YCbCr, which DrawBitmap doesn't handle, so the
// default colordepth is RGB32) // default colordepth is RGB32).
if (inout_format->u.raw_video.display.format == B_YCbCr422) if (inOutFormat->u.raw_video.display.format == B_YCbCr422)
fOutputVideoFormat.display.format = B_YCbCr422; fOutputVideoFormat.display.format = B_YCbCr422;
else else
fOutputVideoFormat.display.format = B_RGB32; fOutputVideoFormat.display.format = B_RGB32;
// search for a pixel-format the codec handles // Search for a pixel-format the codec handles
// XXX We should try this a couple of times until it succeeds, each time // TODO: We should try this a couple of times until it succeeds, each
// XXX using another format pixel-format that is supported by the // time using another pixel-format that is supported by the decoder.
// XXX decoder. But libavcodec doesn't seem to offer any way to tell the // But libavcodec doesn't seem to offer any way to tell the decoder
// XXX decoder which format it should use. // which format it should use.
conv_func = 0; fFormatConversionFunc = 0;
for (int i = 0; i < 1; i++) { // iterate over supported codec formats // Iterate over supported codec formats
for (int i = 0; i < 1; i++) {
// close any previous instance // close any previous instance
if (fCodecInitDone) { if (fCodecInitDone) {
fCodecInitDone = false; fCodecInitDone = false;
avcodec_close(ffc); avcodec_close(fContext);
} }
// XXX set n-th ffc->pix_fmt here // TODO: Set n-th fContext->pix_fmt here
if (avcodec_open(ffc, fCodec) >= 0) { if (avcodec_open(fContext, fCodec) >= 0) {
fCodecInitDone = true; fCodecInitDone = true;
conv_func = resolve_colorspace(fOutputVideoFormat.display.format, ffc->pix_fmt); fFormatConversionFunc = resolve_colorspace(
fOutputVideoFormat.display.format, fContext->pix_fmt);
} }
if (conv_func != 0) if (fFormatConversionFunc != NULL)
break; break;
} }
@@ -368,8 +436,9 @@ AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format)
return B_ERROR; return B_ERROR;
} }
if (!conv_func) { if (fFormatConversionFunc == NULL) {
TRACE("no conv_func found or decoder has not set the pixel format yet!\n"); TRACE("no pixel format conversion function found or decoder has "
"not set the pixel format yet!\n");
} }
if (fOutputVideoFormat.display.format == B_YCbCr422) { if (fOutputVideoFormat.display.format == B_YCbCr422) {
@@ -380,53 +449,33 @@ AVCodecDecoder::NegotiateOutputFormat(media_format *inout_format)
= 4 * fOutputVideoFormat.display.line_width; = 4 * fOutputVideoFormat.display.line_width;
} }
inout_format->type = B_MEDIA_RAW_VIDEO; inOutFormat->type = B_MEDIA_RAW_VIDEO;
inout_format->u.raw_video = fOutputVideoFormat; inOutFormat->u.raw_video = fOutputVideoFormat;
inout_format->require_flags = 0; inOutFormat->require_flags = 0;
inout_format->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
#if DEBUG #if TRACE_AV_CODEC
string_for_format(*inout_format, buffer, sizeof(buffer)); char buffer[1024];
TRACE("[%c] out_format=%s\n", isAudio?('a'):('v'), buffer); string_for_format(*inOutFormat, buffer, sizeof(buffer));
#endif TRACE("[v] outFormat = %s\n", buffer);
TRACE(" returned video format 0x%x\n",
TRACE("#### returned video format 0x%x\n", inOutFormat->u.raw_video.display.format);
inout_format->u.raw_video.display.format); #endif
return B_OK; return B_OK;
}
} }
status_t status_t
AVCodecDecoder::Decode(void *out_buffer, int64 *out_frameCount, AVCodecDecoder::_DecodeAudio(void* outBuffer, int64* outFrameCount,
media_header *mh, media_decode_info *info) media_header* mediaHeader, media_decode_info* info)
{ {
const void *data; // TRACE("audio start_time %.6f\n", mediaHeader->start_time / 1000000.0);
if (!fCodecInitDone) char* output_buffer = (char*)outBuffer;
return B_BAD_VALUE; *outFrameCount = 0;
while (*outFrameCount < fOutputFrameCount) {
#ifdef DO_PROFILING
bigtime_t prof_t1, prof_t2, prof_t3;
#endif
#ifdef UNREAL
set_thread_priority(find_thread(NULL), B_NORMAL_PRIORITY);
#endif
// TRACE("[%c] AVCodecDecoder::Decode() for time %Ld\n", isAudio?('a'):('v'), fStartTime);
mh->start_time = fStartTime;
if (isAudio) {
// 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) { if (fOutputBufferSize < 0) {
TRACE("############ fOutputBufferSize %ld\n", TRACE("############ fOutputBufferSize %ld\n",
fOutputBufferSize); fOutputBufferSize);
@@ -439,21 +488,21 @@ AVCodecDecoder::Decode(void *out_buffer, int64 *out_frameCount,
} }
if (fOutputBufferSize > 0) { if (fOutputBufferSize > 0) {
int32 frames = min_c(fOutputFrameCount - *out_frameCount, int32 frames = min_c(fOutputFrameCount - *outFrameCount,
fOutputBufferSize / fOutputFrameSize); fOutputBufferSize / fOutputFrameSize);
memcpy(output_buffer, fOutputBuffer + fOutputBufferOffset, memcpy(output_buffer, fOutputBuffer + fOutputBufferOffset,
frames * fOutputFrameSize); frames * fOutputFrameSize);
fOutputBufferOffset += frames * fOutputFrameSize; fOutputBufferOffset += frames * fOutputFrameSize;
fOutputBufferSize -= frames * fOutputFrameSize; fOutputBufferSize -= frames * fOutputFrameSize;
output_buffer += frames * fOutputFrameSize; output_buffer += frames * fOutputFrameSize;
*out_frameCount += frames; *outFrameCount += frames;
fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate); fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate);
continue; continue;
} }
if (fChunkBufferSize == 0) { if (fChunkBufferSize == 0) {
media_header chunk_mh; media_header chunkMediaHeader;
status_t err; status_t err;
err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, &chunk_mh); err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, &chunkMediaHeader);
if (err == B_LAST_BUFFER_ERROR) { if (err == B_LAST_BUFFER_ERROR) {
TRACE("Last Chunk with chunk size %ld\n",fChunkBufferSize); TRACE("Last Chunk with chunk size %ld\n",fChunkBufferSize);
fChunkBufferSize = 0; fChunkBufferSize = 0;
@@ -465,15 +514,15 @@ AVCodecDecoder::Decode(void *out_buffer, int64 *out_frameCount,
break; break;
} }
fChunkBufferOffset = 0; fChunkBufferOffset = 0;
fStartTime = chunk_mh.start_time; fStartTime = chunkMediaHeader.start_time;
if (*out_frameCount == 0) if (*outFrameCount == 0)
mh->start_time = chunk_mh.start_time; mediaHeader->start_time = chunkMediaHeader.start_time;
continue; continue;
} }
if (fOutputBufferSize == 0) { if (fOutputBufferSize == 0) {
int len; int len;
int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
len = avcodec_decode_audio2(ffc, (short *)fOutputBuffer, len = avcodec_decode_audio2(fContext, (short *)fOutputBuffer,
&out_size, (uint8_t*)fChunkBuffer + fChunkBufferOffset, &out_size, (uint8_t*)fChunkBuffer + fChunkBufferOffset,
fChunkBufferSize); fChunkBufferSize);
if (len < 0) { if (len < 0) {
@@ -493,111 +542,137 @@ AVCodecDecoder::Decode(void *out_buffer, int64 *out_frameCount,
fOutputBufferSize = out_size; fOutputBufferSize = out_size;
} }
} }
fFrame += *out_frameCount; fFrame += *outFrameCount;
// TRACE("Played %Ld frames at time %Ld\n",*out_frameCount, mh->start_time); // TRACE("Played %Ld frames at time %Ld\n",*outFrameCount, mediaHeader->start_time);
return B_OK;
}
} else { // Video
media_header chunk_mh; status_t
status_t err; AVCodecDecoder::_DecodeVideo(void* outBuffer, int64* outFrameCount,
media_header* mediaHeader, media_decode_info* info)
{
bool firstRun = true;
while (true) {
const void* data;
size_t size; size_t size;
media_header chunkMediaHeader;
err = GetNextChunk(&data, &size, &chunk_mh); status_t err = GetNextChunk(&data, &size, &chunkMediaHeader);
if (err != B_OK) { if (err != B_OK) {
TRACE("AVCodecDecoder::Decode(): error 0x%08lx from GetNextChunk()\n", err); TRACE("AVCodecDecoder::_DecodeVideo(): error from "
"GetNextChunk(): %s\n", strerror(err));
return err; return err;
} }
if (firstRun) {
firstRun = false;
mh->type = B_MEDIA_RAW_VIDEO; mediaHeader->type = B_MEDIA_RAW_VIDEO;
// mh->start_time = chunk_mh.start_time; // mediaHeader->start_time = chunkMediaHeader.start_time;
mh->file_pos = 0; mediaHeader->file_pos = 0;
mh->orig_size = 0; mediaHeader->orig_size = 0;
mh->u.raw_video.field_gamma = 1.0; mediaHeader->u.raw_video.field_gamma = 1.0;
mh->u.raw_video.field_sequence = fFrame; mediaHeader->u.raw_video.field_sequence = fFrame;
mh->u.raw_video.field_number = 0; mediaHeader->u.raw_video.field_number = 0;
mh->u.raw_video.pulldown_number = 0; mediaHeader->u.raw_video.pulldown_number = 0;
mh->u.raw_video.first_active_line = 1; mediaHeader->u.raw_video.first_active_line = 1;
mh->u.raw_video.line_count = fOutputVideoFormat.display.line_count; mediaHeader->u.raw_video.line_count
= fOutputVideoFormat.display.line_count;
TRACE("[%c] start_time=%02d:%02d.%02d field_sequence=%lu\n", TRACE("[v] start_time=%02d:%02d.%02d field_sequence=%lu\n",
isAudio ? ('a') : ('v'), int((mediaHeader->start_time / 60000000) % 60),
int((mh->start_time / 60000000) % 60), int((mediaHeader->start_time / 1000000) % 60),
int((mh->start_time / 1000000) % 60), int((mediaHeader->start_time / 10000) % 100),
int((mh->start_time / 10000) % 100), mediaHeader->u.raw_video.field_sequence);
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) { #if DO_PROFILING
#ifdef DO_PROFILING bigtime_t startTime = system_time();
prof_t2 = 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 #endif
// TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size, // TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size,
// pixfmt_to_string(ffc->pix_fmt)); // pixfmt_to_string(fContext->pix_fmt));
// Some decoders do not set pix_fmt until they have decoded 1 frame // Some decoders do not set pix_fmt until they have decoded 1 frame
if (conv_func == 0) { if (fFormatConversionFunc == NULL) {
conv_func = resolve_colorspace(fOutputVideoFormat.display.format, ffc->pix_fmt); fFormatConversionFunc = resolve_colorspace(
fOutputVideoFormat.display.format, fContext->pix_fmt);
} }
opicture->data[0] = (uint8_t *)out_buffer; fOutputPicture->data[0] = (uint8_t*)outBuffer;
opicture->linesize[0] = fOutputVideoFormat.display.bytes_per_row; fOutputPicture->linesize[0]
= fOutputVideoFormat.display.bytes_per_row;
if (conv_func) { if (fFormatConversionFunc != NULL) {
(*conv_func)(ffpicture, opicture, (*fFormatConversionFunc)(fInputPicture, fOutputPicture,
fOutputVideoFormat.display.line_width, fOutputVideoFormat.display.line_width,
fOutputVideoFormat.display.line_count); fOutputVideoFormat.display.line_count);
} }
#ifdef DEBUG #ifdef DEBUG
dump_ffframe(ffpicture, "ffpict"); dump_ffframe(fInputPicture, "ffpict");
// dump_ffframe(opicture, "opict"); // dump_ffframe(fOutputPicture, "opict");
#endif #endif
*out_frameCount = 1; *outFrameCount = 1;
fFrame++; fFrame++;
#ifdef DO_PROFILING #if DO_PROFILING
prof_t3 = system_time(); bigtime_t doneTime = system_time();
diff1 += prof_t2 - prof_t1; decodingTime += formatConversionStart - startTime;
diff2 += prof_t3 - prof_t2; conversionTime += doneTime - formatConversionStart;
prof_cnt++; profileCounter++;
if (!(fFrame % 10)) { if (!(fFrame % 10)) {
if (info) { if (info) {
TRACE("[%c] profile: d1 = %lld, d2 = %lld (%Ld) required %Ld\n", TRACE("[v] profile: d1 = %lld, d2 = %lld (%Ld) required "
isAudio?('a'):('v'), diff1/prof_cnt, diff2/prof_cnt, "%Ld\n",
decodingTime / profileCounter,
conversionTime / profileCounter,
fFrame, info->time_to_decode); fFrame, info->time_to_decode);
} else { } else {
TRACE("[%c] profile: d1 = %lld, d2 = %lld (%Ld) required %Ld\n", TRACE("[v] profile: d1 = %lld, d2 = %lld (%Ld) required "
isAudio?('a'):('v'), diff1/prof_cnt, diff2/prof_cnt, "%Ld\n",
decodingTime / profileCounter,
conversionTime / profileCounter,
fFrame, bigtime_t(1000000LL / fOutputFrameRate)); fFrame, bigtime_t(1000000LL / fOutputFrameRate));
} }
} }
#endif #endif
}
}
fStartTime = (bigtime_t) (1000000LL * fFrame / fOutputFrameRate);
return B_OK; return B_OK;
} else {
TRACE("frame %lld - no picture yet, len: %d, chunk size: %ld\n",
fFrame, len, size);
}
}
} }
@@ -32,7 +32,7 @@ public:
const void* infoBuffer, size_t infoSize); const void* infoBuffer, size_t infoSize);
virtual status_t NegotiateOutputFormat( virtual status_t NegotiateOutputFormat(
media_format* outputFormat); media_format* inOutFormat);
virtual status_t Decode(void* outBuffer, int64* outFrameCount, virtual status_t Decode(void* outBuffer, int64* outFrameCount,
media_header* mediaHeader, media_header* mediaHeader,
@@ -43,31 +43,44 @@ public:
bigtime_t* time); 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_header fHeader;
media_decode_info fInfo; media_decode_info fInfo;
// friend class avCodecInputStream;
private:
media_format fInputFormat; media_format fInputFormat;
media_raw_video_format fOutputVideoFormat; media_raw_video_format fOutputVideoFormat;
int64 fFrame; int64 fFrame;
bool isAudio; bool fIsAudio;
int ffcodec_index_in_table; int fCodecIndexInTable;
// helps to find codecpretty // helps to find codecpretty
// ffmpeg related datas // FFmpeg related members
AVCodec* fCodec; AVCodec* fCodec;
AVCodecContext* ffc; AVCodecContext* fContext;
AVFrame* ffpicture; AVFrame* fInputPicture;
AVFrame* opicture; AVFrame* fOutputPicture;
bool fCodecInitDone; bool fCodecInitDone;
gfx_convert_func conv_func; // colorspace convert func gfx_convert_func fFormatConversionFunc;
char* fExtraData; char* fExtraData;
int fExtraDataSize; int fExtraDataSize;
@@ -76,7 +89,8 @@ private:
bigtime_t fStartTime; bigtime_t fStartTime;
int32 fOutputFrameCount; int32 fOutputFrameCount;
float fOutputFrameRate; float fOutputFrameRate;
int fOutputFrameSize; // sample size * channel count int fOutputFrameSize;
// sample size * channel count
const void* fChunkBuffer; const void* fChunkBuffer;
int32 fChunkBufferOffset; int32 fChunkBufferOffset;