зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1049325 - Part 1: Notify decoder for input EOS and report MP4Reader when output EOS is reached. r=cpearce
This commit is contained in:
Родитель
2d98c38825
Коммит
88bb55ef9f
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче