#include "AVCodecDecoder.h"
#include <new>
#include <assert.h>
#include <string.h>
#include <Bitmap.h>
#include <Debug.h>
#include <OS.h>
#include <String.h>
#include "Utilities.h"
#undef TRACE
#ifdef TRACE_AV_CODEC
# define TRACE(x...) printf(x)
# define TRACE_AUDIO(x...) printf(x)
# define TRACE_VIDEO(x...) printf(x)
#else
# define TRACE(x...)
# define TRACE_AUDIO(x...)
# define TRACE_VIDEO(x...)
#endif
#ifdef LOG_STREAM_TO_FILE
# include <File.h>
static BFile sAudioStreamLogFile(
"/boot/home/Desktop/AVCodecDebugAudioStream.raw",
B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
static BFile sVideoStreamLogFile(
"/boot/home/Desktop/AVCodecDebugVideoStream.raw",
B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
static int sDumpedPackets = 0;
#endif
typedef AVCodecID CodecID;
struct wave_format_ex {
uint16 format_tag;
uint16 channels;
uint32 frames_per_sec;
uint32 avg_bytes_per_sec;
uint16 block_align;
uint16 bits_per_sample;
uint16 extra_size;
} _PACKED;
struct avformat_codec_context {
int sample_rate;
int channels;
};
#define DO_PROFILING 0
#if DO_PROFILING
static bigtime_t decodingTime = 0;
static bigtime_t conversionTime = 0;
static long profileCounter = 0;
#endif
AVCodecDecoder::AVCodecDecoder()
:
fHeader(),
fInputFormat(),
fFrame(0),
fIsAudio(false),
fCodec(NULL),
fCodecContext(NULL),
fResampleContext(NULL),
fDecodedData(NULL),
fDecodedDataSizeInBytes(0),
fPostProcessedDecodedPicture(av_frame_alloc()),
fRawDecodedPicture(av_frame_alloc()),
fRawDecodedAudio(av_frame_alloc()),
fCodecInitDone(false),
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
fSwsContext(NULL),
#else
fFormatConversionFunc(NULL),
#endif
fExtraData(NULL),
fExtraDataSize(0),
fBlockAlign(0),
fOutputColorSpace(B_NO_COLOR_SPACE),
fOutputFrameCount(0),
fOutputFrameRate(1.0),
fOutputFrameSize(0),
fInputFrameSize(0),
fChunkBuffer(NULL),
fChunkBufferSize(0),
fAudioDecodeError(false),
fDecodedDataBuffer(av_frame_alloc()),
fDecodedDataBufferOffset(0),
fDecodedDataBufferSize(0),
fTempPacket(NULL),
fBufferSinkContext(NULL),
fBufferSourceContext(NULL),
fFilterGraph(NULL),
fFilterFrame(NULL)
{
TRACE("AVCodecDecoder::AVCodecDecoder()\n");
}
AVCodecDecoder::~AVCodecDecoder()
{
TRACE("[%c] AVCodecDecoder::~AVCodecDecoder()\n", fIsAudio ? 'a' : 'v');
#if DO_PROFILING
if (profileCounter > 0) {
printf("[%c] profile: d1 = %lld, d2 = %lld (%lld)\n",
fIsAudio ? 'a' : 'v', decodingTime / profileCounter,
conversionTime / profileCounter, fFrame);
}
#endif
swr_free(&fResampleContext);
free(fChunkBuffer);
free(fDecodedData);
av_frame_free(&fPostProcessedDecodedPicture);
av_frame_free(&fRawDecodedPicture);
av_free(fRawDecodedAudio->opaque);
av_frame_free(&fRawDecodedAudio);
if (fCodecContext != NULL) {
fCodecContext->extradata = NULL;
avcodec_free_context(&fCodecContext);
}
av_frame_free(&fDecodedDataBuffer);
av_frame_free(&fFilterFrame);
avfilter_graph_free(&fFilterGraph);
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
if (fSwsContext != NULL)
sws_freeContext(fSwsContext);
#endif
delete[] fExtraData;
av_packet_free(&fTempPacket);
}
void
AVCodecDecoder::GetCodecInfo(media_codec_info* mci)
{
snprintf(mci->short_name, 32, "%s", fCodec->name);
snprintf(mci->pretty_name, 96, "%s", fCodec->long_name);
mci->id = 0;
mci->sub_id = fCodec->id;
}
status_t
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;
fIsAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO);
TRACE("[%c] AVCodecDecoder::Setup()\n", fIsAudio ? 'a' : 'v');
#ifdef TRACE_AV_CODEC
char buffer[1024];
string_for_format(*ioEncodedFormat, buffer, sizeof(buffer));
TRACE("[%c] input_format = %s\n", fIsAudio ? 'a' : 'v', buffer);
TRACE("[%c] infoSize = %ld\n", fIsAudio ? 'a' : 'v', infoSize);
TRACE("[%c] user_data_type = %08" B_PRIx32 "\n", fIsAudio ? 'a' : 'v',
ioEncodedFormat->user_data_type);
TRACE("[%c] meta_data_size = %" B_PRId32 "\n", fIsAudio ? 'a' : 'v',
ioEncodedFormat->MetaDataSize());
#endif
media_format_description description;
if (BMediaFormats().GetCodeFor(*ioEncodedFormat,
B_MISC_FORMAT_FAMILY, &description) == B_OK) {
if (description.u.misc.file_format != 'ffmp')
return B_NOT_SUPPORTED;
fCodec = avcodec_find_decoder(static_cast<CodecID>(
description.u.misc.codec));
if (fCodec == NULL) {
TRACE(" unable to find the correct FFmpeg decoder (id = %" B_PRIu32 ")\n",
description.u.misc.codec);
return B_ERROR;
}
TRACE(" found decoder %s\n", fCodec->name);
const void* extraData = infoBuffer;
fExtraDataSize = infoSize;
if (description.family == B_WAV_FORMAT_FAMILY
&& infoSize >= sizeof(wave_format_ex)) {
TRACE(" trying to use wave_format_ex\n");
const wave_format_ex* waveFormatData
= (const wave_format_ex*)infoBuffer;
size_t waveFormatSize = infoSize;
if (waveFormatData != NULL && waveFormatSize > 0) {
fBlockAlign = waveFormatData->block_align;
TRACE(" found block align: %d\n", fBlockAlign);
fExtraDataSize = waveFormatData->extra_size;
extraData = waveFormatData + 1;
}
} else {
if (fIsAudio) {
fBlockAlign
= ioEncodedFormat->u.encoded_audio.output.buffer_size;
TRACE(" using buffer_size as block align: %d\n",
fBlockAlign);
}
}
if (extraData != NULL && fExtraDataSize > 0) {
TRACE("AVCodecDecoder: extra data size %ld\n", infoSize);
delete[] fExtraData;
fExtraData = new(std::nothrow) char[fExtraDataSize];
if (fExtraData != NULL)
memcpy(fExtraData, infoBuffer, fExtraDataSize);
else
fExtraDataSize = 0;
}
fInputFormat = *ioEncodedFormat;
return B_OK;
} else {
TRACE("AVCodecDecoder: BMediaFormats().GetCodeFor() failed.\n");
}
printf("AVCodecDecoder::Setup failed!\n");
return B_ERROR;
}
status_t
AVCodecDecoder::SeekedTo(int64 frame, bigtime_t time)
{
status_t ret = B_OK;
if (fCodecInitDone) {
avcodec_flush_buffers(fCodecContext);
_ResetTempPacket();
}
free(fChunkBuffer);
fChunkBuffer = NULL;
fChunkBufferSize = 0;
fDecodedDataBufferOffset = 0;
fDecodedDataBufferSize = 0;
fDecodedDataSizeInBytes = 0;
fFrame = frame;
return ret;
}
status_t
AVCodecDecoder::NegotiateOutputFormat(media_format* inOutFormat)
{
TRACE("AVCodecDecoder::NegotiateOutputFormat() [%c] \n",
fIsAudio ? 'a' : 'v');
#ifdef TRACE_AV_CODEC
char buffer[1024];
string_for_format(*inOutFormat, buffer, sizeof(buffer));
TRACE(" [%c] requested format = %s\n", fIsAudio ? 'a' : 'v', buffer);
#endif
if (fCodecContext != NULL) {
fCodecContext->extradata = NULL;
avcodec_free_context(&fCodecContext);
}
fCodecContext = avcodec_alloc_context3(fCodec);
fCodecInitDone = false;
system_info info;
get_system_info(&info);
fCodecContext->err_recognition = AV_EF_CAREFUL;
fCodecContext->error_concealment = 3;
fCodecContext->thread_count = info.cpu_count;
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;
status_t ret;
if (fIsAudio)
ret = _DecodeAudio(outBuffer, outFrameCount, mediaHeader, info);
else
ret = _DecodeVideo(outBuffer, outFrameCount, mediaHeader, info);
return ret;
}
void
AVCodecDecoder::_ResetTempPacket()
{
if (fTempPacket == NULL)
fTempPacket = av_packet_alloc();
fTempPacket->size = 0;
fTempPacket->data = NULL;
}
static int
get_channel_count(AVCodecContext* context)
{
return context->ch_layout.nb_channels;
}
static void
set_channel_count(AVCodecContext* context, int count)
{
context->ch_layout.nb_channels = count;
}
status_t
AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
{
TRACE("AVCodecDecoder::_NegotiateAudioOutputFormat()\n");
_ApplyEssentialAudioContainerPropertiesToContext();
if (avcodec_open2(fCodecContext, fCodec, NULL) < 0) {
TRACE("avcodec_open() failed to init codec!\n");
return B_ERROR;
}
fCodecInitDone = true;
free(fChunkBuffer);
fChunkBuffer = NULL;
fChunkBufferSize = 0;
fAudioDecodeError = false;
fDecodedDataBufferOffset = 0;
fDecodedDataBufferSize = 0;
_ResetTempPacket();
status_t statusOfDecodingFirstFrameChunk = _DecodeNextAudioFrameChunk();
if (statusOfDecodingFirstFrameChunk != B_OK) {
TRACE("[a] decoding first audio frame chunk failed\n");
return B_ERROR;
}
media_multi_audio_format outputAudioFormat;
outputAudioFormat = media_raw_audio_format::wildcard;
outputAudioFormat.byte_order = B_MEDIA_HOST_ENDIAN;
outputAudioFormat.frame_rate = fCodecContext->sample_rate;
outputAudioFormat.channel_count = get_channel_count(fCodecContext);
ConvertAVSampleFormatToRawAudioFormat(fCodecContext->sample_fmt,
outputAudioFormat.format);
if (outputAudioFormat.format == 0) {
TRACE(" format still a wild-card, assuming B_AUDIO_SHORT.\n");
outputAudioFormat.format = media_raw_audio_format::B_AUDIO_SHORT;
}
outputAudioFormat.buffer_size = inOutFormat->u.raw_audio.buffer_size;
size_t sampleSize = outputAudioFormat.format
& media_raw_audio_format::B_AUDIO_SIZE_MASK;
if (outputAudioFormat.buffer_size == 0) {
outputAudioFormat.buffer_size = 512 * sampleSize
* outputAudioFormat.channel_count;
}
inOutFormat->type = B_MEDIA_RAW_AUDIO;
inOutFormat->u.raw_audio = outputAudioFormat;
inOutFormat->require_flags = 0;
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
fOutputFrameSize = sampleSize * outputAudioFormat.channel_count;
fOutputFrameCount = outputAudioFormat.buffer_size / fOutputFrameSize;
fOutputFrameRate = outputAudioFormat.frame_rate;
if (av_sample_fmt_is_planar(fCodecContext->sample_fmt))
fInputFrameSize = sampleSize;
else
fInputFrameSize = fOutputFrameSize;
fRawDecodedAudio->opaque
= av_realloc(fRawDecodedAudio->opaque, sizeof(avformat_codec_context));
if (fRawDecodedAudio->opaque == NULL)
return B_NO_MEMORY;
if (av_sample_fmt_is_planar(fCodecContext->sample_fmt)) {
fResampleContext = NULL;
swr_alloc_set_opts2(&fResampleContext,
&fCodecContext->ch_layout,
fCodecContext->request_sample_fmt,
fCodecContext->sample_rate,
&fCodecContext->ch_layout,
fCodecContext->sample_fmt,
fCodecContext->sample_rate,
0, NULL);
swr_init(fResampleContext);
}
TRACE(" bit_rate = %" PRId64 ", sample_rate = %d, channels = %d, "
"output frame size: %d, count: %" B_PRId32 ", rate: %.2f\n",
fCodecContext->bit_rate, fCodecContext->sample_rate, fCodecContext->ch_layout.nb_channels,
fOutputFrameSize, fOutputFrameCount, fOutputFrameRate);
return B_OK;
}
status_t
AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
{
TRACE("AVCodecDecoder::_NegotiateVideoOutputFormat()\n");
TRACE(" requested video format 0x%x\n",
inOutFormat->u.raw_video.display.format);
_ApplyEssentialVideoContainerPropertiesToContext();
if (avcodec_open2(fCodecContext, fCodec, NULL) < 0) {
TRACE("avcodec_open() failed to init codec!\n");
return B_ERROR;
}
fCodecInitDone = true;
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
fOutputColorSpace = B_RGB32;
#else
if (inOutFormat->u.raw_video.display.format == B_YCbCr422)
fOutputColorSpace = B_YCbCr422;
else
fOutputColorSpace = B_RGB32;
#endif
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
if (fSwsContext != NULL)
sws_freeContext(fSwsContext);
fSwsContext = NULL;
#else
fFormatConversionFunc = 0;
#endif
free(fChunkBuffer);
fChunkBuffer = NULL;
fChunkBufferSize = 0;
_ResetTempPacket();
status_t statusOfDecodingFirstFrame = _DecodeNextVideoFrame();
if (statusOfDecodingFirstFrame != B_OK) {
TRACE("[v] decoding first video frame failed\n");
return B_ERROR;
}
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
if (fSwsContext == NULL) {
TRACE("No SWS Scale context or decoder has not set the pixel format "
"yet!\n");
}
#else
if (fFormatConversionFunc == NULL) {
TRACE("no pixel format conversion function found or decoder has "
"not set the pixel format yet!\n");
}
#endif
inOutFormat->type = B_MEDIA_RAW_VIDEO;
inOutFormat->require_flags = 0;
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
inOutFormat->u.raw_video = fInputFormat.u.encoded_video.output;
inOutFormat->u.raw_video.interlace = 1;
inOutFormat->u.raw_video.first_active
= fHeader.u.raw_video.first_active_line;
inOutFormat->u.raw_video.last_active = fHeader.u.raw_video.line_count;
inOutFormat->u.raw_video.pixel_width_aspect
= fHeader.u.raw_video.pixel_width_aspect;
inOutFormat->u.raw_video.pixel_height_aspect
= fHeader.u.raw_video.pixel_height_aspect;
float fromFormat = fInputFormat.u.encoded_video.output.field_rate;
if (fromFormat < 70)
inOutFormat->u.raw_video.field_rate = fromFormat;
AVRational codec_fr = fCodecContext->framerate;
if (codec_fr.num > 0 && codec_fr.den > 0
&& (fromFormat == 0 || av_q2d(codec_fr) < fromFormat * 0.7)) {
inOutFormat->u.raw_video.field_rate = av_q2d(fCodecContext->framerate);
}
inOutFormat->u.raw_video.display.format = fOutputColorSpace;
inOutFormat->u.raw_video.display.line_width
= fHeader.u.raw_video.display_line_width;
inOutFormat->u.raw_video.display.line_count
= fHeader.u.raw_video.display_line_count;
inOutFormat->u.raw_video.display.bytes_per_row
= fHeader.u.raw_video.bytes_per_row;
#ifdef 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("AVCodecDecoder::_DecodeAudio(audio start_time %.6fs)\n",
mediaHeader->start_time / 1000000.0);
status_t audioDecodingStatus
= fDecodedDataSizeInBytes > 0 ? B_OK : _DecodeNextAudioFrame();
if (audioDecodingStatus != B_OK)
return audioDecodingStatus;
*outFrameCount = fDecodedDataSizeInBytes / fOutputFrameSize;
*mediaHeader = fHeader;
memcpy(outBuffer, fDecodedData, fDecodedDataSizeInBytes);
fDecodedDataSizeInBytes = 0;
return B_OK;
}
status_t
AVCodecDecoder::_DecodeVideo(void* outBuffer, int64* outFrameCount,
media_header* mediaHeader, media_decode_info* info)
{
status_t videoDecodingStatus
= fDecodedDataSizeInBytes > 0 ? B_OK : _DecodeNextVideoFrame();
if (videoDecodingStatus != B_OK)
return videoDecodingStatus;
*outFrameCount = 1;
*mediaHeader = fHeader;
memcpy(outBuffer, fDecodedData, mediaHeader->size_used);
fDecodedDataSizeInBytes = 0;
return B_OK;
}
status_t
AVCodecDecoder::_DecodeNextAudioFrame()
{
assert(fTempPacket->size >= 0);
assert(fDecodedDataSizeInBytes == 0);
status_t resetStatus = _ResetRawDecodedAudio();
if (resetStatus != B_OK)
return resetStatus;
while (fRawDecodedAudio->nb_samples < fOutputFrameCount) {
_CheckAndFixConditionsThatHintAtBrokenAudioCodeBelow();
bool decodedDataBufferHasData = fDecodedDataBufferSize > 0;
if (decodedDataBufferHasData) {
_MoveAudioFramesToRawDecodedAudioAndUpdateStartTimes();
continue;
}
status_t decodeAudioChunkStatus = _DecodeNextAudioFrameChunk();
if (decodeAudioChunkStatus == B_LAST_BUFFER_ERROR
&& fRawDecodedAudio->nb_samples > 0)
break;
if (decodeAudioChunkStatus != B_OK)
return decodeAudioChunkStatus;
}
fFrame += fRawDecodedAudio->nb_samples;
fDecodedDataSizeInBytes = fRawDecodedAudio->linesize[0];
_UpdateMediaHeaderForAudioFrame();
#ifdef DEBUG
dump_ffframe_audio(fRawDecodedAudio, "ffaudi");
#endif
TRACE_AUDIO(" frame count: %d current: %" B_PRId64 "\n",
fRawDecodedAudio->nb_samples, fFrame);
return B_OK;
}
void
AVCodecDecoder::_ApplyEssentialAudioContainerPropertiesToContext()
{
media_encoded_audio_format containerProperties
= fInputFormat.u.encoded_audio;
fCodecContext->bit_rate
= static_cast<int>(containerProperties.bit_rate);
fCodecContext->frame_size
= static_cast<int>(containerProperties.frame_size);
ConvertRawAudioFormatToAVSampleFormat(
containerProperties.output.format, fCodecContext->sample_fmt);
ConvertRawAudioFormatToAVSampleFormat(
containerProperties.output.format, fCodecContext->request_sample_fmt);
fCodecContext->sample_rate
= static_cast<int>(containerProperties.output.frame_rate);
int channel_count = static_cast<int>(containerProperties.output.channel_count);
if (channel_count == 0) {
TRACE(" channel_count still a wild-card, assuming stereo.\n");
set_channel_count(fCodecContext, 2);
} else
set_channel_count(fCodecContext, channel_count);
fCodecContext->block_align = fBlockAlign;
fCodecContext->extradata = reinterpret_cast<uint8_t*>(fExtraData);
fCodecContext->extradata_size = fExtraDataSize;
if (fInputFormat.MetaDataSize() > 0) {
fCodecContext->extradata = static_cast<uint8_t*>(
const_cast<void*>(fInputFormat.MetaData()));
fCodecContext->extradata_size = fInputFormat.MetaDataSize();
}
TRACE(" bit_rate %" PRId64 ", sample_rate %d, channels %d, block_align %d, "
"extradata_size %d\n",
fCodecContext->bit_rate,
fCodecContext->sample_rate,
fCodecContext->ch_layout.nb_channels,
fCodecContext->block_align,
fCodecContext->extradata_size);
}
status_t
AVCodecDecoder::_ResetRawDecodedAudio()
{
if (fDecodedData == NULL) {
size_t maximumSizeOfDecodedData = fOutputFrameCount * fOutputFrameSize;
fDecodedData
= static_cast<uint8_t*>(malloc(maximumSizeOfDecodedData));
}
if (fDecodedData == NULL)
return B_NO_MEMORY;
fRawDecodedAudio->data[0] = fDecodedData;
fRawDecodedAudio->linesize[0] = 0;
fRawDecodedAudio->format = AV_SAMPLE_FMT_NONE;
fRawDecodedAudio->pkt_dts = AV_NOPTS_VALUE;
fRawDecodedAudio->nb_samples = 0;
memset(fRawDecodedAudio->opaque, 0, sizeof(avformat_codec_context));
return B_OK;
}
void
AVCodecDecoder::_CheckAndFixConditionsThatHintAtBrokenAudioCodeBelow()
{
if (fDecodedDataBufferSize < 0) {
fprintf(stderr, "Decoding read past the end of the decoded data "
"buffer! %" B_PRId32 "\n", fDecodedDataBufferSize);
fDecodedDataBufferSize = 0;
}
if (fTempPacket->size < 0) {
fprintf(stderr, "Decoding read past the end of the temp packet! %d\n",
fTempPacket->size);
fTempPacket->size = 0;
}
}
void
AVCodecDecoder::_MoveAudioFramesToRawDecodedAudioAndUpdateStartTimes()
{
assert(fDecodedDataBufferSize > 0);
assert(fRawDecodedAudio->nb_samples < fOutputFrameCount);
assert(fOutputFrameRate > 0);
int32 outFrames = fOutputFrameCount - fRawDecodedAudio->nb_samples;
int32 inFrames = fDecodedDataBufferSize;
int32 frames = min_c(outFrames, inFrames);
if (frames == 0)
debugger("fDecodedDataBufferSize not multiple of frame size!");
if (av_sample_fmt_is_planar(fCodecContext->sample_fmt)) {
#if 0
const uint8_t* ptr[8];
for (int i = 0; i < 8; i++) {
if (fDecodedDataBuffer->data[i] == NULL)
ptr[i] = NULL;
else
ptr[i] = fDecodedDataBuffer->data[i] + fDecodedDataBufferOffset;
}
inFrames = frames;
frames = swr_convert(fResampleContext, fRawDecodedAudio->data,
outFrames, ptr, inFrames);
if (frames < 0)
debugger("resampling failed");
#else
uintptr_t out = (uintptr_t)fRawDecodedAudio->data[0];
int32 offset = fDecodedDataBufferOffset;
for (int i = 0; i < frames; i++) {
for (int j = 0; j < get_channel_count(fCodecContext); j++) {
memcpy((void*)out, fDecodedDataBuffer->data[j]
+ offset, fInputFrameSize);
out += fInputFrameSize;
}
offset += fInputFrameSize;
}
outFrames = frames;
inFrames = frames;
#endif
} else {
memcpy(fRawDecodedAudio->data[0], fDecodedDataBuffer->data[0]
+ fDecodedDataBufferOffset, frames * fOutputFrameSize);
outFrames = frames;
inFrames = frames;
}
size_t remainingSize = inFrames * fInputFrameSize;
size_t decodedSize = outFrames * fOutputFrameSize;
fDecodedDataBufferSize -= inFrames;
bool firstAudioFramesCopiedToRawDecodedAudio
= fRawDecodedAudio->data[0] != fDecodedData;
if (!firstAudioFramesCopiedToRawDecodedAudio) {
fRawDecodedAudio->format = fDecodedDataBuffer->format;
fRawDecodedAudio->pkt_dts = fDecodedDataBuffer->pkt_dts;
avformat_codec_context* codecContext
= static_cast<avformat_codec_context*>(fRawDecodedAudio->opaque);
codecContext->channels = get_channel_count(fCodecContext);
codecContext->sample_rate = fCodecContext->sample_rate;
}
fRawDecodedAudio->data[0] += decodedSize;
fRawDecodedAudio->linesize[0] += decodedSize;
fRawDecodedAudio->nb_samples += outFrames;
fDecodedDataBufferOffset += remainingSize;
bigtime_t framesTimeInterval = static_cast<bigtime_t>(
(1000000LL * frames) / fOutputFrameRate);
fDecodedDataBuffer->pkt_dts += framesTimeInterval;
fTempPacket->dts += framesTimeInterval;
}
status_t
AVCodecDecoder::_DecodeNextAudioFrameChunk()
{
assert(fDecodedDataBufferSize == 0);
while (fDecodedDataBufferSize == 0) {
status_t loadingChunkStatus
= _LoadNextChunkIfNeededAndAssignStartTime();
if (loadingChunkStatus != B_OK)
return loadingChunkStatus;
status_t decodingStatus
= _DecodeSomeAudioFramesIntoEmptyDecodedDataBuffer();
if (decodingStatus != B_OK) {
memset(fDecodedData, 0, fRawDecodedAudio->linesize[0]);
if (!fAudioDecodeError) {
int32 chunkBufferOffset = fTempPacket->data - fChunkBuffer;
printf("########### audio decode error, "
"fTempPacket->size %d, fChunkBuffer data offset %" B_PRId32
"\n", fTempPacket->size, chunkBufferOffset);
fAudioDecodeError = true;
}
continue;
}
fAudioDecodeError = false;
}
return B_OK;
}
status_t
AVCodecDecoder::_DecodeSomeAudioFramesIntoEmptyDecodedDataBuffer()
{
assert(fDecodedDataBufferSize == 0);
av_frame_unref(fDecodedDataBuffer);
fDecodedDataBufferOffset = 0;
int error = avcodec_receive_frame(fCodecContext, fDecodedDataBuffer);
if (error == AVERROR_EOF)
return B_LAST_BUFFER_ERROR;
if (error == AVERROR(EAGAIN)) {
avcodec_send_packet(fCodecContext, fTempPacket);
fTempPacket->size = 0;
error = avcodec_receive_frame(fCodecContext, fDecodedDataBuffer);
}
fDecodedDataBufferSize = fDecodedDataBuffer->nb_samples;
if (fDecodedDataBufferSize < 0)
fDecodedDataBufferSize = 0;
if (error == 0)
return B_OK;
else
return B_ERROR;
}
void
AVCodecDecoder::_UpdateMediaHeaderForAudioFrame()
{
fHeader.type = B_MEDIA_RAW_AUDIO;
fHeader.file_pos = 0;
fHeader.orig_size = 0;
fHeader.start_time = fRawDecodedAudio->pkt_dts;
fHeader.size_used = fRawDecodedAudio->linesize[0];
avformat_codec_context* codecContext
= static_cast<avformat_codec_context*>(fRawDecodedAudio->opaque);
fHeader.u.raw_audio.channel_count = codecContext->channels;
fHeader.u.raw_audio.frame_rate = codecContext->sample_rate;
}
status_t
AVCodecDecoder::_DecodeNextVideoFrame()
{
int error;
int send_error;
#if DO_PROFILING
bigtime_t startTime = system_time();
#endif
error = avcodec_receive_frame(fCodecContext, fRawDecodedPicture);
if (error == AVERROR_EOF)
return B_LAST_BUFFER_ERROR;
if (error == AVERROR(EAGAIN)) {
do {
status_t loadingChunkStatus
= _LoadNextChunkIfNeededAndAssignStartTime();
if (loadingChunkStatus == B_LAST_BUFFER_ERROR)
return _FlushOneVideoFrameFromDecoderBuffer();
if (loadingChunkStatus != B_OK) {
TRACE("[v] AVCodecDecoder::_DecodeNextVideoFrame(): error from "
"GetNextChunk(): %s\n", strerror(loadingChunkStatus));
return loadingChunkStatus;
}
char timestamp[AV_TS_MAX_STRING_SIZE];
av_ts_make_time_string(timestamp,
fTempPacket->dts, &fCodecContext->time_base);
TRACE("[v] Feed %d more bytes (dts %s)\n", fTempPacket->size,
timestamp);
send_error = avcodec_send_packet(fCodecContext, fTempPacket);
if (send_error < 0 && send_error != AVERROR(EAGAIN)) {
TRACE("[v] AVCodecDecoder: ignoring error in decoding frame %" B_PRId64 ": %d\n",
fFrame, error);
}
fTempPacket->data = NULL;
fTempPacket->size = 0;
error = avcodec_receive_frame(fCodecContext, fRawDecodedPicture);
if (error != 0 && error != AVERROR(EAGAIN)) {
TRACE("[v] frame %" B_PRId64 " decoding error: error code: %d, chunk size: %ld\n",
fFrame, error, fChunkBufferSize);
}
} while (error != 0);
}
#if DO_PROFILING
bigtime_t formatConversionStart = system_time();
#endif
status_t handleStatus = _HandleNewVideoFrameAndUpdateSystemState();
if (handleStatus != B_OK)
return handleStatus;
#if DO_PROFILING
bigtime_t doneTime = system_time();
decodingTime += formatConversionStart - startTime;
conversionTime += doneTime - formatConversionStart;
profileCounter++;
if (!(fFrame % 5)) {
printf("[v] profile: d1 = %lld, d2 = %lld (%lld)\n",
decodingTime / profileCounter, conversionTime / profileCounter,
fFrame);
decodingTime = 0;
conversionTime = 0;
profileCounter = 0;
}
#endif
return error;
}
void
AVCodecDecoder::_ApplyEssentialVideoContainerPropertiesToContext()
{
media_raw_video_format containerProperties
= fInputFormat.u.encoded_video.output;
fCodecContext->width = containerProperties.display.line_width;
fCodecContext->height = containerProperties.display.line_count;
if (containerProperties.pixel_width_aspect > 0
&& containerProperties.pixel_height_aspect > 0) {
ConvertVideoAspectWidthAndHeightToAVCodecContext(
containerProperties.pixel_width_aspect,
containerProperties.pixel_height_aspect, *fCodecContext);
}
if (containerProperties.field_rate > 0.0) {
ConvertVideoFrameRateToAVCodecContext(containerProperties.field_rate,
*fCodecContext);
}
fCodecContext->extradata = reinterpret_cast<uint8_t*>(fExtraData);
fCodecContext->extradata_size = fExtraDataSize;
}
status_t
AVCodecDecoder::_LoadNextChunkIfNeededAndAssignStartTime()
{
if (fTempPacket->size > 0)
return B_OK;
const void* chunkBuffer = NULL;
size_t chunkBufferSize = 0;
media_header chunkMediaHeader;
status_t getNextChunkStatus = GetNextChunk(&chunkBuffer, &chunkBufferSize,
&chunkMediaHeader);
if (getNextChunkStatus != B_OK)
return getNextChunkStatus;
status_t chunkBufferPaddingStatus
= _CopyChunkToChunkBufferAndAddPadding(chunkBuffer, chunkBufferSize);
if (chunkBufferPaddingStatus != B_OK)
return chunkBufferPaddingStatus;
fTempPacket->data = fChunkBuffer;
fTempPacket->size = fChunkBufferSize;
fTempPacket->dts = chunkMediaHeader.start_time;
if (chunkMediaHeader.user_data_type == AVPACKET_USER_DATA_TYPE) {
avpacket_user_data* data = (avpacket_user_data*)&chunkMediaHeader.user_data;
fTempPacket->pts = data->pts;
fTempPacket->dts = data->dts;
fTempPacket->stream_index = data->stream_index;
fTempPacket->flags = data->flags;
fTempPacket->duration = data->duration;
fTempPacket->pos = data->pos;
}
#ifdef LOG_STREAM_TO_FILE
BFile* logFile = fIsAudio ? &sAudioStreamLogFile : &sVideoStreamLogFile;
if (sDumpedPackets < 100) {
logFile->Write(chunkBuffer, fChunkBufferSize);
printf("wrote %ld bytes\n", fChunkBufferSize);
sDumpedPackets++;
} else if (sDumpedPackets == 100)
logFile->Unset();
#endif
return B_OK;
}
status_t
AVCodecDecoder::_CopyChunkToChunkBufferAndAddPadding(const void* chunk,
size_t chunkSize)
{
uint8_t* tmpBuffer = static_cast<uint8_t*>(realloc(fChunkBuffer,
chunkSize + AV_INPUT_BUFFER_PADDING_SIZE));
if (tmpBuffer == NULL) {
free(fChunkBuffer);
fChunkBuffer = NULL;
fChunkBufferSize = 0;
return B_NO_MEMORY;
} else {
fChunkBuffer = tmpBuffer;
}
memcpy(fChunkBuffer, chunk, chunkSize);
memset(fChunkBuffer + chunkSize, 0, AV_INPUT_BUFFER_PADDING_SIZE);
fChunkBufferSize = chunkSize;
return B_OK;
}
status_t
AVCodecDecoder::_HandleNewVideoFrameAndUpdateSystemState()
{
_UpdateMediaHeaderForVideoFrame();
status_t postProcessStatus = _DeinterlaceAndColorConvertVideoFrame();
if (postProcessStatus != B_OK)
return postProcessStatus;
#ifdef DEBUG
dump_ffframe_video(fRawDecodedPicture, "ffpict");
#endif
fFrame++;
return B_OK;
}
status_t
AVCodecDecoder::_FlushOneVideoFrameFromDecoderBuffer()
{
avcodec_send_packet(fCodecContext, NULL);
int error = avcodec_receive_frame(fCodecContext, fRawDecodedPicture);
if (error != 0 && error != AVERROR(EAGAIN)) {
return B_LAST_BUFFER_ERROR;
}
return _HandleNewVideoFrameAndUpdateSystemState();
}
void
AVCodecDecoder::_UpdateMediaHeaderForVideoFrame()
{
fHeader.type = B_MEDIA_RAW_VIDEO;
fHeader.file_pos = 0;
fHeader.orig_size = 0;
fHeader.start_time = fRawDecodedPicture->best_effort_timestamp;
fHeader.size_used = av_image_get_buffer_size(
colorspace_to_pixfmt(fOutputColorSpace), fRawDecodedPicture->width,
fRawDecodedPicture->height, 1);
fHeader.u.raw_video.display_line_width = fRawDecodedPicture->width;
fHeader.u.raw_video.display_line_count = fRawDecodedPicture->height;
fHeader.u.raw_video.bytes_per_row
= CalculateBytesPerRowWithColorSpaceAndVideoWidth(fOutputColorSpace,
fRawDecodedPicture->width);
fHeader.u.raw_video.field_gamma = 1.0;
fHeader.u.raw_video.field_sequence = fFrame;
fHeader.u.raw_video.field_number = 0;
fHeader.u.raw_video.pulldown_number = 0;
fHeader.u.raw_video.first_active_line = 1;
fHeader.u.raw_video.line_count = fRawDecodedPicture->height;
ConvertAVCodecContextToVideoAspectWidthAndHeight(*fCodecContext,
fHeader.u.raw_video.pixel_width_aspect,
fHeader.u.raw_video.pixel_height_aspect);
char timestamp[AV_TS_MAX_STRING_SIZE];
av_ts_make_time_string(timestamp,
fRawDecodedPicture->best_effort_timestamp, &fCodecContext->time_base);
TRACE("[v] start_time=%s field_sequence=%" B_PRIu32 "\n",
timestamp, fHeader.u.raw_video.field_sequence);
}
status_t
AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
{
int displayWidth = fRawDecodedPicture->width;
int displayHeight = fRawDecodedPicture->height;
AVFrame deinterlacedPicture;
bool useDeinterlacedPicture = false;
if (fRawDecodedPicture->flags & AV_FRAME_FLAG_INTERLACED) {
AVFrame rawPicture;
rawPicture.data[0] = fRawDecodedPicture->data[0];
rawPicture.data[1] = fRawDecodedPicture->data[1];
rawPicture.data[2] = fRawDecodedPicture->data[2];
rawPicture.data[3] = fRawDecodedPicture->data[3];
rawPicture.linesize[0] = fRawDecodedPicture->linesize[0];
rawPicture.linesize[1] = fRawDecodedPicture->linesize[1];
rawPicture.linesize[2] = fRawDecodedPicture->linesize[2];
rawPicture.linesize[3] = fRawDecodedPicture->linesize[3];
if (av_image_alloc(deinterlacedPicture.data,
deinterlacedPicture.linesize, displayWidth, displayHeight,
fCodecContext->pix_fmt, 1) < 0)
return B_NO_MEMORY;
_ProcessFilterGraph(&deinterlacedPicture, &rawPicture,
fCodecContext->pix_fmt, displayWidth, displayHeight);
useDeinterlacedPicture = true;
}
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
if (fSwsContext == NULL) {
fSwsContext = sws_getContext(displayWidth, displayHeight,
fCodecContext->pix_fmt, displayWidth, displayHeight,
colorspace_to_pixfmt(fOutputColorSpace),
SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
#else
if (fFormatConversionFunc == NULL) {
fFormatConversionFunc = resolve_colorspace(fOutputColorSpace,
fCodecContext->pix_fmt, displayWidth, displayHeight);
}
#endif
fDecodedDataSizeInBytes = fHeader.size_used;
if (fDecodedData == NULL) {
const size_t kOptimalAlignmentForColorConversion = 32;
posix_memalign(reinterpret_cast<void**>(&fDecodedData),
kOptimalAlignmentForColorConversion, fDecodedDataSizeInBytes);
}
if (fDecodedData == NULL)
return B_NO_MEMORY;
fPostProcessedDecodedPicture->data[0] = fDecodedData;
fPostProcessedDecodedPicture->linesize[0]
= fHeader.u.raw_video.bytes_per_row;
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
if (fSwsContext != NULL) {
#else
if (fFormatConversionFunc != NULL) {
#endif
if (useDeinterlacedPicture) {
AVFrame deinterlacedFrame;
deinterlacedFrame.data[0] = deinterlacedPicture.data[0];
deinterlacedFrame.data[1] = deinterlacedPicture.data[1];
deinterlacedFrame.data[2] = deinterlacedPicture.data[2];
deinterlacedFrame.data[3] = deinterlacedPicture.data[3];
deinterlacedFrame.linesize[0]
= deinterlacedPicture.linesize[0];
deinterlacedFrame.linesize[1]
= deinterlacedPicture.linesize[1];
deinterlacedFrame.linesize[2]
= deinterlacedPicture.linesize[2];
deinterlacedFrame.linesize[3]
= deinterlacedPicture.linesize[3];
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
sws_scale(fSwsContext, deinterlacedFrame.data,
deinterlacedFrame.linesize, 0, displayHeight,
fPostProcessedDecodedPicture->data,
fPostProcessedDecodedPicture->linesize);
#else
(*fFormatConversionFunc)(&deinterlacedFrame,
fPostProcessedDecodedPicture, displayWidth, displayHeight);
#endif
} else {
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
sws_scale(fSwsContext, fRawDecodedPicture->data,
fRawDecodedPicture->linesize, 0, displayHeight,
fPostProcessedDecodedPicture->data,
fPostProcessedDecodedPicture->linesize);
#else
(*fFormatConversionFunc)(fRawDecodedPicture,
fPostProcessedDecodedPicture, displayWidth, displayHeight);
#endif
}
}
if (fRawDecodedPicture->flags & AV_FRAME_FLAG_INTERLACED)
av_freep(&deinterlacedPicture.data[0]);
return B_OK;
}
status_t
AVCodecDecoder::_InitFilterGraph(enum AVPixelFormat pixfmt, int32 width,
int32 height)
{
if (fFilterGraph != NULL) {
av_frame_free(&fFilterFrame);
avfilter_graph_free(&fFilterGraph);
}
fFilterGraph = avfilter_graph_alloc();
BString arguments;
arguments.SetToFormat("buffer=video_size=%" B_PRId32 "x%" B_PRId32
":pix_fmt=%d:time_base=1/1:pixel_aspect=0/1[in];[in]yadif[out];"
"[out]buffersink", width, height,
pixfmt);
AVFilterInOut* inputs = NULL;
AVFilterInOut* outputs = NULL;
TRACE("[v] _InitFilterGraph(): %s\n", arguments.String());
int ret = avfilter_graph_parse2(fFilterGraph, arguments.String(), &inputs,
&outputs);
if (ret < 0) {
fprintf(stderr, "avfilter_graph_parse2() failed\n");
return B_BAD_VALUE;
}
ret = avfilter_graph_config(fFilterGraph, NULL);
if (ret < 0) {
fprintf(stderr, "avfilter_graph_config() failed\n");
return B_BAD_VALUE;
}
fBufferSourceContext = avfilter_graph_get_filter(fFilterGraph,
"Parsed_buffer_0");
fBufferSinkContext = avfilter_graph_get_filter(fFilterGraph,
"Parsed_buffersink_2");
if (fBufferSourceContext == NULL || fBufferSinkContext == NULL) {
fprintf(stderr, "avfilter_graph_get_filter() failed\n");
return B_BAD_VALUE;
}
fFilterFrame = av_frame_alloc();
fLastWidth = width;
fLastHeight = height;
fLastPixfmt = pixfmt;
return B_OK;
}
status_t
AVCodecDecoder::_ProcessFilterGraph(AVFrame *dst, const AVFrame *src,
enum AVPixelFormat pixfmt, int32 width, int32 height)
{
if (fFilterGraph == NULL || width != fLastWidth
|| height != fLastHeight || pixfmt != fLastPixfmt) {
status_t err = _InitFilterGraph(pixfmt, width, height);
if (err != B_OK)
return err;
}
memcpy(fFilterFrame->data, src->data, sizeof(src->data));
memcpy(fFilterFrame->linesize, src->linesize, sizeof(src->linesize));
fFilterFrame->width = width;
fFilterFrame->height = height;
fFilterFrame->format = pixfmt;
int ret = av_buffersrc_add_frame(fBufferSourceContext, fFilterFrame);
if (ret < 0)
return B_NO_MEMORY;
ret = av_buffersink_get_frame(fBufferSinkContext, fFilterFrame);
if (ret < 0)
return B_BAD_DATA;
av_image_copy(dst->data, dst->linesize, (const uint8**)fFilterFrame->data,
fFilterFrame->linesize, pixfmt, width, height);
av_frame_unref(fFilterFrame);
return B_OK;
}