Files
palemoon27/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
T
roytam1 0c01ffebb4 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1197669 - Part1 - 1.Enable the testcase on B2G. 2. fix the mimetype check in testcase. r=jwwang (9bbae092f)
- Bug 1197669 - Part2 - 1. Ensure the EOS is sent to MediaCodec. 2. mEndOfStream should be protected by monitor. r=sotaro (62b8e66b7)
- bug 1193922 skip SetIsOnlyNodeForContext after shutdown r=padenot (df991d0af)
- Minor (1d695f7c4)
- Bug 1197051 - Don't try to decode ahead while seeking - r=jya (7d8d877ad)
- Bug 1197075: P1. Revert "Bug 1197051 - Don't try to decode ahead while seeking - r=jya". r=edwin (e837b12e3)
- Bug 1143575. Push all available frames to the compositor. r=cpearce (109e54ae8)
- partial of  Bug 1197075: P2. Revert "Bug 1171257 - Add force decode ahead to MediaFormatReader r=jya,bholley". r=edwin (5f4859e47)
- Minor (a841ca407)
- Bug 1199155. Part 2 - fix includes and forward declarations. r=roc. (ca523595a)
- Bug 1199155. Part 1 - move dom/media/DecodedStream.* to dom/media/mediasink/ and implement the interface of MediaSink. r=roc. (90b376a24)
- Bug 1199155. Part 3 - rename mDecodedStream to mStreamSink. r=roc. (1aae54328)
- Bug 1203374. Part 1 - extract the code of computing canplaythrough so it is reusable. r=jya. (acc5fc8c6)
- Bug 1203374. Part 2 - duplicate the implementation of MediaDecoder::CanPlayThrough so MDSM can call its own CanPlayThrough() on its own thread. r=jya. (a042541a8)
- Bug 1182928 - Disable dormant mode for EME videos in Firefox Beta and Release. r=sotaro (ea5964ae9)
- Bug 1197022 - [EME] Disable dormant mode for EME videos on all channels. r=kentuckyfriedtakahe (742d00672)
- Bug 1179110 - Fix ComputePlaybackRate. r=jww (a43272af5)
- Bug 1178622 - Fix enum-to-string mismatch in MediaDecoder.cpp. r=jya. (b9da7ebf3)
- Bug 1203418. Part 1 - cache the results of ComputePlaybackRate() so they can be mirrored by MDSM. r=cpearce. (95f7ac068)
- Bug 1203418. Part 2 - duplicate the implementation of MediaDecoder::GetStatistics so MDSM can call it on its own thread. r=cpearce. (2755b85a7)
- Bug 1203418. Part 3 - ensure MDSM::mPlaybackOffset and MediaDecoder::mPlaybackPosition are mono-increasing to avoid "jitter" in calculating playback statistics. r=cpearce. (54dcb6219)
- Bug 1104616 - Proxy video capture access to the main process. r=jesup,glandium,mrbkap (cfdd08459)
- Bug 1200614 - Protect the capture engines array from concurrent access (during shutdown). r=jesup (c4dbd6e10)
- Bug 1200614 - Check whether engines are still alive when webrtc ops run. r=jesup (d42d3474f)
- Bug 1194640 - add NSPR logging of camera capabilities, r=jesup (c7365b916)
- Bug 1204413 - Make MediaDecoder::IsTransportSeekable run on the main thread. r=kinetik. (098868b0d)
- Bug 1198202 - Increase hardware video decoding fallback threshold. r=ajones (1059f9b0f)
- Bug 1201197 - add dedicated listener to enumerateDevices. r=jesup (a51477957)
- Bug 1199562. Part 1 - rename mAudioSink to mMediaSink as well as related member names. r=roc. (c2db1b4d4)
- Bug 1199562. Part 2 - replace usage of mStreamSink with mMediaSink in most cases. r=roc. (7aa0754c4)
- Bug 1199562. Part 3 - remove unused code. r=roc. (343c5be2f)
- Bug 1203877 - Remove MediaDecoder::UpdatePlaybackOffset. r=kinetik. (4476dd1f8)
- Bug 1204430. Part 2 - mirror MediaDecoder::mMediaSeekable. r=kinetik. (51ca1197d)
- Bug 1206576 - Dispatch some MDSM functions to hide its internal thread model. r=jya. (74b9a169c)
- Bug 1206574 - Remove AbstractMediaDecoder::IsShutdown(). r=cpearce. (4ed17a27f)
- Bug 1207017. Part 1 - fix coding styles. r=kinetik. (cc10a28c3)
- Bug 1207017. Part 2 - remove duplicated GetStateMachine(). r=kinetik. (19b663098)
- Bug 1200477 - Allow building with WebRTC disabled. r=glandium,mrbkap (56055c3c7)
2021-10-14 09:25:28 +08:00

399 lines
13 KiB
C++

/* 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 "MediaEngineCameraVideoSource.h"
#include <limits>
namespace mozilla {
using namespace mozilla::gfx;
using namespace mozilla::dom;
extern PRLogModuleInfo* GetMediaManagerLog();
#define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
#define LOGFRAME(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
// guts for appending data to the MSG track
bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource,
layers::Image* aImage,
TrackID aID,
StreamTime delta)
{
MOZ_ASSERT(aSource);
VideoSegment segment;
nsRefPtr<layers::Image> image = aImage;
IntSize size(image ? mWidth : 0, image ? mHeight : 0);
segment.AppendFrame(image.forget(), delta, size);
// This is safe from any thread, and is safe if the track is Finished
// or Destroyed.
// This can fail if either a) we haven't added the track yet, or b)
// we've removed or finished the track.
return aSource->AppendToTrack(aID, &(segment));
}
// Sub-classes (B2G or desktop) should overload one of both of these two methods
// to provide capabilities
size_t
MediaEngineCameraVideoSource::NumCapabilities()
{
return mHardcodedCapabilities.Length();
}
void
MediaEngineCameraVideoSource::GetCapability(size_t aIndex,
webrtc::CaptureCapability& aOut)
{
MOZ_ASSERT(aIndex < mHardcodedCapabilities.Length());
aOut = mHardcodedCapabilities[aIndex];
}
uint32_t
MediaEngineCameraVideoSource::GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
const MediaTrackConstraintSet &aConstraints,
bool aAdvanced,
const nsString& aDeviceId)
{
// Treat width|height|frameRate == 0 on capability as "can do any".
// This allows for orthogonal capabilities that are not in discrete steps.
uint64_t distance =
uint64_t(FitnessDistance(aDeviceId, aConstraints.mDeviceId, aAdvanced)) +
uint64_t(FitnessDistance(mFacingMode, aConstraints.mFacingMode, aAdvanced)) +
uint64_t(aCandidate.width? FitnessDistance(int32_t(aCandidate.width),
aConstraints.mWidth,
aAdvanced) : 0) +
uint64_t(aCandidate.height? FitnessDistance(int32_t(aCandidate.height),
aConstraints.mHeight,
aAdvanced) : 0) +
uint64_t(aCandidate.maxFPS? FitnessDistance(double(aCandidate.maxFPS),
aConstraints.mFrameRate,
aAdvanced) : 0);
return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
}
// Find best capability by removing inferiors. May leave >1 of equal distance
/* static */ void
MediaEngineCameraVideoSource::TrimLessFitCandidates(CapabilitySet& set) {
uint32_t best = UINT32_MAX;
for (auto& candidate : set) {
if (best > candidate.mDistance) {
best = candidate.mDistance;
}
}
for (size_t i = 0; i < set.Length();) {
if (set[i].mDistance > best) {
set.RemoveElementAt(i);
} else {
++i;
}
}
MOZ_ASSERT(set.Length());
}
// GetBestFitnessDistance returns the best distance the capture device can offer
// as a whole, given an accumulated number of ConstraintSets.
// Ideal values are considered in the first ConstraintSet only.
// Plain values are treated as Ideal in the first ConstraintSet.
// Plain values are treated as Exact in subsequent ConstraintSets.
// Infinity = UINT32_MAX e.g. device cannot satisfy accumulated ConstraintSets.
// A finite result may be used to calculate this device's ranking as a choice.
uint32_t
MediaEngineCameraVideoSource::GetBestFitnessDistance(
const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId)
{
size_t num = NumCapabilities();
CapabilitySet candidateSet;
for (size_t i = 0; i < num; i++) {
candidateSet.AppendElement(i);
}
bool first = true;
for (const MediaTrackConstraintSet* cs : aConstraintSets) {
for (size_t i = 0; i < candidateSet.Length(); ) {
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
uint32_t distance = GetFitnessDistance(cap, *cs, !first, aDeviceId);
if (distance == UINT32_MAX) {
candidateSet.RemoveElementAt(i);
} else {
++i;
if (first) {
candidate.mDistance = distance;
}
}
}
first = false;
}
if (!candidateSet.Length()) {
return UINT32_MAX;
}
TrimLessFitCandidates(candidateSet);
return candidateSet[0].mDistance;
}
void
MediaEngineCameraVideoSource::LogConstraints(
const MediaTrackConstraintSet& aConstraints, bool aAdvanced)
{
NormalizedConstraintSet c(aConstraints, aAdvanced);
LOG(((c.mWidth.mIdeal.WasPassed()?
"Constraints: width: { min: %d, max: %d, ideal: %d }" :
"Constraints: width: { min: %d, max: %d }"),
c.mWidth.mMin, c.mWidth.mMax,
c.mWidth.mIdeal.WasPassed()? c.mWidth.mIdeal.Value() : 0));
LOG(((c.mHeight.mIdeal.WasPassed()?
" height: { min: %d, max: %d, ideal: %d }" :
" height: { min: %d, max: %d }"),
c.mHeight.mMin, c.mHeight.mMax,
c.mHeight.mIdeal.WasPassed()? c.mHeight.mIdeal.Value() : 0));
LOG(((c.mFrameRate.mIdeal.WasPassed()?
" frameRate: { min: %f, max: %f, ideal: %f }" :
" frameRate: { min: %f, max: %f }"),
c.mFrameRate.mMin, c.mFrameRate.mMax,
c.mFrameRate.mIdeal.WasPassed()? c.mFrameRate.mIdeal.Value() : 0));
}
void
MediaEngineCameraVideoSource::LogCapability(const char* aHeader,
const webrtc::CaptureCapability &aCapability, uint32_t aDistance)
{
// RawVideoType and VideoCodecType media/webrtc/trunk/webrtc/common_types.h
static const char* const types[] = {
"I420",
"YV12",
"YUY2",
"UYVY",
"IYUV",
"ARGB",
"RGB24",
"RGB565",
"ARGB4444",
"ARGB1555",
"MJPEG",
"NV12",
"NV21",
"BGRA",
"Unknown type"
};
static const char* const codec[] = {
"VP8",
"VP9",
"H264",
"I420",
"RED",
"ULPFEC",
"Generic codec",
"Unknown codec"
};
LOG(("%s: %4u x %4u x %2u maxFps, %s, %s. Distance = %lu",
aHeader, aCapability.width, aCapability.height, aCapability.maxFPS,
types[std::min(std::max(uint32_t(0), uint32_t(aCapability.rawType)),
uint32_t(sizeof(types) / sizeof(*types) - 1))],
codec[std::min(std::max(uint32_t(0), uint32_t(aCapability.codecType)),
uint32_t(sizeof(codec) / sizeof(*codec) - 1))],
aDistance));
}
bool
MediaEngineCameraVideoSource::ChooseCapability(
const MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
aPrefs.GetWidth(), aPrefs.GetHeight(),
aPrefs.mFPS, aPrefs.mMinFPS));
LogConstraints(aConstraints, false);
if (aConstraints.mAdvanced.WasPassed()) {
LOG(("Advanced array[%u]:", aConstraints.mAdvanced.Value().Length()));
for (auto& advanced : aConstraints.mAdvanced.Value()) {
LogConstraints(advanced, true);
}
}
}
size_t num = NumCapabilities();
CapabilitySet candidateSet;
for (size_t i = 0; i < num; i++) {
candidateSet.AppendElement(i);
}
// First, filter capabilities by required constraints (min, max, exact).
for (size_t i = 0; i < candidateSet.Length();) {
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
candidate.mDistance = GetFitnessDistance(cap, aConstraints, false, aDeviceId);
LogCapability("Capability", cap, candidate.mDistance);
if (candidate.mDistance == UINT32_MAX) {
candidateSet.RemoveElementAt(i);
} else {
++i;
}
}
// Filter further with all advanced constraints (that don't overconstrain).
if (aConstraints.mAdvanced.WasPassed()) {
for (const MediaTrackConstraintSet &cs : aConstraints.mAdvanced.Value()) {
CapabilitySet rejects;
for (size_t i = 0; i < candidateSet.Length();) {
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
if (GetFitnessDistance(cap, cs, true, aDeviceId) == UINT32_MAX) {
rejects.AppendElement(candidate);
candidateSet.RemoveElementAt(i);
} else {
++i;
}
}
if (!candidateSet.Length()) {
candidateSet.AppendElements(Move(rejects));
}
}
}
if (!candidateSet.Length()) {
LOG(("failed to find capability match from %d choices",num));
return false;
}
// Remaining algorithm is up to the UA.
TrimLessFitCandidates(candidateSet);
// Any remaining multiples all have the same distance. A common case of this
// occurs when no ideal is specified. Lean toward defaults.
uint32_t sameDistance = candidateSet[0].mDistance;
{
MediaTrackConstraintSet prefs;
prefs.mWidth.SetAsLong() = aPrefs.GetWidth();
prefs.mHeight.SetAsLong() = aPrefs.GetHeight();
prefs.mFrameRate.SetAsDouble() = aPrefs.mFPS;
for (auto& candidate : candidateSet) {
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
candidate.mDistance = GetFitnessDistance(cap, prefs, false, aDeviceId);
}
TrimLessFitCandidates(candidateSet);
}
// Any remaining multiples all have the same distance, but may vary on
// format. Some formats are more desirable for certain use like WebRTC.
// E.g. I420 over RGB24 can remove a needless format conversion.
bool found = false;
for (auto& candidate : candidateSet) {
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
if (cap.rawType == webrtc::RawVideoType::kVideoI420 ||
cap.rawType == webrtc::RawVideoType::kVideoYUY2 ||
cap.rawType == webrtc::RawVideoType::kVideoYV12) {
mCapability = cap;
found = true;
break;
}
}
if (!found) {
GetCapability(candidateSet[0].mIndex, mCapability);
}
LogCapability("Chosen capability", mCapability, sameDistance);
return true;
}
void
MediaEngineCameraVideoSource::SetName(nsString aName)
{
mDeviceName = aName;
bool hasFacingMode = false;
VideoFacingModeEnum facingMode = VideoFacingModeEnum::User;
// Set facing mode based on device name.
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
if (aName.EqualsLiteral("back")) {
hasFacingMode = true;
facingMode = VideoFacingModeEnum::Environment;
} else if (aName.EqualsLiteral("front")) {
hasFacingMode = true;
facingMode = VideoFacingModeEnum::User;
}
#endif // MOZ_B2G_CAMERA
#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
// Names are generated. Example: "Camera 0, Facing back, Orientation 90"
//
// See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
// webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
if (aName.Find(NS_LITERAL_STRING("Facing back")) != kNotFound) {
hasFacingMode = true;
facingMode = VideoFacingModeEnum::Environment;
} else if (aName.Find(NS_LITERAL_STRING("Facing front")) != kNotFound) {
hasFacingMode = true;
facingMode = VideoFacingModeEnum::User;
}
#endif // ANDROID
#ifdef XP_MACOSX
// Kludge to test user-facing cameras on OSX.
if (aName.Find(NS_LITERAL_STRING("Face")) != -1) {
hasFacingMode = true;
facingMode = VideoFacingModeEnum::User;
}
#endif
if (hasFacingMode) {
mFacingMode.Assign(NS_ConvertUTF8toUTF16(
VideoFacingModeEnumValues::strings[uint32_t(facingMode)].value));
} else {
mFacingMode.Truncate();
}
}
void
MediaEngineCameraVideoSource::GetName(nsAString& aName)
{
aName = mDeviceName;
}
void
MediaEngineCameraVideoSource::SetUUID(const char* aUUID)
{
mUniqueId.Assign(aUUID);
}
void
MediaEngineCameraVideoSource::GetUUID(nsACString& aUUID)
{
aUUID = mUniqueId;
}
const nsCString&
MediaEngineCameraVideoSource::GetUUID()
{
return mUniqueId;
}
void
MediaEngineCameraVideoSource::SetDirectListeners(bool aHasDirectListeners)
{
LOG((__FUNCTION__));
mHasDirectListeners = aHasDirectListeners;
}
} // namespace mozilla