зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1033912 - Separate MediaTaskQueues for the video/audio decoding. r=cpearce, r=brsun
This commit is contained in:
Родитель
d7c7e3a117
Коммит
c59bde3672
|
@ -28,7 +28,10 @@
|
|||
#include "gfx2DGlue.h"
|
||||
|
||||
#include "MediaStreamSource.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
|
||||
using namespace android;
|
||||
|
@ -125,8 +128,11 @@ MediaCodecReader::Track::Track()
|
|||
: mDurationUs(INT64_C(0))
|
||||
, mInputIndex(sInvalidInputIndex)
|
||||
, mInputEndOfStream(false)
|
||||
, mOutputEndOfStream(false)
|
||||
, mSeekTimeUs(sInvalidTimestampUs)
|
||||
, mFlushed(false)
|
||||
, mDiscontinuity(false)
|
||||
, mTaskQueue(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -220,12 +226,61 @@ MediaCodecReader::Shutdown()
|
|||
ReleaseResources();
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::DecodeAudioData()
|
||||
void
|
||||
MediaCodecReader::DispatchAudioTask()
|
||||
{
|
||||
MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
if (mAudioTrack.mTaskQueue && mAudioTrack.mTaskQueue->IsEmpty()) {
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethod(this,
|
||||
&MediaCodecReader::DecodeAudioDataTask);
|
||||
mAudioTrack.mTaskQueue->Dispatch(task);
|
||||
}
|
||||
}
|
||||
|
||||
if (mAudioTrack.mCodec == nullptr || !mAudioTrack.mCodec->allocated()) {
|
||||
void
|
||||
MediaCodecReader::DispatchVideoTask(int64_t aTimeThreshold)
|
||||
{
|
||||
if (mVideoTrack.mTaskQueue && mVideoTrack.mTaskQueue->IsEmpty()) {
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArg<int64_t>(this,
|
||||
&MediaCodecReader::DecodeVideoFrameTask,
|
||||
aTimeThreshold);
|
||||
mVideoTrack.mTaskQueue->Dispatch(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::RequestAudioData()
|
||||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(HasAudio());
|
||||
if (CheckAudioResources()) {
|
||||
DispatchAudioTask();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(HasVideo());
|
||||
|
||||
int64_t threshold = sInvalidTimestampUs;
|
||||
if (aSkipToNextKeyframe && IsValidTimestampUs(aTimeThreshold)) {
|
||||
mVideoTrack.mTaskQueue->Flush();
|
||||
threshold = aTimeThreshold;
|
||||
}
|
||||
if (CheckVideoResources()) {
|
||||
DispatchVideoTask(threshold);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::DecodeAudioDataSync()
|
||||
{
|
||||
if (mAudioTrack.mCodec == nullptr || !mAudioTrack.mCodec->allocated() ||
|
||||
mAudioTrack.mOutputEndOfStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -234,14 +289,22 @@ MediaCodecReader::DecodeAudioData()
|
|||
status_t status;
|
||||
TimeStamp timeout = TimeStamp::Now() + TimeDuration::FromSeconds(sMaxAudioDecodeDurationS);
|
||||
while (true) {
|
||||
if (timeout < TimeStamp::Now()) {
|
||||
return true; // Try it again later.
|
||||
}
|
||||
// Try to fill more input buffers and then get one output buffer.
|
||||
// FIXME: use callback from MediaCodec
|
||||
FillCodecInputData(mAudioTrack);
|
||||
|
||||
status = GetCodecOutputData(mAudioTrack, bufferInfo, sInvalidTimestampUs, timeout);
|
||||
if (status == OK || status == ERROR_END_OF_STREAM) {
|
||||
break;
|
||||
} else if (status == -EAGAIN) {
|
||||
return true; // Try it again later.
|
||||
if (TimeStamp::Now() > timeout) {
|
||||
// Don't let this loop run for too long. Try it again later.
|
||||
if (CheckAudioResources()) {
|
||||
DispatchAudioTask();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
continue; // Try it again now.
|
||||
} else if (status == INFO_FORMAT_CHANGED) {
|
||||
if (UpdateAudioInfo()) {
|
||||
continue; // Try it again now.
|
||||
|
@ -253,8 +316,7 @@ MediaCodecReader::DecodeAudioData()
|
|||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
bool result = false;
|
||||
if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 && bufferInfo.mBuffer->data() != nullptr) {
|
||||
// This is the approximate byte position in the stream.
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
|
@ -273,137 +335,52 @@ MediaCodecReader::DecodeAudioData()
|
|||
mInfo.mAudio.mChannels));
|
||||
}
|
||||
|
||||
mAudioTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
|
||||
if (status == ERROR_END_OF_STREAM) {
|
||||
return false;
|
||||
if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
|
||||
(status == ERROR_END_OF_STREAM)) {
|
||||
AudioQueue().Finish();
|
||||
}
|
||||
mAudioTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold)
|
||||
MediaCodecReader::DecodeAudioDataTask()
|
||||
{
|
||||
MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
if (mVideoTrack.mCodec == nullptr || !mVideoTrack.mCodec->allocated()) {
|
||||
return false;
|
||||
bool result = DecodeAudioDataSync();
|
||||
if (AudioQueue().GetSize() > 0) {
|
||||
AudioData* a = AudioQueue().PopFront();
|
||||
if (a) {
|
||||
if (mAudioTrack.mDiscontinuity) {
|
||||
a->mDiscontinuity = true;
|
||||
mAudioTrack.mDiscontinuity = false;
|
||||
}
|
||||
|
||||
int64_t threshold = sInvalidTimestampUs;
|
||||
if (aKeyframeSkip && IsValidTimestampUs(aTimeThreshold)) {
|
||||
threshold = aTimeThreshold;
|
||||
}
|
||||
|
||||
// Get one video output data from MediaCodec
|
||||
CodecBufferInfo bufferInfo;
|
||||
status_t status;
|
||||
TimeStamp timeout = TimeStamp::Now() + TimeDuration::FromSeconds(sMaxVideoDecodeDurationS);
|
||||
while (true) {
|
||||
if (timeout < TimeStamp::Now()) {
|
||||
return true; // Try it again later.
|
||||
}
|
||||
status = GetCodecOutputData(mVideoTrack, bufferInfo, threshold, timeout);
|
||||
if (status == OK || status == ERROR_END_OF_STREAM) {
|
||||
break;
|
||||
} else if (status == -EAGAIN) {
|
||||
return true; // Try it again later.
|
||||
} else if (status == INFO_FORMAT_CHANGED) {
|
||||
if (UpdateVideoInfo()) {
|
||||
continue; // Try it again now.
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
GetCallback()->OnAudioDecoded(a);
|
||||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 && bufferInfo.mBuffer->data() != nullptr) {
|
||||
uint8_t *yuv420p_buffer = bufferInfo.mBuffer->data();
|
||||
int32_t stride = mVideoTrack.mStride;
|
||||
int32_t slice_height = mVideoTrack.mSliceHeight;
|
||||
|
||||
// Converts to OMX_COLOR_FormatYUV420Planar
|
||||
if (mVideoTrack.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
|
||||
ARect crop;
|
||||
crop.top = 0;
|
||||
crop.bottom = mVideoTrack.mHeight;
|
||||
crop.left = 0;
|
||||
crop.right = mVideoTrack.mWidth;
|
||||
|
||||
yuv420p_buffer = GetColorConverterBuffer(mVideoTrack.mWidth, mVideoTrack.mHeight);
|
||||
if (mColorConverter.convertDecoderOutputToI420(
|
||||
bufferInfo.mBuffer->data(), mVideoTrack.mWidth, mVideoTrack.mHeight, crop, yuv420p_buffer) != OK) {
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
NS_WARNING("Unable to convert color format");
|
||||
return false;
|
||||
if (AudioQueue().AtEndOfStream()) {
|
||||
GetCallback()->OnAudioEOS();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
stride = mVideoTrack.mWidth;
|
||||
slice_height = mVideoTrack.mHeight;
|
||||
bool
|
||||
MediaCodecReader::DecodeVideoFrameTask(int64_t aTimeThreshold)
|
||||
{
|
||||
bool result = DecodeVideoFrameSync(aTimeThreshold);
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
VideoData* v = VideoQueue().PopFront();
|
||||
if (v) {
|
||||
if (mVideoTrack.mDiscontinuity) {
|
||||
v->mDiscontinuity = true;
|
||||
mVideoTrack.mDiscontinuity = false;
|
||||
}
|
||||
|
||||
size_t yuv420p_y_size = stride * slice_height;
|
||||
size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
|
||||
uint8_t *yuv420p_y = yuv420p_buffer;
|
||||
uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
|
||||
uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
|
||||
|
||||
// This is the approximate byte position in the stream.
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
b.mPlanes[0].mData = yuv420p_y;
|
||||
b.mPlanes[0].mWidth = mVideoTrack.mWidth;
|
||||
b.mPlanes[0].mHeight = mVideoTrack.mHeight;
|
||||
b.mPlanes[0].mStride = stride;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
b.mPlanes[1].mData = yuv420p_u;
|
||||
b.mPlanes[1].mWidth = (mVideoTrack.mWidth + 1) / 2;
|
||||
b.mPlanes[1].mHeight = (mVideoTrack.mHeight + 1) / 2;
|
||||
b.mPlanes[1].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[1].mOffset = 0;
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
b.mPlanes[2].mData = yuv420p_v;
|
||||
b.mPlanes[2].mWidth =(mVideoTrack.mWidth + 1) / 2;
|
||||
b.mPlanes[2].mHeight = (mVideoTrack.mHeight + 1) / 2;
|
||||
b.mPlanes[2].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
VideoData *v = VideoData::Create(
|
||||
mInfo.mVideo,
|
||||
mDecoder->GetImageContainer(),
|
||||
pos,
|
||||
bufferInfo.mTimeUs,
|
||||
1, // We don't know the duration.
|
||||
b,
|
||||
bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_SYNCFRAME,
|
||||
-1,
|
||||
mVideoTrack.mRelativePictureRect);
|
||||
|
||||
if (v != nullptr) {
|
||||
result = true;
|
||||
mVideoQueue.Push(v);
|
||||
aKeyframeSkip = false;
|
||||
} else {
|
||||
NS_WARNING("Unable to create VideoData");
|
||||
GetCallback()->OnVideoDecoded(v);
|
||||
}
|
||||
}
|
||||
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
|
||||
if (status == ERROR_END_OF_STREAM) {
|
||||
return false;
|
||||
if (VideoQueue().AtEndOfStream()) {
|
||||
GetCallback()->OnVideoEOS();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -475,6 +452,148 @@ MediaCodecReader::ReadMetadata(MediaInfo* aInfo,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaCodecReader::ResetDecode()
|
||||
{
|
||||
if (CheckAudioResources()) {
|
||||
mAudioTrack.mTaskQueue->Flush();
|
||||
FlushCodecData(mAudioTrack);
|
||||
mAudioTrack.mDiscontinuity = true;
|
||||
}
|
||||
if (CheckVideoResources()) {
|
||||
mVideoTrack.mTaskQueue->Flush();
|
||||
FlushCodecData(mVideoTrack);
|
||||
mVideoTrack.mDiscontinuity = true;
|
||||
}
|
||||
|
||||
return MediaDecoderReader::ResetDecode();
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::DecodeVideoFrameSync(int64_t aTimeThreshold)
|
||||
{
|
||||
if (mVideoTrack.mCodec == nullptr || !mVideoTrack.mCodec->allocated() ||
|
||||
mVideoTrack.mOutputEndOfStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get one video output data from MediaCodec
|
||||
CodecBufferInfo bufferInfo;
|
||||
status_t status;
|
||||
TimeStamp timeout = TimeStamp::Now() + TimeDuration::FromSeconds(sMaxVideoDecodeDurationS);
|
||||
while (true) {
|
||||
// Try to fill more input buffers and then get one output buffer.
|
||||
// FIXME: use callback from MediaCodec
|
||||
FillCodecInputData(mVideoTrack);
|
||||
|
||||
status = GetCodecOutputData(mVideoTrack, bufferInfo, aTimeThreshold, timeout);
|
||||
if (status == OK || status == ERROR_END_OF_STREAM) {
|
||||
break;
|
||||
} else if (status == -EAGAIN) {
|
||||
if (TimeStamp::Now() > timeout) {
|
||||
// Don't let this loop run for too long. Try it again later.
|
||||
if (CheckVideoResources()) {
|
||||
DispatchVideoTask(aTimeThreshold);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
continue; // Try it again now.
|
||||
} else if (status == INFO_FORMAT_CHANGED) {
|
||||
if (UpdateVideoInfo()) {
|
||||
continue; // Try it again now.
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (bufferInfo.mBuffer != nullptr && bufferInfo.mSize > 0 && bufferInfo.mBuffer->data() != nullptr) {
|
||||
uint8_t *yuv420p_buffer = bufferInfo.mBuffer->data();
|
||||
int32_t stride = mVideoTrack.mStride;
|
||||
int32_t slice_height = mVideoTrack.mSliceHeight;
|
||||
|
||||
// Converts to OMX_COLOR_FormatYUV420Planar
|
||||
if (mVideoTrack.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
|
||||
ARect crop;
|
||||
crop.top = 0;
|
||||
crop.bottom = mVideoTrack.mHeight;
|
||||
crop.left = 0;
|
||||
crop.right = mVideoTrack.mWidth;
|
||||
|
||||
yuv420p_buffer = GetColorConverterBuffer(mVideoTrack.mWidth, mVideoTrack.mHeight);
|
||||
if (mColorConverter.convertDecoderOutputToI420(
|
||||
bufferInfo.mBuffer->data(), mVideoTrack.mWidth, mVideoTrack.mHeight, crop, yuv420p_buffer) != OK) {
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
NS_WARNING("Unable to convert color format");
|
||||
return false;
|
||||
}
|
||||
|
||||
stride = mVideoTrack.mWidth;
|
||||
slice_height = mVideoTrack.mHeight;
|
||||
}
|
||||
|
||||
size_t yuv420p_y_size = stride * slice_height;
|
||||
size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
|
||||
uint8_t *yuv420p_y = yuv420p_buffer;
|
||||
uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
|
||||
uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
|
||||
|
||||
// This is the approximate byte position in the stream.
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
b.mPlanes[0].mData = yuv420p_y;
|
||||
b.mPlanes[0].mWidth = mVideoTrack.mWidth;
|
||||
b.mPlanes[0].mHeight = mVideoTrack.mHeight;
|
||||
b.mPlanes[0].mStride = stride;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
b.mPlanes[1].mData = yuv420p_u;
|
||||
b.mPlanes[1].mWidth = (mVideoTrack.mWidth + 1) / 2;
|
||||
b.mPlanes[1].mHeight = (mVideoTrack.mHeight + 1) / 2;
|
||||
b.mPlanes[1].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[1].mOffset = 0;
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
b.mPlanes[2].mData = yuv420p_v;
|
||||
b.mPlanes[2].mWidth =(mVideoTrack.mWidth + 1) / 2;
|
||||
b.mPlanes[2].mHeight = (mVideoTrack.mHeight + 1) / 2;
|
||||
b.mPlanes[2].mStride = (stride + 1) / 2;
|
||||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
VideoData *v = VideoData::Create(
|
||||
mInfo.mVideo,
|
||||
mDecoder->GetImageContainer(),
|
||||
pos,
|
||||
bufferInfo.mTimeUs,
|
||||
1, // We don't know the duration.
|
||||
b,
|
||||
bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_SYNCFRAME,
|
||||
-1,
|
||||
mVideoTrack.mRelativePictureRect);
|
||||
|
||||
if (v != nullptr) {
|
||||
result = true;
|
||||
VideoQueue().Push(v);
|
||||
} else {
|
||||
NS_WARNING("Unable to create VideoData");
|
||||
}
|
||||
}
|
||||
|
||||
if ((bufferInfo.mFlags & MediaCodec::BUFFER_FLAG_EOS) ||
|
||||
(status == ERROR_END_OF_STREAM)) {
|
||||
VideoQueue().Finish();
|
||||
}
|
||||
mVideoTrack.mCodec->releaseOutputBuffer(bufferInfo.mIndex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaCodecReader::Seek(int64_t aTime,
|
||||
int64_t aStartTime,
|
||||
|
@ -483,6 +602,16 @@ MediaCodecReader::Seek(int64_t aTime,
|
|||
{
|
||||
MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
mVideoTrack.mSeekTimeUs = aTime;
|
||||
mAudioTrack.mSeekTimeUs = aTime;
|
||||
mVideoTrack.mInputEndOfStream = false;
|
||||
mVideoTrack.mOutputEndOfStream = false;
|
||||
mAudioTrack.mInputEndOfStream = false;
|
||||
mAudioTrack.mOutputEndOfStream = false;
|
||||
mAudioTrack.mFlushed = false;
|
||||
mVideoTrack.mFlushed = false;
|
||||
|
||||
if (CheckVideoResources()) {
|
||||
VideoFrameContainer* videoframe = mDecoder->GetVideoFrameContainer();
|
||||
if (videoframe != nullptr) {
|
||||
mozilla::layers::ImageContainer *image = videoframe->GetImageContainer();
|
||||
|
@ -491,35 +620,35 @@ MediaCodecReader::Seek(int64_t aTime,
|
|||
}
|
||||
}
|
||||
|
||||
mAudioTrack.mInputEndOfStream = false;
|
||||
mVideoTrack.mInputEndOfStream = false;
|
||||
|
||||
mAudioTrack.mSeekTimeUs = aTime;
|
||||
mVideoTrack.mSeekTimeUs = aTime;
|
||||
|
||||
mAudioTrack.mFlushed = false;
|
||||
mVideoTrack.mFlushed = false;
|
||||
|
||||
// Regulate the seek time to the closest sync point of video data.
|
||||
if (HasVideo() && mVideoTrack.mSource != nullptr) {
|
||||
MediaBuffer *source_buffer = nullptr;
|
||||
MediaBuffer* source_buffer = nullptr;
|
||||
MediaSource::ReadOptions options;
|
||||
int64_t timestamp = sInvalidTimestampUs;
|
||||
options.setSeekTo(aTime, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
|
||||
if (mVideoTrack.mSource->read(&source_buffer, &options) != OK || source_buffer == nullptr) {
|
||||
if (mVideoTrack.mSource->read(&source_buffer, &options) != OK ||
|
||||
source_buffer == nullptr) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
sp<MetaData> format = source_buffer->meta_data();
|
||||
if (format != nullptr) {
|
||||
int64_t timestamp = sInvalidTimestampUs;
|
||||
if (format->findInt64(kKeyTime, ×tamp) && IsValidTimestampUs(timestamp)) {
|
||||
mAudioTrack.mSeekTimeUs = timestamp;
|
||||
mVideoTrack.mSeekTimeUs = timestamp;
|
||||
mAudioTrack.mSeekTimeUs = timestamp;
|
||||
}
|
||||
format = nullptr;
|
||||
}
|
||||
source_buffer->release();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mVideoTrack.mTaskQueue->IsEmpty());
|
||||
DispatchVideoTask(mVideoTrack.mSeekTimeUs);
|
||||
|
||||
if (CheckAudioResources()) {
|
||||
MOZ_ASSERT(mAudioTrack.mTaskQueue->IsEmpty());
|
||||
DispatchAudioTask();
|
||||
}
|
||||
} else if (CheckAudioResources()) {// Audio only
|
||||
MOZ_ASSERT(mAudioTrack.mTaskQueue->IsEmpty());
|
||||
DispatchAudioTask();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -542,7 +671,8 @@ MediaCodecReader::ReallocateResources()
|
|||
if (CreateLooper() &&
|
||||
CreateExtractor() &&
|
||||
CreateMediaSources() &&
|
||||
CreateMediaCodecs()) {
|
||||
CreateMediaCodecs() &&
|
||||
CreateTaskQueues()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -573,6 +703,7 @@ MediaCodecReader::ReleaseResources()
|
|||
DestroyMediaSources();
|
||||
DestroyExtractor();
|
||||
DestroyLooper();
|
||||
ShutdownTaskQueues();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -711,6 +842,38 @@ MediaCodecReader::DestroyMediaSources()
|
|||
mAudioOffloadTrack.mSource = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MediaCodecReader::ShutdownTaskQueues()
|
||||
{
|
||||
if(mAudioTrack.mTaskQueue) {
|
||||
mAudioTrack.mTaskQueue->Shutdown();
|
||||
mAudioTrack.mTaskQueue = nullptr;
|
||||
}
|
||||
if(mVideoTrack.mTaskQueue) {
|
||||
mVideoTrack.mTaskQueue->Shutdown();
|
||||
mVideoTrack.mTaskQueue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::CreateTaskQueues()
|
||||
{
|
||||
if (mAudioTrack.mSource != nullptr && mAudioTrack.mCodec != nullptr &&
|
||||
!mAudioTrack.mTaskQueue) {
|
||||
mAudioTrack.mTaskQueue = new MediaTaskQueue(
|
||||
SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaCodecReader Audio"), 1));
|
||||
NS_ENSURE_TRUE(mAudioTrack.mTaskQueue, false);
|
||||
}
|
||||
if (mVideoTrack.mSource != nullptr && mVideoTrack.mCodec != nullptr &&
|
||||
!mVideoTrack.mTaskQueue) {
|
||||
mVideoTrack.mTaskQueue = new MediaTaskQueue(
|
||||
SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaCodecReader Video"), 1));
|
||||
NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaCodecReader::CreateMediaCodecs()
|
||||
{
|
||||
|
@ -1103,30 +1266,26 @@ MediaCodecReader::GetCodecOutputData(Track &aTrack,
|
|||
{
|
||||
// Read next frame.
|
||||
CodecBufferInfo info;
|
||||
|
||||
// Try to fill more input buffers and then get one output buffer.
|
||||
// FIXME: use callback from MediaCodec
|
||||
status_t status = OK;
|
||||
|
||||
while (status == OK || status == INFO_OUTPUT_BUFFERS_CHANGED ||
|
||||
status == -EAGAIN || status == ERROR_END_OF_STREAM) {
|
||||
// Try to fill more input buffers and then get one output buffer.
|
||||
// FIXME: use callback from MediaCodec
|
||||
status = FillCodecInputData(aTrack);
|
||||
status == -EAGAIN) {
|
||||
|
||||
int64_t duration = (int64_t)(aTimeout - TimeStamp::Now()).ToMicroseconds();
|
||||
if (!IsValidDurationUs(duration)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (status == OK || status == ERROR_END_OF_STREAM) {
|
||||
status = aTrack.mCodec->dequeueOutputBuffer(
|
||||
&info.mIndex, &info.mOffset, &info.mSize, &info.mTimeUs, &info.mFlags, duration);
|
||||
if (info.mFlags & MediaCodec::BUFFER_FLAG_EOS) {
|
||||
// Check EOS first.
|
||||
if (status == ERROR_END_OF_STREAM ||
|
||||
info.mFlags & MediaCodec::BUFFER_FLAG_EOS) {
|
||||
aBuffer = info;
|
||||
aBuffer.mBuffer = aTrack.mOutputBuffers[info.mIndex];
|
||||
aTrack.mOutputEndOfStream = true;
|
||||
return ERROR_END_OF_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == OK) {
|
||||
if (!IsValidTimestampUs(aThreshold) || info.mTimeUs >= aThreshold) {
|
||||
|
|
|
@ -29,6 +29,8 @@ struct MediaCodec;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaTaskQueue;
|
||||
|
||||
class MediaCodecReader : public MediaOmxCommonReader
|
||||
{
|
||||
public:
|
||||
|
@ -53,17 +55,15 @@ public:
|
|||
// irreversible, whereas ReleaseMediaResources() is reversible.
|
||||
virtual void Shutdown();
|
||||
|
||||
// Decodes an unspecified amount of audio data, enqueuing the audio data
|
||||
// in mAudioQueue. Returns true when there's more audio to decode,
|
||||
// false if the audio is finished, end of file has been reached,
|
||||
// or an un-recoverable read error has occured.
|
||||
virtual bool DecodeAudioData();
|
||||
// Flush the MediaTaskQueue, flush MediaCodec and raise the mDiscontinuity.
|
||||
virtual nsresult ResetDecode() MOZ_OVERRIDE;
|
||||
|
||||
// Reads and decodes one video frame. Packets with a timestamp less
|
||||
// than aTimeThreshold will be decoded (unless they're not keyframes
|
||||
// and aKeyframeSkip is true), but will not be added to the queue.
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold);
|
||||
// Disptach a DecodeVideoFrameTask to decode video data.
|
||||
virtual void RequestVideoData(bool aSkipToNextKeyframe,
|
||||
int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
// Disptach a DecodeAduioDataTask to decode video data.
|
||||
virtual void RequestAudioData() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool HasAudio();
|
||||
virtual bool HasVideo();
|
||||
|
@ -111,9 +111,15 @@ protected:
|
|||
|
||||
// playback parameters
|
||||
CheckedUint32 mInputIndex;
|
||||
// mDiscontinuity, mFlushed, mInputEndOfStream, mInputEndOfStream,
|
||||
// mSeekTimeUs don't be protected by a lock because the
|
||||
// mTaskQueue->Flush() will flush all tasks.
|
||||
bool mInputEndOfStream;
|
||||
bool mOutputEndOfStream;
|
||||
int64_t mSeekTimeUs;
|
||||
bool mFlushed; // meaningless when mSeekTimeUs is invalid.
|
||||
bool mDiscontinuity;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
};
|
||||
|
||||
// Receive a message from MessageHandler.
|
||||
|
@ -230,6 +236,24 @@ private:
|
|||
void DestroyMediaCodecs();
|
||||
static void DestroyMediaCodecs(Track &aTrack);
|
||||
|
||||
bool CreateTaskQueues();
|
||||
void ShutdownTaskQueues();
|
||||
bool DecodeVideoFrameTask(int64_t aTimeThreshold);
|
||||
bool DecodeVideoFrameSync(int64_t aTimeThreshold);
|
||||
bool DecodeAudioDataTask();
|
||||
bool DecodeAudioDataSync();
|
||||
void DispatchVideoTask(int64_t aTimeThreshold);
|
||||
void DispatchAudioTask();
|
||||
inline bool CheckVideoResources() {
|
||||
return (HasVideo() && mVideoTrack.mSource != nullptr &&
|
||||
mVideoTrack.mTaskQueue);
|
||||
}
|
||||
|
||||
inline bool CheckAudioResources() {
|
||||
return (HasAudio() && mAudioTrack.mSource != nullptr &&
|
||||
mAudioTrack.mTaskQueue);
|
||||
}
|
||||
|
||||
bool UpdateDuration();
|
||||
bool UpdateAudioInfo();
|
||||
bool UpdateVideoInfo();
|
||||
|
|
Загрузка…
Ссылка в новой задаче