Files
palemoon27/dom/media/webm/SoftwareWebMVideoDecoder.cpp
T
roytam1 dd9173e4d3 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1146086: use promise to Init() in PlatformDecoderModule. r=jya,r=cpearce (aed679865)
- partial of Bug 1128380: Add IsHardwareAccelerated implementation for AVCC and mac decoder. r=cpearce (8b376df05)
- Bug 1192675: P1. Ensure VDA/VT APIs are only ever accessed from the same thread. r=cpearce (fa9c8de6a)
- Bug 1178098 - Report why DXVA initialization failed to about:support. r=cpearce (0b06a28e9)
- Bug 1167690 - Part 1: Hook up NPPVpluginIsPlayingAudio to the plugin process; r=josh (30df04ca2)
- Bug 1167690 - Add NPAPI:AudioControl enums to npapi.h. r=josh (5369f6fa9)
- Bug 1167690 - Part 2: Integrate plugins which support the NPAPI audio extensions with the Audio Channel Service; r=BenWa (145cecdc4)
- Bug 1167690 - Part 3: Hook up NPNVmuteAudioBool to the plugin process; r=josh (36558b729)
- Bug 1167690 - Part 4: Add support for testing plugin audio channel integration to the test plugin; r=josh (04af51882)
2021-08-20 11:16:41 +08:00

257 lines
7.7 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SoftwareWebMVideoDecoder.h"
#include "AbstractMediaDecoder.h"
#include "gfx2DGlue.h"
#include "MediaDecoderStateMachine.h"
#include "MediaResource.h"
#include "nsError.h"
#include "OggReader.h"
#include "TimeUnits.h"
#include "VorbisUtils.h"
#include "WebMBufferedParser.h"
#include "NesteggPacketHolder.h"
#include "prsystem.h"
#include <algorithm>
#define VPX_DONT_DEFINE_STDINT_TYPES
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"
namespace mozilla {
using namespace gfx;
using namespace layers;
SoftwareWebMVideoDecoder::SoftwareWebMVideoDecoder(WebMReader* aReader)
: WebMVideoDecoder(),
mReader(aReader)
{
MOZ_COUNT_CTOR(SoftwareWebMVideoDecoder);
memset(&mVPX, 0, sizeof(vpx_codec_ctx_t));
}
SoftwareWebMVideoDecoder::~SoftwareWebMVideoDecoder()
{
MOZ_COUNT_DTOR(SoftwareWebMVideoDecoder);
}
void
SoftwareWebMVideoDecoder::Shutdown()
{
vpx_codec_destroy(&mVPX);
mReader = nullptr;
}
/* static */
WebMVideoDecoder*
SoftwareWebMVideoDecoder::Create(WebMReader* aReader)
{
return new SoftwareWebMVideoDecoder(aReader);
}
nsRefPtr<InitPromise>
SoftwareWebMVideoDecoder::Init(unsigned int aWidth, unsigned int aHeight)
{
nsresult rv = InitDecoder(aWidth, aHeight);
if (NS_SUCCEEDED(rv)) {
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
}
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult
SoftwareWebMVideoDecoder::InitDecoder(unsigned int aWidth, unsigned int aHeight)
{
int decode_threads = 2; //Default to 2 threads for small sizes or VP8
vpx_codec_iface_t* dx = nullptr;
switch(mReader->GetVideoCodec()) {
case NESTEGG_CODEC_VP8:
dx = vpx_codec_vp8_dx();
break;
case NESTEGG_CODEC_VP9:
dx = vpx_codec_vp9_dx();
if (aWidth >= 2048) {
decode_threads = 8;
} else if (aWidth >= 1024) {
decode_threads = 4;
}
break;
}
// Never exceed the number of system cores!
decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
vpx_codec_dec_cfg_t config;
config.threads = decode_threads;
config.w = aWidth;
config.h = aHeight;
if (!dx || vpx_codec_dec_init(&mVPX, dx, &config, 0)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
SoftwareWebMVideoDecoder::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
MOZ_ASSERT(mReader->OnTaskQueue());
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mReader->GetDecoder());
nsRefPtr<NesteggPacketHolder> holder(mReader->NextPacket(WebMReader::VIDEO));
if (!holder) {
return false;
}
nestegg_packet* packet = holder->Packet();
unsigned int track = 0;
int r = nestegg_packet_track(packet, &track);
if (r == -1) {
return false;
}
unsigned int count = 0;
r = nestegg_packet_count(packet, &count);
if (r == -1) {
return false;
}
if (count > 1) {
NS_WARNING("Packet contains more than one video frame");
return false;
}
int64_t tstamp = holder->Timestamp();
// The end time of this frame is the start time of the next frame. Fetch
// the timestamp of the next packet for this track. If we've reached the
// end of the resource, use the file's duration as the end time of this
// video frame.
int64_t next_tstamp = 0;
nsRefPtr<NesteggPacketHolder> next_holder(mReader->NextPacket(WebMReader::VIDEO));
if (next_holder) {
next_tstamp = next_holder->Timestamp();
mReader->PushVideoPacket(next_holder);
} else {
next_tstamp = tstamp;
next_tstamp += tstamp - mReader->GetLastVideoFrameTime();
}
mReader->SetLastVideoFrameTime(tstamp);
unsigned char* data;
size_t length;
r = nestegg_packet_data(packet, 0, &data, &length);
if (r == -1) {
return false;
}
vpx_codec_stream_info_t si;
memset(&si, 0, sizeof(si));
si.sz = sizeof(si);
if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP8) {
vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
} else if (mReader->GetVideoCodec() == NESTEGG_CODEC_VP9) {
vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
}
if (aKeyframeSkip && (!si.is_kf || tstamp < aTimeThreshold)) {
// Skipping to next keyframe...
a.mParsed++;
a.mDropped++;
return true;
}
if (aKeyframeSkip && si.is_kf) {
aKeyframeSkip = false;
}
if (vpx_codec_decode(&mVPX, data, length, nullptr, 0)) {
return false;
}
// If the timestamp of the video frame is less than
// the time threshold required then it is not added
// to the video queue and won't be displayed.
if (tstamp < aTimeThreshold) {
a.mParsed++;
a.mDropped++;
return true;
}
vpx_codec_iter_t iter = nullptr;
vpx_image_t *img;
while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420, "WebM image format not I420");
// Chroma shifts are rounded down as per the decoding examples in the SDK
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = img->planes[0];
b.mPlanes[0].mStride = img->stride[0];
b.mPlanes[0].mHeight = img->d_h;
b.mPlanes[0].mWidth = img->d_w;
b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
b.mPlanes[1].mData = img->planes[1];
b.mPlanes[1].mStride = img->stride[1];
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
b.mPlanes[2].mData = img->planes[2];
b.mPlanes[2].mStride = img->stride[2];
b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
nsIntRect pictureRect = mReader->GetPicture();
IntRect picture = pictureRect;
nsIntSize initFrame = mReader->GetInitialFrame();
if (img->d_w != static_cast<uint32_t>(initFrame.width) ||
img->d_h != static_cast<uint32_t>(initFrame.height)) {
// Frame size is different from what the container reports. This is
// legal in WebM, and we will preserve the ratio of the crop rectangle
// as it was reported relative to the picture size reported by the
// container.
picture.x = (pictureRect.x * img->d_w) / initFrame.width;
picture.y = (pictureRect.y * img->d_h) / initFrame.height;
picture.width = (img->d_w * pictureRect.width) / initFrame.width;
picture.height = (img->d_h * pictureRect.height) / initFrame.height;
}
VideoInfo videoInfo = mReader->GetMediaInfo().mVideo;
nsRefPtr<VideoData> v = VideoData::Create(videoInfo,
mReader->GetDecoder()->GetImageContainer(),
holder->Offset(),
tstamp,
next_tstamp - tstamp,
b,
si.is_kf,
-1,
picture);
if (!v) {
return false;
}
a.mParsed++;
a.mDecoded++;
NS_ASSERTION(a.mDecoded <= a.mParsed,
"Expect only 1 frame per chunk per packet in WebM...");
mReader->VideoQueue().Push(v);
}
return true;
}
} // namespace mozilla