* Use the new avcodec_decode_audio3() call, avcodec_decode_audio2()
gives the deprecated warning... We need to cache an AVPacket for this.
* Check the allocation of fOutputBuffer.
* When seeking, we need to flush the already decoded stuff
in fOutputBuffer, and throw away the last chunk buffer as well.
* Handle an incomplete input format at least to the point of not
crashing with a divide error (mp3_reader would give us such an
incomplete format for example).
* _DecodeAudio():
- Fixed some edge cases in the audio decoding loop: avcodec_decode_audio3()
can return a 0 length, which means no error, but no decoded frames
either. ffplay throws away the chunk in this case, do the same.
- Convert some invalid situations that were printf()s into debugger()s.
- Add much more comments to explain how everything works.
* Fixed the occasional coding style issue.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38400 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -112,9 +112,9 @@ AVCodecDecoder::~AVCodecDecoder()
|
|||||||
|
|
||||||
#ifdef DO_PROFILING
|
#ifdef DO_PROFILING
|
||||||
if (profileCounter > 0) {
|
if (profileCounter > 0) {
|
||||||
printf("[%c] profile: d1 = %lld, d2 = %lld (%Ld)\n",
|
printf("[%c] profile: d1 = %lld, d2 = %lld (%Ld)\n",
|
||||||
fIsAudio?('a'):('v'), decodingTime / profileCounter, conversionTime / profileCounter,
|
fIsAudio?('a'):('v'), decodingTime / profileCounter,
|
||||||
fFrame);
|
conversionTime / profileCounter, fFrame);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -157,8 +157,11 @@ AVCodecDecoder::Setup(media_format* ioEncodedFormat, const void* infoBuffer,
|
|||||||
fIsAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO);
|
fIsAudio = (ioEncodedFormat->type == B_MEDIA_ENCODED_AUDIO);
|
||||||
TRACE("[%c] AVCodecDecoder::Setup()\n", fIsAudio?('a'):('v'));
|
TRACE("[%c] AVCodecDecoder::Setup()\n", fIsAudio?('a'):('v'));
|
||||||
|
|
||||||
if (fIsAudio && !fOutputBuffer)
|
if (fIsAudio && fOutputBuffer == NULL) {
|
||||||
fOutputBuffer = new char[AVCODEC_MAX_AUDIO_FRAME_SIZE];
|
fOutputBuffer = new(std::nothrow) char[AVCODEC_MAX_AUDIO_FRAME_SIZE];
|
||||||
|
if (fOutputBuffer == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TRACE_AV_CODEC
|
#ifdef TRACE_AV_CODEC
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
@@ -271,9 +274,8 @@ AVCodecDecoder::Seek(uint32 seekTo, int64 seekFrame, int64* frame,
|
|||||||
// Reset the FFmpeg codec to flush buffers, so we keep the sync
|
// Reset the FFmpeg codec to flush buffers, so we keep the sync
|
||||||
#if 1
|
#if 1
|
||||||
if (fCodecInitDone) {
|
if (fCodecInitDone) {
|
||||||
fCodecInitDone = false;
|
|
||||||
avcodec_close(fContext);
|
avcodec_close(fContext);
|
||||||
fCodecInitDone = (avcodec_open(fContext, fCodec) >= 0);
|
fCodecInitDone = avcodec_open(fContext, fCodec) >= 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// For example, this doesn't work on the H.264 codec. :-/
|
// For example, this doesn't work on the H.264 codec. :-/
|
||||||
@@ -298,6 +300,13 @@ AVCodecDecoder::Seek(uint32 seekTo, int64 seekFrame, int64* frame,
|
|||||||
} else
|
} else
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// Flush internal buffers as well.
|
||||||
|
fChunkBuffer = NULL;
|
||||||
|
fChunkBufferOffset = 0;
|
||||||
|
fChunkBufferSize = 0;
|
||||||
|
fOutputBufferOffset = 0;
|
||||||
|
fOutputBufferSize = 0;
|
||||||
|
|
||||||
fFrame = *frame;
|
fFrame = *frame;
|
||||||
fStartTime = *time;
|
fStartTime = *time;
|
||||||
TRACE("so new frame is %Ld at time %.6f\n", *frame, *time / 1000000.0);
|
TRACE("so new frame is %Ld at time %.6f\n", *frame, *time / 1000000.0);
|
||||||
@@ -365,11 +374,17 @@ AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
|
|||||||
= fInputFormat.u.encoded_audio.output.channel_count;
|
= fInputFormat.u.encoded_audio.output.channel_count;
|
||||||
outputAudioFormat.format = fInputFormat.u.encoded_audio.output.format;
|
outputAudioFormat.format = fInputFormat.u.encoded_audio.output.format;
|
||||||
// Check that format is not still a wild card!
|
// Check that format is not still a wild card!
|
||||||
if (outputAudioFormat.format == 0)
|
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.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||||
|
}
|
||||||
|
// Check that channel count is not still a wild card!
|
||||||
|
if (outputAudioFormat.channel_count == 0) {
|
||||||
|
TRACE(" channel_count still a wild-card, assuming stereo.\n");
|
||||||
|
outputAudioFormat.channel_count = 2;
|
||||||
|
}
|
||||||
|
|
||||||
outputAudioFormat.buffer_size
|
outputAudioFormat.buffer_size = 1024 * outputAudioFormat.channel_count;
|
||||||
= 1024 * fInputFormat.u.encoded_audio.output.channel_count;
|
|
||||||
inOutFormat->type = B_MEDIA_RAW_AUDIO;
|
inOutFormat->type = B_MEDIA_RAW_AUDIO;
|
||||||
inOutFormat->u.raw_audio = outputAudioFormat;
|
inOutFormat->u.raw_audio = outputAudioFormat;
|
||||||
|
|
||||||
@@ -377,7 +392,7 @@ AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
|
|||||||
fContext->frame_size = (int)fInputFormat.u.encoded_audio.frame_size;
|
fContext->frame_size = (int)fInputFormat.u.encoded_audio.frame_size;
|
||||||
fContext->sample_rate
|
fContext->sample_rate
|
||||||
= (int)fInputFormat.u.encoded_audio.output.frame_rate;
|
= (int)fInputFormat.u.encoded_audio.output.frame_rate;
|
||||||
fContext->channels = fInputFormat.u.encoded_audio.output.channel_count;
|
fContext->channels = outputAudioFormat.channel_count;
|
||||||
fContext->block_align = fBlockAlign;
|
fContext->block_align = fBlockAlign;
|
||||||
fContext->extradata = (uint8_t*)fExtraData;
|
fContext->extradata = (uint8_t*)fExtraData;
|
||||||
fContext->extradata_size = fExtraDataSize;
|
fContext->extradata_size = fExtraDataSize;
|
||||||
@@ -426,6 +441,8 @@ AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
|
|||||||
fOutputBufferOffset = 0;
|
fOutputBufferOffset = 0;
|
||||||
fOutputBufferSize = 0;
|
fOutputBufferSize = 0;
|
||||||
|
|
||||||
|
av_init_packet(&fAudioTempPacket);
|
||||||
|
|
||||||
inOutFormat->require_flags = 0;
|
inOutFormat->require_flags = 0;
|
||||||
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
|
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
|
||||||
|
|
||||||
@@ -547,43 +564,53 @@ AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
AVCodecDecoder::_DecodeAudio(void* outBuffer, int64* outFrameCount,
|
AVCodecDecoder::_DecodeAudio(void* _buffer, int64* outFrameCount,
|
||||||
media_header* mediaHeader, media_decode_info* info)
|
media_header* mediaHeader, media_decode_info* info)
|
||||||
{
|
{
|
||||||
TRACE_AUDIO("AVCodecDecoder::_DecodeAudio()\n");
|
TRACE_AUDIO("AVCodecDecoder::_DecodeAudio(audio start_time %.6fs)\n",
|
||||||
// TRACE_AUDIO(" audio start_time %.6f\n",
|
mediaHeader->start_time / 1000000.0);
|
||||||
// mediaHeader->start_time / 1000000.0);
|
|
||||||
|
|
||||||
char* output_buffer = (char*)outBuffer;
|
|
||||||
*outFrameCount = 0;
|
*outFrameCount = 0;
|
||||||
|
|
||||||
|
uint8* buffer = reinterpret_cast<uint8*>(_buffer);
|
||||||
while (*outFrameCount < fOutputFrameCount) {
|
while (*outFrameCount < fOutputFrameCount) {
|
||||||
|
// Check conditions which would hint at broken code below.
|
||||||
if (fOutputBufferSize < 0) {
|
if (fOutputBufferSize < 0) {
|
||||||
TRACE_AUDIO(" ############ fOutputBufferSize %ld\n",
|
debugger("Decoding read past the end of the output buffer!");
|
||||||
fOutputBufferSize);
|
|
||||||
fOutputBufferSize = 0;
|
fOutputBufferSize = 0;
|
||||||
}
|
}
|
||||||
if (fChunkBufferSize < 0) {
|
if (fChunkBufferSize < 0) {
|
||||||
TRACE_AUDIO(" ############ fChunkBufferSize %ld\n",
|
debugger("Decoding read past the end of the chunk buffer!");
|
||||||
fChunkBufferSize);
|
|
||||||
fChunkBufferSize = 0;
|
fChunkBufferSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fOutputBufferSize > 0) {
|
if (fOutputBufferSize > 0) {
|
||||||
|
// We still have decoded audio frames from the last
|
||||||
|
// invokation, which start at fOutputBuffer + fOutputBufferOffset
|
||||||
|
// and are of fOutputBufferSize. Copy those into the buffer,
|
||||||
|
// but not more than it can hold.
|
||||||
int32 frames = min_c(fOutputFrameCount - *outFrameCount,
|
int32 frames = min_c(fOutputFrameCount - *outFrameCount,
|
||||||
fOutputBufferSize / fOutputFrameSize);
|
fOutputBufferSize / fOutputFrameSize);
|
||||||
memcpy(output_buffer, fOutputBuffer + fOutputBufferOffset,
|
if (frames == 0)
|
||||||
frames * fOutputFrameSize);
|
debugger("fOutputBufferSize not multiple of frame size!");
|
||||||
fOutputBufferOffset += frames * fOutputFrameSize;
|
size_t remainingSize = frames * fOutputFrameSize;
|
||||||
fOutputBufferSize -= frames * fOutputFrameSize;
|
memcpy(buffer, fOutputBuffer + fOutputBufferOffset, remainingSize);
|
||||||
output_buffer += frames * fOutputFrameSize;
|
fOutputBufferOffset += remainingSize;
|
||||||
|
fOutputBufferSize -= remainingSize;
|
||||||
|
buffer += remainingSize;
|
||||||
*outFrameCount += frames;
|
*outFrameCount += frames;
|
||||||
fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate);
|
fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (fChunkBufferSize == 0) {
|
if (fChunkBufferSize == 0) {
|
||||||
|
// Time to read the next chunk buffer. We use a separate
|
||||||
|
// media_header, since the chunk header may not belong to
|
||||||
|
// the start of the decoded audio frames we return. For
|
||||||
|
// example we may have used frames from a previous invokation,
|
||||||
|
// or we may have to read several chunks until we fill up the
|
||||||
|
// output buffer.
|
||||||
media_header chunkMediaHeader;
|
media_header chunkMediaHeader;
|
||||||
status_t err;
|
status_t err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize,
|
||||||
err = GetNextChunk(&fChunkBuffer, &fChunkBufferSize,
|
|
||||||
&chunkMediaHeader);
|
&chunkMediaHeader);
|
||||||
if (err == B_LAST_BUFFER_ERROR) {
|
if (err == B_LAST_BUFFER_ERROR) {
|
||||||
TRACE_AUDIO(" Last Chunk with chunk size %ld\n",
|
TRACE_AUDIO(" Last Chunk with chunk size %ld\n",
|
||||||
@@ -600,38 +627,40 @@ AVCodecDecoder::_DecodeAudio(void* outBuffer, int64* outFrameCount,
|
|||||||
fStartTime = chunkMediaHeader.start_time;
|
fStartTime = chunkMediaHeader.start_time;
|
||||||
if (*outFrameCount == 0)
|
if (*outFrameCount == 0)
|
||||||
mediaHeader->start_time = chunkMediaHeader.start_time;
|
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) {
|
|
||||||
if (!fAudioDecodeError) {
|
|
||||||
printf("########### audio decode error, "
|
|
||||||
"fChunkBufferSize %ld, fChunkBufferOffset %ld\n",
|
|
||||||
fChunkBufferSize, fChunkBufferOffset);
|
|
||||||
fAudioDecodeError = true;
|
|
||||||
}
|
|
||||||
out_size = 0;
|
|
||||||
len = 0;
|
|
||||||
fChunkBufferOffset = 0;
|
|
||||||
fChunkBufferSize = 0;
|
|
||||||
} else
|
|
||||||
fAudioDecodeError = false;
|
|
||||||
|
|
||||||
fChunkBufferOffset += len;
|
fAudioTempPacket.data = (uint8_t*)fChunkBuffer + fChunkBufferOffset;
|
||||||
fChunkBufferSize -= len;
|
fAudioTempPacket.size = fChunkBufferSize;
|
||||||
fOutputBufferOffset = 0;
|
// Initialize decodedBytes to the output buffer size.
|
||||||
fOutputBufferSize = out_size;
|
int decodedBytes = AVCODEC_MAX_AUDIO_FRAME_SIZE;
|
||||||
|
int usedBytes = avcodec_decode_audio3(fContext,
|
||||||
|
(int16*)fOutputBuffer, &decodedBytes, &fAudioTempPacket);
|
||||||
|
if (usedBytes < 0 && !fAudioDecodeError) {
|
||||||
|
// Failure
|
||||||
|
printf("########### audio decode error, "
|
||||||
|
"fChunkBufferSize %ld, fChunkBufferOffset %ld\n",
|
||||||
|
fChunkBufferSize, fChunkBufferOffset);
|
||||||
|
fAudioDecodeError = true;
|
||||||
}
|
}
|
||||||
|
if (usedBytes <= 0) {
|
||||||
|
// Error or failure to produce decompressed output.
|
||||||
|
// Skip the chunk buffer data entirely.
|
||||||
|
usedBytes = fChunkBufferSize;
|
||||||
|
decodedBytes = 0;
|
||||||
|
} else {
|
||||||
|
// Success
|
||||||
|
fAudioDecodeError = false;
|
||||||
|
}
|
||||||
|
//printf(" chunk size: %d, decoded: %d, used: %d\n",
|
||||||
|
//fAudioTempPacket.size, decodedBytes, usedBytes);
|
||||||
|
|
||||||
|
fChunkBufferOffset += usedBytes;
|
||||||
|
fChunkBufferSize -= usedBytes;
|
||||||
|
fOutputBufferOffset = 0;
|
||||||
|
fOutputBufferSize = decodedBytes;
|
||||||
}
|
}
|
||||||
TRACE_AUDIO(" frame count: %lld\n", *outFrameCount);
|
|
||||||
fFrame += *outFrameCount;
|
fFrame += *outFrameCount;
|
||||||
|
TRACE_AUDIO(" frame count: %lld current: %lld\n", *outFrameCount, fFrame);
|
||||||
// TRACE("Played %Ld frames at time %Ld\n",*outFrameCount, mediaHeader->start_time);
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ private:
|
|||||||
int32 fOutputBufferOffset;
|
int32 fOutputBufferOffset;
|
||||||
int32 fOutputBufferSize;
|
int32 fOutputBufferSize;
|
||||||
|
|
||||||
|
AVPacket fAudioTempPacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // AVCODEC_DECODER_H
|
#endif // AVCODEC_DECODER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user