gecko-dev/dom/media/encoder/VP8TrackEncoder.cpp

717 lines
24 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "VP8TrackEncoder.h"
#include "GeckoProfiler.h"
#include "LayersLogging.h"
#include "libyuv.h"
#include "mozilla/gfx/2D.h"
#include "prsystem.h"
#include "VideoSegment.h"
#include "VideoUtils.h"
#include "vpx/vp8cx.h"
#include "vpx/vpx_encoder.h"
#include "WebMWriter.h"
#include "mozilla/media/MediaUtils.h"
#include "mozilla/dom/ImageUtils.h"
#include "mozilla/dom/ImageBitmapBinding.h"
namespace mozilla {
LazyLogModule gVP8TrackEncoderLog("VP8TrackEncoder");
#define VP8LOG(level, msg, ...) MOZ_LOG(gVP8TrackEncoderLog, \
level, \
(msg, ##__VA_ARGS__))
#define DEFAULT_BITRATE_BPS 2500000
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::media;
using namespace mozilla::dom;
static already_AddRefed<SourceSurface>
GetSourceSurface(already_AddRefed<Image> aImg)
{
RefPtr<Image> img = aImg;
if (!img) {
return nullptr;
}
if (!img->AsGLImage() || NS_IsMainThread()) {
RefPtr<SourceSurface> surf = img->GetAsSourceSurface();
return surf.forget();
}
// GLImage::GetAsSourceSurface() only supports main thread
RefPtr<SourceSurface> surf;
RefPtr<Runnable> runnable = NewRunnableFrom([img, &surf]() -> nsresult {
surf = img->GetAsSourceSurface();
return NS_OK;
});
NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
return surf.forget();
}
VP8TrackEncoder::VP8TrackEncoder(TrackRate aTrackRate,
FrameDroppingMode aFrameDroppingMode)
: VideoTrackEncoder(aTrackRate, aFrameDroppingMode)
, mEncodedTimestamp(0)
, mVPXContext(new vpx_codec_ctx_t())
, mVPXImageWrapper(new vpx_image_t())
{
MOZ_COUNT_CTOR(VP8TrackEncoder);
}
VP8TrackEncoder::~VP8TrackEncoder()
{
Destroy();
MOZ_COUNT_DTOR(VP8TrackEncoder);
}
void
VP8TrackEncoder::Destroy()
{
if (mInitialized) {
vpx_codec_destroy(mVPXContext);
}
if (mVPXImageWrapper) {
vpx_img_free(mVPXImageWrapper);
}
mInitialized = false;
}
nsresult
VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
int32_t aDisplayHeight)
{
if (aWidth < 1 || aHeight < 1 || aDisplayWidth < 1 || aDisplayHeight < 1) {
return NS_ERROR_FAILURE;
}
if (mInitialized) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
// Encoder configuration structure.
vpx_codec_enc_cfg_t config;
nsresult rv = SetConfigurationValues(aWidth, aHeight, aDisplayWidth, aDisplayHeight, config);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Creating a wrapper to the image - setting image data to NULL. Actual
// pointer will be set in encode. Setting align to 1, as it is meaningless
// (actual memory is not allocated).
vpx_img_wrap(mVPXImageWrapper, VPX_IMG_FMT_I420,
mFrameWidth, mFrameHeight, 1, nullptr);
vpx_codec_flags_t flags = 0;
flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
if (vpx_codec_enc_init(mVPXContext, vpx_codec_vp8_cx(), &config, flags)) {
return NS_ERROR_FAILURE;
}
vpx_codec_control(mVPXContext, VP8E_SET_STATIC_THRESHOLD, 1);
vpx_codec_control(mVPXContext, VP8E_SET_CPUUSED, -6);
vpx_codec_control(mVPXContext, VP8E_SET_TOKEN_PARTITIONS,
VP8_ONE_TOKENPARTITION);
SetInitialized();
return NS_OK;
}
nsresult
VP8TrackEncoder::Reconfigure(int32_t aWidth, int32_t aHeight,
int32_t aDisplayWidth, int32_t aDisplayHeight)
{
if(aWidth <= 0 || aHeight <= 0 || aDisplayWidth <= 0 || aDisplayHeight <= 0) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
if (!mInitialized) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
// Recreate image wrapper
vpx_img_free(mVPXImageWrapper);
vpx_img_wrap(mVPXImageWrapper, VPX_IMG_FMT_I420, aWidth, aHeight, 1, nullptr);
// Encoder configuration structure.
vpx_codec_enc_cfg_t config;
nsresult rv = SetConfigurationValues(aWidth, aHeight, aDisplayWidth, aDisplayHeight, config);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Set new configuration
if (vpx_codec_enc_config_set(mVPXContext.get(), &config) != VPX_CODEC_OK) {
VP8LOG(LogLevel::Error, "Failed to set new configuration");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
VP8TrackEncoder::SetConfigurationValues(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
int32_t aDisplayHeight, vpx_codec_enc_cfg_t& config)
{
mFrameWidth = aWidth;
mFrameHeight = aHeight;
mDisplayWidth = aDisplayWidth;
mDisplayHeight = aDisplayHeight;
// Encoder configuration structure.
memset(&config, 0, sizeof(vpx_codec_enc_cfg_t));
if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config, 0)) {
VP8LOG(LogLevel::Error, "Failed to get default configuration");
return NS_ERROR_FAILURE;
}
config.g_w = mFrameWidth;
config.g_h = mFrameHeight;
// TODO: Maybe we should have various aFrameRate bitrate pair for each devices?
// or for different platform
// rc_target_bitrate needs kbit/s
config.rc_target_bitrate = (mVideoBitrate != 0 ? mVideoBitrate : DEFAULT_BITRATE_BPS)/1000;
// Setting the time base of the codec
config.g_timebase.num = 1;
config.g_timebase.den = mTrackRate;
config.g_error_resilient = 0;
config.g_lag_in_frames = 0; // 0- no frame lagging
int32_t number_of_cores = PR_GetNumberOfProcessors();
if (mFrameWidth * mFrameHeight > 1280 * 960 && number_of_cores >= 6) {
config.g_threads = 3; // 3 threads for 1080p.
} else if (mFrameWidth * mFrameHeight > 640 * 480 && number_of_cores >= 3) {
config.g_threads = 2; // 2 threads for qHD/HD.
} else {
config.g_threads = 1; // 1 thread for VGA or less
}
// rate control settings
config.rc_dropframe_thresh = 0;
config.rc_end_usage = VPX_VBR;
config.g_pass = VPX_RC_ONE_PASS;
// ffmpeg doesn't currently support streams that use resize.
// Therefore, for safety, we should turn it off until it does.
config.rc_resize_allowed = 0;
config.rc_undershoot_pct = 100;
config.rc_overshoot_pct = 15;
config.rc_buf_initial_sz = 500;
config.rc_buf_optimal_sz = 600;
config.rc_buf_sz = 1000;
config.kf_mode = VPX_KF_AUTO;
// Ensure that we can output one I-frame per second.
config.kf_max_dist = 60;
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
VP8TrackEncoder::GetMetadata()
{
Bug 1375392 - Tweak the PROFILER_LABEL* macros. r=mstange. This patch makes the following changes to the macros. - Removes PROFILER_LABEL_FUNC. It's only suitable for use in functions outside classes, due to PROFILER_FUNCTION_NAME not getting class names, and it was mostly misused. - Removes PROFILER_FUNCTION_NAME. It's no longer used, and __func__ is universally available now anyway. - Combines the first two string literal arguments of PROFILER_LABEL and PROFILER_LABEL_DYNAMIC into a single argument. There was no good reason for them to be separate, and it forced a '::' in the label, which isn't always appropriate. Also, the meaning of the "name_space" argument was interpreted in an interesting variety of ways. - Adds an "AUTO_" prefix to PROFILER_LABEL and PROFILER_LABEL_DYNAMIC, to make it clearer they construct RAII objects rather than just being function calls. (I myself have screwed up the scoping because of this in the past.) - Fills in the 'js::ProfileEntry::Category::' qualifier within the macro, so the caller doesn't need to. This makes a *lot* more of the uses fit onto a single line. The patch also makes the following changes to the macro uses (beyond those required by the changes described above). - Fixes a bunch of labels that had gotten out of sync with the name of the class and/or function that encloses them. - Removes a useless PROFILER_LABEL use within a trivial scope in EventStateManager::DispatchMouseOrPointerEvent(). It clearly wasn't serving any useful purpose. It also serves as extra evidence that the AUTO_ prefix is a good idea. - Tweaks DecodePool::SyncRunIf{Preferred,Possible} so that the labelling is done within them, instead of at their callsites, because that's a more standard way of doing things. --HG-- extra : rebase_source : 318d1bc6fc1425a94aacbf489dd46e4f83211de4
2017-06-22 07:08:53 +00:00
AUTO_PROFILER_LABEL("VP8TrackEncoder::GetMetadata", OTHER);
MOZ_ASSERT(mInitialized || mCanceled);
if (mCanceled || mEncodingComplete) {
return nullptr;
}
if (!mInitialized) {
return nullptr;
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 05:24:48 +00:00
RefPtr<VP8Metadata> meta = new VP8Metadata();
meta->mWidth = mFrameWidth;
meta->mHeight = mFrameHeight;
meta->mDisplayWidth = mDisplayWidth;
meta->mDisplayHeight = mDisplayHeight;
VP8LOG(LogLevel::Info, "GetMetadata() width=%d, height=%d, "
"displayWidht=%d, displayHeight=%d",
meta->mWidth, meta->mHeight, meta->mDisplayWidth, meta->mDisplayHeight);
return meta.forget();
}
nsresult
VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
{
vpx_codec_iter_t iter = nullptr;
EncodedFrame::FrameType frameType = EncodedFrame::VP8_P_FRAME;
nsTArray<uint8_t> frameData;
const vpx_codec_cx_pkt_t *pkt = nullptr;
while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) {
switch (pkt->kind) {
case VPX_CODEC_CX_FRAME_PKT: {
// Copy the encoded data from libvpx to frameData
frameData.AppendElements((uint8_t*)pkt->data.frame.buf,
pkt->data.frame.sz);
break;
}
default: {
break;
}
}
// End of frame
if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
frameType = EncodedFrame::VP8_I_FRAME;
}
break;
}
}
if (!frameData.IsEmpty()) {
// Copy the encoded data to aData.
EncodedFrame* videoData = new EncodedFrame();
videoData->SetFrameType(frameType);
// Convert the timestamp and duration to Usecs.
CheckedInt64 timestamp = FramesToUsecs(pkt->data.frame.pts, mTrackRate);
if (!timestamp.isValid()) {
NS_ERROR("Microsecond timestamp overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
videoData->SetTimeStamp((uint64_t)timestamp.value());
mExtractedDuration += pkt->data.frame.duration;
if (!mExtractedDuration.isValid()) {
NS_ERROR("Duration overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
CheckedInt64 totalDuration =
FramesToUsecs(mExtractedDuration.value(), mTrackRate);
if (!totalDuration.isValid()) {
NS_ERROR("Duration overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
CheckedInt64 duration = totalDuration - mExtractedDurationUs;
if (!duration.isValid()) {
NS_ERROR("Duration overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
mExtractedDurationUs = totalDuration;
videoData->SetDuration((uint64_t)duration.value());
videoData->SwapInFrameData(frameData);
VP8LOG(LogLevel::Verbose,
"GetEncodedPartitions TimeStamp %" PRIu64 ", Duration %" PRIu64 ", FrameType %d",
videoData->GetTimeStamp(), videoData->GetDuration(),
videoData->GetFrameType());
aData.AppendEncodedFrame(videoData);
}
return pkt ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
{
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 05:24:48 +00:00
RefPtr<Image> img;
if (aChunk.mFrame.GetForceBlack() || aChunk.IsNull()) {
if (!mMuteFrame) {
mMuteFrame = VideoFrame::CreateBlackImage(gfx::IntSize(mFrameWidth, mFrameHeight));
}
if (!mMuteFrame) {
VP8LOG(LogLevel::Warning, "Failed to allocate black image of size %dx%d",
mFrameWidth, mFrameHeight);
return NS_OK;
}
img = mMuteFrame;
} else {
img = aChunk.mFrame.GetImage();
}
if (img->GetSize() != IntSize(mFrameWidth, mFrameHeight)) {
VP8LOG(LogLevel::Info,
"Dynamic resolution change (was %dx%d, now %dx%d).",
mFrameWidth, mFrameHeight, img->GetSize().width, img->GetSize().height);
gfx::IntSize intrinsicSize = aChunk.mFrame.GetIntrinsicSize();
gfx::IntSize imgSize = aChunk.mFrame.GetImage()->GetSize();
if (imgSize <= IntSize(mFrameWidth, mFrameHeight) && // check buffer size instead
// If the new size is less than or equal to old,
// the existing encoder instance can continue.
NS_SUCCEEDED(Reconfigure(imgSize.width,
imgSize.height,
intrinsicSize.width,
intrinsicSize.height))) {
VP8LOG(LogLevel::Info, "Reconfigured VP8 encoder.");
} else {
// New frame size is larger; re-create the encoder.
Destroy();
nsresult rv = Init(imgSize.width,
imgSize.height,
intrinsicSize.width,
intrinsicSize.height);
VP8LOG(LogLevel::Info, "Recreated VP8 encoder.");
NS_ENSURE_SUCCESS(rv, rv);
}
}
ImageFormat format = img->GetFormat();
if (format == ImageFormat::PLANAR_YCBCR) {
PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
MOZ_RELEASE_ASSERT(yuv);
if (!yuv->IsValid()) {
NS_WARNING("PlanarYCbCrImage is not valid");
return NS_ERROR_FAILURE;
}
// The ImageUtils API may change depending on our support for ImageBitmap
// extensions. Should this happen in a breaking way we should abstract out
// the format detection for use here.
const ImageUtils imageUtils(img);
const ImageBitmapFormat imageBitmapFormat = imageUtils.GetFormat();
if (imageBitmapFormat == ImageBitmapFormat::YUV420P) {
// 420 planar, no need for conversions
const PlanarYCbCrImage::Data* data = yuv->GetData();
mVPXImageWrapper->planes[VPX_PLANE_Y] = data->mYChannel;
mVPXImageWrapper->planes[VPX_PLANE_U] = data->mCbChannel;
mVPXImageWrapper->planes[VPX_PLANE_V] = data->mCrChannel;
mVPXImageWrapper->stride[VPX_PLANE_Y] = data->mYStride;
mVPXImageWrapper->stride[VPX_PLANE_U] = data->mCbCrStride;
mVPXImageWrapper->stride[VPX_PLANE_V] = data->mCbCrStride;
return NS_OK;
}
}
// Not 420 planar, have to convert
uint32_t yPlaneSize = mFrameWidth * mFrameHeight;
uint32_t halfWidth = (mFrameWidth + 1) / 2;
uint32_t halfHeight = (mFrameHeight + 1) / 2;
uint32_t uvPlaneSize = halfWidth * halfHeight;
if (mI420Frame.Length() != yPlaneSize + uvPlaneSize * 2) {
mI420Frame.SetLength(yPlaneSize + uvPlaneSize * 2);
}
uint8_t *y = mI420Frame.Elements();
uint8_t *cb = mI420Frame.Elements() + yPlaneSize;
uint8_t *cr = mI420Frame.Elements() + yPlaneSize + uvPlaneSize;
if (format == ImageFormat::PLANAR_YCBCR) {
PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
MOZ_RELEASE_ASSERT(yuv);
if (!yuv->IsValid()) {
NS_WARNING("PlanarYCbCrImage is not valid");
return NS_ERROR_FAILURE;
}
const ImageUtils imageUtils(img);
const ImageBitmapFormat imageBitmapFormat = imageUtils.GetFormat();
const PlanarYCbCrImage::Data *data = yuv->GetData();
int rv;
std::string yuvFormat;
if (imageBitmapFormat == ImageBitmapFormat::YUV420SP_NV12) {
rv = libyuv::NV12ToI420(data->mYChannel,
data->mYStride,
data->mCbChannel,
data->mCbCrStride,
y,
mFrameWidth,
cb,
halfWidth,
cr,
halfWidth,
mFrameWidth,
mFrameHeight);
yuvFormat = "NV12";
} else if (imageBitmapFormat == ImageBitmapFormat::YUV420SP_NV21) {
rv = libyuv::NV21ToI420(data->mYChannel,
data->mYStride,
data->mCrChannel,
data->mCbCrStride,
y,
mFrameWidth,
cb,
halfWidth,
cr,
halfWidth,
mFrameWidth,
mFrameHeight);
yuvFormat = "NV21";
} else if (imageBitmapFormat == ImageBitmapFormat::YUV444P) {
rv = libyuv::I444ToI420(data->mYChannel,
data->mYStride,
data->mCbChannel,
data->mCbCrStride,
data->mCrChannel,
data->mCbCrStride,
y,
mFrameWidth,
cb,
halfWidth,
cr,
halfWidth,
mFrameWidth,
mFrameHeight);
yuvFormat = "I444";
} else if (imageBitmapFormat == ImageBitmapFormat::YUV422P) {
rv = libyuv::I422ToI420(data->mYChannel,
data->mYStride,
data->mCbChannel,
data->mCbCrStride,
data->mCrChannel,
data->mCbCrStride,
y,
mFrameWidth,
cb,
halfWidth,
cr,
halfWidth,
mFrameWidth,
mFrameHeight);
yuvFormat = "I422";
} else {
VP8LOG(LogLevel::Error, "Unsupported planar format");
NS_ASSERTION(false, "Unsupported planar format");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (rv != 0) {
VP8LOG(LogLevel::Error, "Converting an %s frame to I420 failed", yuvFormat.c_str());
return NS_ERROR_FAILURE;
}
VP8LOG(LogLevel::Verbose, "Converted an %s frame to I420", yuvFormat.c_str());
} else {
// Not YCbCr at all. Try to get access to the raw data and convert.
RefPtr<SourceSurface> surf = GetSourceSurface(img.forget());
if (!surf) {
VP8LOG(LogLevel::Error, "Getting surface from %s image failed", Stringify(format).c_str());
return NS_ERROR_FAILURE;
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 05:24:48 +00:00
RefPtr<DataSourceSurface> data = surf->GetDataSurface();
if (!data) {
VP8LOG(LogLevel::Error, "Getting data surface from %s image with %s (%s) surface failed",
Stringify(format).c_str(), Stringify(surf->GetType()).c_str(),
Stringify(surf->GetFormat()).c_str());
return NS_ERROR_FAILURE;
}
DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
if (!map.IsMapped()) {
VP8LOG(LogLevel::Error, "Reading DataSourceSurface from %s image with %s (%s) surface failed",
Stringify(format).c_str(), Stringify(surf->GetType()).c_str(),
Stringify(surf->GetFormat()).c_str());
return NS_ERROR_FAILURE;
}
int rv;
switch (surf->GetFormat()) {
case SurfaceFormat::B8G8R8A8:
case SurfaceFormat::B8G8R8X8:
rv = libyuv::ARGBToI420(static_cast<uint8*>(map.GetData()),
map.GetStride(),
y, mFrameWidth,
cb, halfWidth,
cr, halfWidth,
mFrameWidth, mFrameHeight);
break;
case SurfaceFormat::R5G6B5_UINT16:
rv = libyuv::RGB565ToI420(static_cast<uint8*>(map.GetData()),
map.GetStride(),
y, mFrameWidth,
cb, halfWidth,
cr, halfWidth,
mFrameWidth, mFrameHeight);
break;
default:
VP8LOG(LogLevel::Error, "Unsupported SourceSurface format %s",
Stringify(surf->GetFormat()).c_str());
NS_ASSERTION(false, "Unsupported SourceSurface format");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (rv != 0) {
VP8LOG(LogLevel::Error, "%s to I420 conversion failed",
Stringify(surf->GetFormat()).c_str());
return NS_ERROR_FAILURE;
}
VP8LOG(LogLevel::Verbose, "Converted a %s frame to I420",
Stringify(surf->GetFormat()).c_str());
}
mVPXImageWrapper->planes[VPX_PLANE_Y] = y;
mVPXImageWrapper->planes[VPX_PLANE_U] = cb;
mVPXImageWrapper->planes[VPX_PLANE_V] = cr;
mVPXImageWrapper->stride[VPX_PLANE_Y] = mFrameWidth;
mVPXImageWrapper->stride[VPX_PLANE_U] = halfWidth;
mVPXImageWrapper->stride[VPX_PLANE_V] = halfWidth;
return NS_OK;
}
// These two define value used in GetNextEncodeOperation to determine the
// EncodeOperation for next target frame.
#define I_FRAME_RATIO (0.5)
#define SKIP_FRAME_RATIO (0.75)
/**
* Compares the elapsed time from the beginning of GetEncodedTrack and
* the processed frame duration in mSourceSegment
* in order to set the nextEncodeOperation for next target frame.
*/
VP8TrackEncoder::EncodeOperation
VP8TrackEncoder::GetNextEncodeOperation(TimeDuration aTimeElapsed,
StreamTime aProcessedDuration)
{
if (mFrameDroppingMode == FrameDroppingMode::DISALLOW) {
return ENCODE_NORMAL_FRAME;
}
int64_t durationInUsec =
FramesToUsecs(aProcessedDuration, mTrackRate).value();
if (aTimeElapsed.ToMicroseconds() > (durationInUsec * SKIP_FRAME_RATIO)) {
// The encoder is too slow.
// We should skip next frame to consume the mSourceSegment.
return SKIP_FRAME;
} else if (aTimeElapsed.ToMicroseconds() > (durationInUsec * I_FRAME_RATIO)) {
// The encoder is a little slow.
// We force the encoder to encode an I-frame to accelerate.
return ENCODE_I_FRAME;
} else {
return ENCODE_NORMAL_FRAME;
}
}
/**
* Encoding flow in GetEncodedTrack():
* 1: Check the mInitialized state and the packet duration.
* 2: Move the data from mRawSegment to mSourceSegment.
* 3: Encode the video chunks in mSourceSegment in a for-loop.
* 3.1: The duration is taken straight from the video chunk's duration.
* 3.2: Setup the video chunk with mVPXImageWrapper by PrepareRawFrame().
* 3.3: Pass frame to vp8 encoder by vpx_codec_encode().
* 3.4: Get the encoded frame from encoder by GetEncodedPartitions().
* 3.5: Set the nextEncodeOperation for the next target frame.
* There is a heuristic: If the frame duration we have processed in
* mSourceSegment is 100ms, means that we can't spend more than 100ms to
* encode it.
* 4. Remove the encoded chunks in mSourceSegment after for-loop.
*/
nsresult
VP8TrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
Bug 1375392 - Tweak the PROFILER_LABEL* macros. r=mstange. This patch makes the following changes to the macros. - Removes PROFILER_LABEL_FUNC. It's only suitable for use in functions outside classes, due to PROFILER_FUNCTION_NAME not getting class names, and it was mostly misused. - Removes PROFILER_FUNCTION_NAME. It's no longer used, and __func__ is universally available now anyway. - Combines the first two string literal arguments of PROFILER_LABEL and PROFILER_LABEL_DYNAMIC into a single argument. There was no good reason for them to be separate, and it forced a '::' in the label, which isn't always appropriate. Also, the meaning of the "name_space" argument was interpreted in an interesting variety of ways. - Adds an "AUTO_" prefix to PROFILER_LABEL and PROFILER_LABEL_DYNAMIC, to make it clearer they construct RAII objects rather than just being function calls. (I myself have screwed up the scoping because of this in the past.) - Fills in the 'js::ProfileEntry::Category::' qualifier within the macro, so the caller doesn't need to. This makes a *lot* more of the uses fit onto a single line. The patch also makes the following changes to the macro uses (beyond those required by the changes described above). - Fixes a bunch of labels that had gotten out of sync with the name of the class and/or function that encloses them. - Removes a useless PROFILER_LABEL use within a trivial scope in EventStateManager::DispatchMouseOrPointerEvent(). It clearly wasn't serving any useful purpose. It also serves as extra evidence that the AUTO_ prefix is a good idea. - Tweaks DecodePool::SyncRunIf{Preferred,Possible} so that the labelling is done within them, instead of at their callsites, because that's a more standard way of doing things. --HG-- extra : rebase_source : 318d1bc6fc1425a94aacbf489dd46e4f83211de4
2017-06-22 07:08:53 +00:00
AUTO_PROFILER_LABEL("VP8TrackEncoder::GetEncodedTrack", OTHER);
MOZ_ASSERT(mInitialized || mCanceled);
if (mCanceled || mEncodingComplete) {
return NS_ERROR_FAILURE;
}
if (!mInitialized) {
return NS_ERROR_FAILURE;
}
TakeTrackData(mSourceSegment);
StreamTime totalProcessedDuration = 0;
TimeStamp timebase = TimeStamp::Now();
EncodeOperation nextEncodeOperation = ENCODE_NORMAL_FRAME;
for (VideoSegment::ChunkIterator iter(mSourceSegment);
!iter.IsEnded(); iter.Next()) {
VideoChunk &chunk = *iter;
VP8LOG(LogLevel::Verbose, "nextEncodeOperation is %d for frame of duration %" PRId64,
nextEncodeOperation, chunk.GetDuration());
// Encode frame.
if (nextEncodeOperation != SKIP_FRAME) {
nsresult rv = PrepareRawFrame(chunk);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
// Encode the data with VP8 encoder
int flags = 0;
if (nextEncodeOperation == ENCODE_I_FRAME) {
VP8LOG(LogLevel::Warning, "MediaRecorder lagging behind. Encoding keyframe.");
flags |= VPX_EFLAG_FORCE_KF;
}
if (vpx_codec_encode(mVPXContext, mVPXImageWrapper, mEncodedTimestamp,
(unsigned long)chunk.GetDuration(), flags,
VPX_DL_REALTIME)) {
VP8LOG(LogLevel::Error, "vpx_codec_encode failed to encode the frame.");
return NS_ERROR_FAILURE;
}
// Get the encoded data from VP8 encoder.
rv = GetEncodedPartitions(aData);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
} else {
// SKIP_FRAME
// Extend the duration of the last encoded data in aData
// because this frame will be skipped.
VP8LOG(LogLevel::Warning, "MediaRecorder lagging behind. Skipping a frame.");
RefPtr<EncodedFrame> last = aData.GetEncodedFrames().LastElement();
if (last) {
mExtractedDuration += chunk.mDuration;
if (!mExtractedDuration.isValid()) {
NS_ERROR("skipped duration overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
CheckedInt64 totalDuration = FramesToUsecs(mExtractedDuration.value(), mTrackRate);
CheckedInt64 skippedDuration = totalDuration - mExtractedDurationUs;
mExtractedDurationUs = totalDuration;
if (!skippedDuration.isValid()) {
NS_ERROR("skipped duration overflow");
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
}
last->SetDuration(last->GetDuration() +
(static_cast<uint64_t>(skippedDuration.value())));
}
}
// Move forward the mEncodedTimestamp.
mEncodedTimestamp += chunk.GetDuration();
totalProcessedDuration += chunk.GetDuration();
// Check what to do next.
TimeDuration elapsedTime = TimeStamp::Now() - timebase;
nextEncodeOperation = GetNextEncodeOperation(elapsedTime,
totalProcessedDuration);
}
// Remove the chunks we have processed.
mSourceSegment.Clear();
// End of stream, pull the rest frames in encoder.
if (mEndOfStream) {
VP8LOG(LogLevel::Debug, "mEndOfStream is true");
mEncodingComplete = true;
// Bug 1243611, keep calling vpx_codec_encode and vpx_codec_get_cx_data
// until vpx_codec_get_cx_data return null.
do {
if (vpx_codec_encode(mVPXContext, nullptr, mEncodedTimestamp,
0, 0, VPX_DL_REALTIME)) {
return NS_ERROR_FAILURE;
}
} while(NS_SUCCEEDED(GetEncodedPartitions(aData)));
}
return NS_OK ;
}
} // namespace mozilla