Bug 1049325 - Part 1: Notify decoder for input EOS and report MP4Reader when output EOS is reached. r=cpearce

This commit is contained in:
Blake Wu 2014-08-21 14:22:25 +08:00
Родитель 2d98c38825
Коммит 88bb55ef9f
4 изменённых файлов: 110 добавлений и 33 удалений

Просмотреть файл

@ -100,9 +100,22 @@ GonkAudioDecoderManager::CreateAudioData(int64_t aStreamOffset, AudioData **v) {
size_t size;
int64_t timeUs;
if (!(mAudioBuffer != nullptr && mAudioBuffer->data() != nullptr)) {
ALOG("Audio Buffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
return NS_ERROR_UNEXPECTED;
}
if (mAudioBuffer->range_length() == 0) {
// Some decoders may return spurious empty buffers that we just want to ignore
// quoted from Android's AwesomePlayer.cpp
ReleaseAudioBuffer();
return NS_ERROR_NOT_AVAILABLE;
}
data = mAudioBuffer->data();
dataOffset = mAudioBuffer->range_offset();
size = mAudioBuffer->range_length();
@ -137,16 +150,12 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
switch (err) {
case OK:
{
if (mAudioBuffer && mAudioBuffer->range_length() != 0) {
int64_t timeUs;
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
return NS_ERROR_UNEXPECTED;
}
}
AudioData* data = nullptr;
nsresult rv = CreateAudioData(aStreamOffset, &data);
// Frame should be non null only when we succeeded.
if (rv != NS_OK) {
if (rv == NS_ERROR_NOT_AVAILABLE) {
// Decoder outputs a empty video buffer, try again
return NS_ERROR_NOT_AVAILABLE;
} else if (rv != NS_OK || data == nullptr) {
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
@ -165,7 +174,17 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
}
case android::ERROR_END_OF_STREAM:
{
ALOG("End of Stream");
ALOG("Got EOS frame!");
AudioData* data = nullptr;
nsresult rv = CreateAudioData(aStreamOffset, &data);
if (rv == NS_ERROR_NOT_AVAILABLE) {
// For EOS, no need to do any thing.
return NS_ERROR_ABORT;
} else if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create audio data!");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
return NS_ERROR_ABORT;
}
case -ETIMEDOUT:
@ -197,9 +216,20 @@ void GonkAudioDecoderManager::ReleaseAudioBuffer() {
nsresult
GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
if (mDecoder == nullptr) {
ALOG("Decoder is not inited");
return NS_ERROR_UNEXPECTED;
}
status_t rv;
if (aSample) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
} else {
// Inputted data is null, so it is going to notify decoder EOS
rv = mDecoder->Input(0, 0, 0ll, 0);
}
return rv == OK ? NS_OK : NS_ERROR_UNEXPECTED;
}

Просмотреть файл

@ -31,6 +31,7 @@ GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mManager(aManager)
, mSignaledEOS(false)
{
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
}
@ -77,8 +78,9 @@ GonkMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
mCallback->Error();
return;
}
mLastStreamOffset = aSample->byte_offset;
if (aSample) {
mLastStreamOffset = aSample->byte_offset;
}
ProcessOutput();
}
@ -92,6 +94,9 @@ GonkMediaDataDecoder::ProcessOutput()
if (rv == NS_OK) {
mCallback->Output(output.forget());
continue;
} else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
// Try to get more frames before getting EOS frame
continue;
}
else {
break;
@ -105,6 +110,15 @@ GonkMediaDataDecoder::ProcessOutput()
if (rv != NS_OK) {
NS_WARNING("GonkMediaDataDecoder failed to output data");
ALOG("Failed to output data");
// GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
if (rv == NS_ERROR_ABORT) {
if (output.get() != nullptr) {
mCallback->Output(output.forget());
}
mCallback->DrainComplete();
mSignaledEOS = false;
return;
}
mCallback->Error();
}
}
@ -125,9 +139,10 @@ GonkMediaDataDecoder::Flush()
void
GonkMediaDataDecoder::ProcessDrain()
{
// Then extract all available output.
// Notify decoder input EOS by sending a null data.
ProcessDecode(nullptr);
mSignaledEOS = true;
ProcessOutput();
mCallback->DrainComplete();
}
nsresult

Просмотреть файл

@ -68,7 +68,9 @@ public:
private:
// Called on the task queue. Inserts the sample into the decoder, and
// extracts output if available.
// extracts output if available, if aSample is null, it means there is
// no data from source, it will notify the decoder EOS and flush all the
// decoded frames.
void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
// Called on the task queue. Extracts output if available, and delivers
@ -88,6 +90,8 @@ private:
// The last offset into the media resource that was passed into Input().
// This is used to approximate the decoder's position in the media resource.
int64_t mLastStreamOffset;
// Set it ture when there is no input data
bool mSignaledEOS;
};
} // namespace mozilla

Просмотреть файл

@ -115,11 +115,24 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
*v = nullptr;
int64_t timeUs;
int32_t keyFrame;
if (!(mVideoBuffer != nullptr && mVideoBuffer->data() != nullptr)) {
ALOG("Video Buffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
ALOG("Decoder did not return frame time");
return NS_ERROR_UNEXPECTED;
}
if (mVideoBuffer->range_length() == 0) {
// Some decoders may return spurious empty buffers that we just want to ignore
// quoted from Android's AwesomePlayer.cpp
ReleaseVideoBuffer();
return NS_ERROR_NOT_AVAILABLE;
}
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
keyFrame = 0;
}
@ -137,11 +150,6 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
}
if (!(mVideoBuffer != nullptr && mVideoBuffer->size() > 0 && mVideoBuffer->data() != nullptr)) {
ALOG("mVideoBuffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
int32_t stride = mFrameInfo.mStride;
int32_t slice_height = mFrameInfo.mSliceHeight;
@ -268,9 +276,11 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
{
VideoData* data = nullptr;
nsresult rv = CreateVideoData(aStreamOffset, &data);
// Frame should be non null only when we succeeded.
if (rv != NS_OK || data == nullptr){
ALOG("Error unexpected in CreateVideoData");
if (rv == NS_ERROR_NOT_AVAILABLE) {
// Decoder outputs a empty video buffer, try again
return NS_ERROR_NOT_AVAILABLE;
} else if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create VideoData");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
@ -293,7 +303,18 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
}
case android::ERROR_END_OF_STREAM:
{
ALOG("End of Stream");
ALOG("Got the EOS frame!");
VideoData* data = nullptr;
nsresult rv = CreateVideoData(aStreamOffset, &data);
if (rv == NS_ERROR_NOT_AVAILABLE) {
// For EOS, no need to do any thing.
return NS_ERROR_ABORT;
}
if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create video data");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
return NS_ERROR_ABORT;
}
case -ETIMEDOUT:
@ -325,17 +346,24 @@ void GonkVideoDecoderManager::ReleaseVideoBuffer() {
nsresult
GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
{
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
// Forward sample data to the decoder.
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
if (mDecoder == nullptr) {
ALOG("Decoder is not inited");
return NS_ERROR_UNEXPECTED;
}
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
status_t rv;
if (aSample != nullptr) {
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
// Forward sample data to the decoder.
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
}
else {
// Inputted data is null, so it is going to notify decoder EOS
rv = mDecoder->Input(nullptr, 0, 0ll, 0);
}
return (rv == OK) ? NS_OK : NS_ERROR_FAILURE;
}