Bug 1294154: Handle case where WMF is unable to determine time and duration. r=mattwoodrow

The WMF decoder doesn't handle well the case where a single frame was given to decode.
When draining, the output is a correctly decoded frame but with a time of 0 and a duration set at 1/30th.
This is a workaround

MozReview-Commit-ID: JbjgNmPXKIM

--HG--
extra : rebase_source : f25a1fd503678383265ec5053b41f3116ff52da0
This commit is contained in:
Jean-Yves Avenard 2016-09-02 20:49:17 +10:00
parent d98e95abb8
commit 747d2faea4
3 changed files with 47 additions and 7 deletions

View File

@ -37,12 +37,13 @@ public:
virtual HRESULT Output(int64_t aStreamOffset,
RefPtr<MediaData>& aOutput) = 0;
void Flush() {
virtual void Flush()
{
mDecoder->Flush();
mSeekTargetThreshold.reset();
}
void Drain()
virtual void Drain()
{
if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
NS_WARNING("Failed to send DRAIN command to MFT");

View File

@ -525,6 +525,8 @@ WMFVideoMFTManager::Input(MediaRawData* aSample)
NS_ENSURE_TRUE(SUCCEEDED(hr) && mLastInput != nullptr, hr);
mLastDuration = aSample->mDuration;
mLastTime = aSample->mTime;
mSamplesCount++;
// Forward sample data to the decoder.
return mDecoder->Input(mLastInput);
@ -832,6 +834,15 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset,
HRESULT hr;
aOutData = nullptr;
int typeChangeCount = 0;
bool wasDraining = mDraining;
int64_t sampleCount = mSamplesCount;
if (wasDraining) {
mSamplesCount = 0;
mDraining = false;
}
media::TimeUnit pts;
media::TimeUnit duration;
// Loop until we decode a sample, or an unexpected error that we can't
// handle occurs.
@ -873,12 +884,21 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset,
}
continue;
}
pts = GetSampleTime(sample);
duration = GetSampleDuration(sample);
if (!pts.IsValid() || !duration.IsValid()) {
return E_FAIL;
}
if (wasDraining && sampleCount == 1 && pts == media::TimeUnit()) {
// WMF is unable to calculate a duration if only a single sample
// was parsed. Additionally, the pts always comes out at 0 under those
// circumstances.
// Seeing that we've only fed the decoder a single frame, the pts
// and duration are known, it's of the last sample.
pts = media::TimeUnit::FromMicroseconds(mLastTime);
duration = media::TimeUnit::FromMicroseconds(mLastDuration);
}
if (mSeekTargetThreshold.isSome()) {
media::TimeUnit pts = GetSampleTime(sample);
media::TimeUnit duration = GetSampleDuration(sample);
if (!pts.IsValid() || !duration.IsValid()) {
return E_FAIL;
}
if ((pts + duration) < mSeekTargetThreshold.ref()) {
LOG("Dropping video frame which pts is smaller than seek target.");
// It is necessary to clear the pointer to release the previous output
@ -907,6 +927,9 @@ WMFVideoMFTManager::Output(int64_t aStreamOffset,
NS_ENSURE_TRUE(frame, E_FAIL);
aOutData = frame;
// Set the potentially corrected pts and duration.
aOutData->mTime = pts.ToMicroseconds();
aOutData->mDuration = duration.ToMicroseconds();
if (mNullOutputCount) {
mGotValidOutputAfterNullOutput = true;

View File

@ -49,6 +49,19 @@ public:
? "wmf hardware video decoder" : "wmf software video decoder";
}
void Flush() override
{
MFTManager::Flush();
mDraining = false;
mSamplesCount = 0;
}
void Drain() override
{
MFTManager::Drain();
mDraining = true;
}
private:
bool ValidateVideoInfo();
@ -81,6 +94,9 @@ private:
RefPtr<IMFSample> mLastInput;
float mLastDuration;
int64_t mLastTime = 0;
bool mDraining = false;
int64_t mSamplesCount = 0;
bool mDXVAEnabled;
const layers::LayersBackend mLayersBackend;