From 5f33e2f42471e150b58ef2f61e41bb403185d945 Mon Sep 17 00:00:00 2001 From: Denis Grigorev Date: Fri, 25 Jun 2021 14:20:28 +0300 Subject: [PATCH] [sailfishos][webrtc] Enable support for WebRTC video. JB#53982 --- embedding/embedlite/embedding.js | 11 +- ...c-Disable-enumeration-of-video-devic.patch | 45 - ...tc-Enable-GMP-for-encoding.-JB-53982.patch | 477 ++++ ...c-Implement-video-capture-module.-JB.patch | 2255 +++++++++++++++++ rpm/xulrunner-qt5.spec | 4 +- 5 files changed, 2744 insertions(+), 48 deletions(-) delete mode 100644 rpm/0069-sailfishos-webrtc-Disable-enumeration-of-video-devic.patch create mode 100644 rpm/0069-sailfishos-webrtc-Enable-GMP-for-encoding.-JB-53982.patch create mode 100644 rpm/0070-sailfishos-webrtc-Implement-video-capture-module.-JB.patch diff --git a/embedding/embedlite/embedding.js b/embedding/embedlite/embedding.js index 3b577153e4be8..d0a1f66bc8ac1 100644 --- a/embedding/embedlite/embedding.js +++ b/embedding/embedlite/embedding.js @@ -436,5 +436,12 @@ pref("media.cubeb.backend", "pulse"); // On ESR60 customelements is only enabled for nightly. Enable for us. pref("dom.webcomponents.customelements.enabled", true); -// Disable WebRTC video until it is implemented -pref("media.navigator.video.enabled", false); +// No native handle support (yet) for video frames, so higher resolution degrade performance +pref("media.navigator.video.default_width", 320); +pref("media.navigator.video.default_height", 240); + +// Many browsers prefer VP9 over H264. If the sailfish-browser is the initiator of the session, +// then the remote peer may override our preference and put VP9 in front of h264. Due to some bug, +// the gecko skips the peer's preference and creates an h264 decoder. As a workaround, disable VP9 +// until the bug is fixed. +pref("media.peerconnection.video.vp9_enabled", false); diff --git a/rpm/0069-sailfishos-webrtc-Disable-enumeration-of-video-devic.patch b/rpm/0069-sailfishos-webrtc-Disable-enumeration-of-video-devic.patch deleted file mode 100644 index 80d95431629cb..0000000000000 --- a/rpm/0069-sailfishos-webrtc-Disable-enumeration-of-video-devic.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Denis Grigorev -Date: Fri, 16 Apr 2021 12:40:46 +0300 -Subject: [PATCH] [sailfishos][webrtc] Disable enumeration of video - devices. JB#53756 - -Video capture is not yet implemented on SFOS. Do not report about -/dev/video* devices as it will break audio calls on sites requesting -video devices. - -Signed-off-by: Denis Grigorev ---- - .../modules/video_capture/linux/device_info_linux.cc | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc b/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc -index d1bcdf2d3c33..cb8bb6fe6e50 100644 ---- a/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc -+++ b/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc -@@ -241,6 +241,7 @@ uint32_t DeviceInfoLinux::NumberOfDevices() - WEBRTC_TRACE(webrtc::kTraceApiCall, - webrtc::kTraceVideoCapture, 0, "%s", __FUNCTION__); - -+#if !defined(MOZ_EMBEDLITE) - uint32_t count = 0; - char device[20]; - int fd = -1; -@@ -257,6 +258,14 @@ uint32_t DeviceInfoLinux::NumberOfDevices() - } - - return count; -+#else -+ /* -+ * Video capture is not yet implemented on SFOS. -+ * Do not report about /dev/video* devices as it will break audio calls -+ * on sites requesting video devices. -+ */ -+ return 0; -+#endif - } - - int32_t DeviceInfoLinux::GetDeviceName( --- -2.17.1 - diff --git a/rpm/0069-sailfishos-webrtc-Enable-GMP-for-encoding.-JB-53982.patch b/rpm/0069-sailfishos-webrtc-Enable-GMP-for-encoding.-JB-53982.patch new file mode 100644 index 0000000000000..176b22861413b --- /dev/null +++ b/rpm/0069-sailfishos-webrtc-Enable-GMP-for-encoding.-JB-53982.patch @@ -0,0 +1,477 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Denis Grigorev +Date: Fri, 30 Apr 2021 14:59:18 +0300 +Subject: [PATCH] [sailfishos][webrtc] Enable GMP for encoding. JB#53982 + +--- + dom/media/gmp/GMPSharedMemManager.cpp | 34 +++-- + dom/media/gmp/GMPSharedMemManager.h | 8 +- + dom/media/gmp/GMPVideoDecoderParent.cpp | 4 +- + .../src/media-conduit/GmpVideoCodec.cpp | 4 +- + .../src/media-conduit/GmpVideoCodec.h | 2 +- + .../src/media-conduit/VideoConduit.cpp | 31 ++++- + .../src/media-conduit/WebrtcGmpVideoCodec.cpp | 119 ++++++++++++++---- + .../src/media-conduit/WebrtcGmpVideoCodec.h | 6 +- + .../src/peerconnection/PeerConnectionImpl.cpp | 6 +- + 9 files changed, 163 insertions(+), 51 deletions(-) + +diff --git a/dom/media/gmp/GMPSharedMemManager.cpp b/dom/media/gmp/GMPSharedMemManager.cpp +index a7f462ce7a7f..fa279bffb49e 100644 +--- a/dom/media/gmp/GMPSharedMemManager.cpp ++++ b/dom/media/gmp/GMPSharedMemManager.cpp +@@ -24,13 +24,16 @@ bool GMPSharedMemManager::MgrAllocShmem( + ipc::Shmem::SharedMemory::SharedMemoryType aType, ipc::Shmem* aMem) { + mData->CheckThread(); + +- // first look to see if we have a free buffer large enough +- for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) { +- MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable()); +- if (aSize <= GetGmpFreelist(aClass)[i].Size()) { +- *aMem = GetGmpFreelist(aClass)[i]; +- GetGmpFreelist(aClass).RemoveElementAt(i); +- return true; ++ { ++ // first look to see if we have a free buffer large enough ++ MutexAutoLock lock(mMutex); ++ for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) { ++ MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable()); ++ if (aSize <= GetGmpFreelist(aClass)[i].Size()) { ++ *aMem = GetGmpFreelist(aClass)[i]; ++ GetGmpFreelist(aClass).RemoveElementAt(i); ++ return true; ++ } + } + } + +@@ -39,6 +42,7 @@ bool GMPSharedMemManager::MgrAllocShmem( + aSize = (aSize + (pagesize - 1)) & ~(pagesize - 1); // round up to page size + bool retval = Alloc(aSize, aType, aMem); + if (retval) { ++ MutexAutoLock lock(mMutex); + // The allocator (or NeedsShmem call) should never return less than we ask + // for... + MOZ_ASSERT(aMem->Size() >= aSize); +@@ -68,11 +72,18 @@ bool GMPSharedMemManager::MgrDeallocShmem(GMPSharedMem::GMPMemoryClasses aClass, + // XXX This works; there are better pool algorithms. We need to avoid + // "falling off a cliff" with too low a number + if (GetGmpFreelist(aClass).Length() > 10) { +- Dealloc(GetGmpFreelist(aClass)[0]); +- GetGmpFreelist(aClass).RemoveElementAt(0); ++ ipc::Shmem element; ++ { ++ MutexAutoLock lock(mMutex); ++ element = GetGmpFreelist(aClass)[0]; ++ GetGmpFreelist(aClass).RemoveElementAt(0); ++ mData->mGmpAllocated[aClass]--; ++ } ++ Dealloc(element); + // The allocation numbers will be fubar on the Child! +- mData->mGmpAllocated[aClass]--; + } ++ ++ MutexAutoLock lock(mMutex); + for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) { + MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable()); + total += GetGmpFreelist(aClass)[i].Size(); +@@ -86,7 +97,8 @@ bool GMPSharedMemManager::MgrDeallocShmem(GMPSharedMem::GMPMemoryClasses aClass, + return true; + } + +-uint32_t GMPSharedMemManager::NumInUse(GMPSharedMem::GMPMemoryClasses aClass) { ++int32_t GMPSharedMemManager::NumInUse(GMPSharedMem::GMPMemoryClasses aClass) { ++ MutexAutoLock lock(mMutex); + return mData->mGmpAllocated[aClass] - GetGmpFreelist(aClass).Length(); + } + +diff --git a/dom/media/gmp/GMPSharedMemManager.h b/dom/media/gmp/GMPSharedMemManager.h +index 738a4d114c2b..fbc2b4438d3e 100644 +--- a/dom/media/gmp/GMPSharedMemManager.h ++++ b/dom/media/gmp/GMPSharedMemManager.h +@@ -7,6 +7,7 @@ + #define GMPSharedMemManager_h_ + + #include "mozilla/ipc/Shmem.h" ++#include "mozilla/Mutex.h" + #include "nsTArray.h" + + namespace mozilla { +@@ -27,7 +28,7 @@ class GMPSharedMem { + // returned to the parent pool (which is not included). If more than + // this are needed, we presume the client has either crashed or hung + // (perhaps temporarily). +- static const uint32_t kGMPBufLimit = 20; ++ static const int32_t kGMPBufLimit = 32; + + GMPSharedMem() { + for (size_t i = 0; i < sizeof(mGmpAllocated) / sizeof(mGmpAllocated[0]); +@@ -49,7 +50,7 @@ class GMPSharedMem { + + class GMPSharedMemManager { + public: +- explicit GMPSharedMemManager(GMPSharedMem* aData) : mData(aData) {} ++ explicit GMPSharedMemManager(GMPSharedMem* aData) : mMutex("GMPSharedMemManager::mMutex"), mData(aData) {} + virtual ~GMPSharedMemManager() {} + + virtual bool MgrAllocShmem(GMPSharedMem::GMPMemoryClasses aClass, +@@ -61,7 +62,7 @@ class GMPSharedMemManager { + + // So we can know if data is "piling up" for the plugin - I.e. it's hung or + // crashed +- virtual uint32_t NumInUse(GMPSharedMem::GMPMemoryClasses aClass); ++ virtual int32_t NumInUse(GMPSharedMem::GMPMemoryClasses aClass); + + // These have to be implemented using the AllocShmem/etc provided by the + // IPDL-generated interfaces, so have the Parent/Child implement them. +@@ -70,6 +71,7 @@ class GMPSharedMemManager { + ipc::Shmem* aMem) = 0; + virtual void Dealloc(ipc::Shmem& aMem) = 0; + ++ Mutex mMutex; + private: + nsTArray& GetGmpFreelist(GMPSharedMem::GMPMemoryClasses aTypes) { + return mData->mGmpFreelist[aTypes]; +diff --git a/dom/media/gmp/GMPVideoDecoderParent.cpp b/dom/media/gmp/GMPVideoDecoderParent.cpp +index 1fa2425a3c2c..6bfde6b04e26 100644 +--- a/dom/media/gmp/GMPVideoDecoderParent.cpp ++++ b/dom/media/gmp/GMPVideoDecoderParent.cpp +@@ -134,9 +134,9 @@ nsresult GMPVideoDecoderParent::Decode( + // Very rough kill-switch if the plugin stops processing. If it's merely + // hung and continues, we'll come back to life eventually. + // 3* is because we're using 3 buffers per frame for i420 data for now. +- if ((NumInUse(GMPSharedMem::kGMPFrameData) > +- 3 * GMPSharedMem::kGMPBufLimit) || ++ if ((NumInUse(GMPSharedMem::kGMPFrameData) > 3 * GMPSharedMem::kGMPBufLimit) || + (NumInUse(GMPSharedMem::kGMPEncodedData) > GMPSharedMem::kGMPBufLimit)) { ++ + LOGE( + ("GMPVideoDecoderParent[%p]::Decode() ERROR; shmem buffer limit hit " + "frame=%d encoded=%d", +diff --git a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp +index b5f39bc3b7d4..287ccf3a7862 100644 +--- a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp ++++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp +@@ -11,8 +11,8 @@ WebrtcVideoEncoder* GmpVideoCodec::CreateEncoder() { + return new WebrtcVideoEncoderProxy(); + } + +-WebrtcVideoDecoder* GmpVideoCodec::CreateDecoder() { +- return new WebrtcVideoDecoderProxy(); ++WebrtcVideoDecoder* GmpVideoCodec::CreateDecoder(webrtc::VideoCodecType type) { ++ return new WebrtcVideoDecoderProxy(type); + } + + } // namespace mozilla +diff --git a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h +index 318a891d0678..0fcaac43f364 100644 +--- a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h ++++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.h +@@ -11,7 +11,7 @@ namespace mozilla { + class GmpVideoCodec { + public: + static WebrtcVideoEncoder* CreateEncoder(); +- static WebrtcVideoDecoder* CreateDecoder(); ++ static WebrtcVideoDecoder* CreateDecoder(webrtc::VideoCodecType type); + }; + + } // namespace mozilla +diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +index c8dddccec26b..e74f1061ce28 100644 +--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp ++++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +@@ -1501,15 +1501,37 @@ webrtc::VideoDecoder* WebrtcVideoConduit::CreateDecoder( + return decoder; + } + +- switch (aType) { ++ // Attempt to create a GMP decoder. ++ { ++ nsCString tag; ++ ++ switch (aType) { + case webrtc::VideoCodecType::kVideoCodecH264: +- // get an external decoder +- decoder = GmpVideoCodec::CreateDecoder(); ++ tag = NS_LITERAL_CSTRING("h264"); ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP8: ++ tag = NS_LITERAL_CSTRING("vp8"); ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP9: ++ tag = NS_LITERAL_CSTRING("vp9"); ++ break; ++ default: ++ return nullptr; ++ } ++ ++ if (HaveGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER), { tag })) { ++ decoder = GmpVideoCodec::CreateDecoder(aType); + if (decoder) { + mRecvCodecPlugin = static_cast(decoder); + } +- break; ++ return decoder; ++ } ++ } + ++ switch (aType) { ++ case webrtc::VideoCodecType::kVideoCodecH264: ++ // No support for software h264. ++ return nullptr; + case webrtc::VideoCodecType::kVideoCodecVP8: + #ifdef MOZ_WEBRTC_MEDIACODEC + // attempt to get a decoder +@@ -1545,7 +1567,6 @@ webrtc::VideoDecoder* WebrtcVideoConduit::CreateDecoder( + MOZ_ASSERT(webrtc::VP9Decoder::IsSupported()); + decoder = webrtc::VP9Decoder::Create(); + break; +- + default: + break; + } +diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp +index f7976636ec47..f92e33880637 100644 +--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp ++++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp +@@ -153,15 +153,32 @@ int32_t WebrtcGmpVideoEncoder::InitEncode( + + memset(&mCodecSpecificInfo.codecSpecific, 0, + sizeof(mCodecSpecificInfo.codecSpecific)); +- mCodecSpecificInfo.codecType = webrtc::kVideoCodecH264; +- mCodecSpecificInfo.codecSpecific.H264.packetization_mode = +- aCodecSettings->H264().packetizationMode == 1 +- ? webrtc::H264PacketizationMode::NonInterleaved +- : webrtc::H264PacketizationMode::SingleNalUnit; ++ mCodecSpecificInfo.codecType = aCodecSettings->codecType; + +- if (mCodecSpecificInfo.codecSpecific.H264.packetization_mode == +- webrtc::H264PacketizationMode::NonInterleaved) { +- mMaxPayloadSize = 0; // No limit, use FUAs ++ switch (aCodecSettings->codecType) { ++ case webrtc::VideoCodecType::kVideoCodecH264: { ++ codecParams.mCodecType = kGMPVideoCodecH264; ++ ++ mCodecSpecificInfo.codecSpecific.H264.packetization_mode = ++ aCodecSettings->H264().packetizationMode == 1 ++ ? webrtc::H264PacketizationMode::NonInterleaved ++ : webrtc::H264PacketizationMode::SingleNalUnit; ++ ++ if (mCodecSpecificInfo.codecSpecific.H264.packetization_mode == ++ webrtc::H264PacketizationMode::NonInterleaved) { ++ mMaxPayloadSize = 0; // No limit, use FUAs ++ } ++ break; ++ } ++ case webrtc::VideoCodecType::kVideoCodecVP8: ++ codecParams.mCodecType = kGMPVideoCodecVP8; ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP9: ++ codecParams.mCodecType = kGMPVideoCodecVP9; ++ break; ++ default: ++ // The requested codec is not supported. ++ return WEBRTC_VIDEO_CODEC_ERROR; + } + + if (aCodecSettings->mode == webrtc::kScreensharing) { +@@ -193,7 +210,18 @@ void WebrtcGmpVideoEncoder::InitEncode_g( + const GMPVideoCodec& aCodecParams, int32_t aNumberOfCores, + uint32_t aMaxPayloadSize, const RefPtr& aInitDone) { + nsTArray tags; +- tags.AppendElement(NS_LITERAL_CSTRING("h264")); ++ switch (aCodecParams.mCodecType) { ++ case kGMPVideoCodecVP8: ++ tags.AppendElement(NS_LITERAL_CSTRING("vp8")); ++ break; ++ case kGMPVideoCodecVP9: ++ tags.AppendElement(NS_LITERAL_CSTRING("vp9")); ++ break; ++ case kGMPVideoCodecH264: ++ default: ++ tags.AppendElement(NS_LITERAL_CSTRING("h264")); ++ break; ++ } + UniquePtr callback( + new InitDoneCallback(aThis, aInitDone, aCodecParams, aMaxPayloadSize)); + aThis->mInitting = true; +@@ -607,14 +635,15 @@ void WebrtcGmpVideoEncoder::Encoded( + } + + // Decoder. +-WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() ++WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder(webrtc::VideoCodecType aType) + : mGMP(nullptr), + mInitting(false), + mHost(nullptr), + mCallbackMutex("WebrtcGmpVideoDecoder decoded callback mutex"), + mCallback(nullptr), + mCachedPluginId(0), +- mDecoderStatus(GMPNoErr) { ++ mDecoderStatus(GMPNoErr), ++ mCodecType(aType) { + if (mPCHandle.empty()) { + mPCHandle = WebrtcGmpPCHandleSetter::GetCurrentHandle(); + } +@@ -653,7 +682,21 @@ int32_t WebrtcGmpVideoDecoder::InitDecode( + const webrtc::VideoCodec* aCodecSettings, int32_t aNumberOfCores, + const RefPtr& aInitDone) { + nsTArray tags; +- tags.AppendElement(NS_LITERAL_CSTRING("h264")); ++ switch (aCodecSettings->codecType) { ++ case webrtc::VideoCodecType::kVideoCodecVP8: ++ tags.AppendElement(NS_LITERAL_CSTRING("vp8")); ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP9: ++ tags.AppendElement(NS_LITERAL_CSTRING("vp9")); ++ break; ++ case webrtc::VideoCodecType::kVideoCodecH264: ++ default: ++ tags.AppendElement(NS_LITERAL_CSTRING("h264")); ++ break; ++ } ++ ++ memcpy(&aThis->mCodecSettings, aCodecSettings, sizeof(aThis->mCodecSettings)); ++ + UniquePtr callback( + new InitDoneCallback(aThis, aInitDone)); + aThis->mInitting = true; +@@ -695,11 +738,29 @@ int32_t WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP, + GMPVideoCodec codec; + memset(&codec, 0, sizeof(codec)); + codec.mGMPApiVersion = 33; ++ codec.mWidth = mCodecSettings.width; ++ codec.mHeight = mCodecSettings.height; + +- // XXX this is currently a hack +- // GMPVideoCodecUnion codecSpecific; +- // memset(&codecSpecific, 0, sizeof(codecSpecific)); + nsTArray codecSpecific; ++ ++ switch (mCodecSettings.codecType) { ++ case webrtc::VideoCodecType::kVideoCodecH264: { ++ // Currently gmp-droid does not support codec-specific data ++ // TODO: Check OpenH264 requirements ++ codec.mCodecType = kGMPVideoCodecH264; ++ break; ++ } ++ case webrtc::VideoCodecType::kVideoCodecVP8: ++ codec.mCodecType = kGMPVideoCodecVP8; ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP9: ++ codec.mCodecType = kGMPVideoCodecVP9; ++ break; ++ default: ++ // The requested codec is not supported. ++ return WEBRTC_VIDEO_CODEC_ERROR; ++ } ++ + nsresult rv = mGMP->InitDecode(codec, codecSpecific, this, 1); + if (NS_FAILED(rv)) { + *aErrorOut = "GMP Decode: InitDecode failed"; +@@ -820,13 +881,9 @@ void WebrtcGmpVideoDecoder::Decode_g(const RefPtr& aThis, + return; + } + +- // XXX At this point, we only will get mode1 data (a single length and a +- // buffer) Session_info.cc/etc code needs to change to support mode 0. +- *(reinterpret_cast(frame->Buffer())) = frame->Size(); +- +- // XXX It'd be wonderful not to have to memcpy the encoded data! +- memcpy(frame->Buffer() + 4, aDecodeData->mImage._buffer + 4, +- frame->Size() - 4); ++ // gmp-droid does not require conversion of NAL units for h264 ++ // TODO: Check OpenH264 requirements ++ memcpy(frame->Buffer(), aDecodeData->mImage._buffer, frame->Size()); + + frame->SetEncodedWidth(aDecodeData->mImage._encodedWidth); + frame->SetEncodedHeight(aDecodeData->mImage._encodedHeight); +@@ -845,11 +902,25 @@ void WebrtcGmpVideoDecoder::Decode_g(const RefPtr& aThis, + return; + } + ++ frame->SetFrameType(ft); ++ + // Bug XXXXXX: Set codecSpecific info + GMPCodecSpecificInfo info; + memset(&info, 0, sizeof(info)); +- info.mCodecType = kGMPVideoCodecH264; +- info.mCodecSpecific.mH264.mSimulcastIdx = 0; ++ switch (aThis->mCodecType) { ++ case webrtc::VideoCodecType::kVideoCodecH264: ++ info.mCodecType = kGMPVideoCodecH264; ++ info.mCodecSpecific.mH264.mSimulcastIdx = 0; ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP8: ++ info.mCodecType = kGMPVideoCodecVP8; ++ break; ++ case webrtc::VideoCodecType::kVideoCodecVP9: ++ info.mCodecType = kGMPVideoCodecVP9; ++ break; ++ default: ++ break; ++ } + nsTArray codecSpecificInfo; + codecSpecificInfo.AppendElements((uint8_t*)&info, + sizeof(GMPCodecSpecificInfo)); +diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h +index 920f7460d1a3..bba398eac38d 100644 +--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h ++++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h +@@ -324,7 +324,7 @@ class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder { + + class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy { + public: +- WebrtcGmpVideoDecoder(); ++ WebrtcGmpVideoDecoder(webrtc::VideoCodecType aType); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoDecoder); + + // Implement VideoEncoder interface, sort of. +@@ -413,6 +413,8 @@ class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy { + Atomic mCachedPluginId; + Atomic mDecoderStatus; + std::string mPCHandle; ++ webrtc::VideoCodecType mCodecType; ++ webrtc::VideoCodec mCodecSettings; + }; + + // Basically a strong ref to a WebrtcGmpVideoDecoder, that also translates +@@ -422,7 +424,7 @@ class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy { + // the "real" encoder. + class WebrtcVideoDecoderProxy : public WebrtcVideoDecoder { + public: +- WebrtcVideoDecoderProxy() : mDecoderImpl(new WebrtcGmpVideoDecoder) {} ++ WebrtcVideoDecoderProxy(webrtc::VideoCodecType aType) : mDecoderImpl(new WebrtcGmpVideoDecoder(aType)) {} + + virtual ~WebrtcVideoDecoderProxy() { + RegisterDecodeCompleteCallback(nullptr); +diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +index 17ef17dd26d0..87995581f36f 100644 +--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp ++++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +@@ -782,7 +782,7 @@ class ConfigureCodec { + mUseAudioFec(false), + mRedUlpfecEnabled(false), + mDtmfEnabled(false) { +- mSoftwareH264Enabled = PeerConnectionCtx::GetInstance()->gmpHasH264(); ++ mHardwareH264Supported = PeerConnectionCtx::GetInstance()->gmpHasH264(); + + mH264Enabled = mHardwareH264Supported || mSoftwareH264Enabled; + +@@ -851,11 +851,15 @@ class ConfigureCodec { + // Might disable it, but we set up other params anyway + videoCodec.mEnabled = mH264Enabled; + ++ // FIXME: For some unknown reason the gecko reports packetization mode 0 ++ // Skip this. ++#if 0 + if (videoCodec.mPacketizationMode == 0 && !mSoftwareH264Enabled) { + // We're assuming packetization mode 0 is unsupported by + // hardware. + videoCodec.mEnabled = false; + } ++#endif + + if (mHardwareH264Supported) { + videoCodec.mStronglyPreferred = true; +-- +2.17.1 + diff --git a/rpm/0070-sailfishos-webrtc-Implement-video-capture-module.-JB.patch b/rpm/0070-sailfishos-webrtc-Implement-video-capture-module.-JB.patch new file mode 100644 index 0000000000000..d17d32e86bbc7 --- /dev/null +++ b/rpm/0070-sailfishos-webrtc-Implement-video-capture-module.-JB.patch @@ -0,0 +1,2255 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Denis Grigorev +Date: Thu, 4 Feb 2021 21:14:28 +0300 +Subject: [PATCH] [sailfishos][webrtc] Implement video capture module. JB#53982 + +--- + .../webrtc/modules/video_capture/BUILD.gn | 17 +- + .../modules/video_capture/sfos/camera.h | 58 ++ + .../video_capture/sfos/camera_manager.cc | 33 + + .../video_capture/sfos/camera_manager.h | 56 ++ + .../video_capture/sfos/device_info_sfos.cc | 157 ++++ + .../video_capture/sfos/device_info_sfos.h | 56 ++ + .../video_capture/sfos/gst_droid_camera.cc | 496 ++++++++++++ + .../video_capture/sfos/gst_droid_camera.h | 111 +++ + .../modules/video_capture/sfos/v4l2_camera.cc | 764 ++++++++++++++++++ + .../modules/video_capture/sfos/v4l2_camera.h | 95 +++ + .../video_capture/sfos/video_capture_sfos.cc | 174 ++++ + .../video_capture/sfos/video_capture_sfos.h | 67 ++ + .../video_capture_internal_impl_gn/moz.build | 15 +- + old-configure.in | 11 + + 14 files changed, 2097 insertions(+), 13 deletions(-) + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera.h + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.cc + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.h + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.cc + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.h + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.cc + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.h + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.cc + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.h + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.cc + create mode 100644 media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.h + +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn b/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn +index e4984238bac3..c2f10a7a7e87 100644 +--- a/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/BUILD.gn +@@ -98,10 +98,12 @@ if (!build_with_chromium) { + + if (is_linux || is_bsd) { + sources = [ +- "linux/device_info_linux.cc", +- "linux/device_info_linux.h", +- "linux/video_capture_linux.cc", +- "linux/video_capture_linux.h", ++ "gst/device_info_gst.cc", ++ "gst/device_info_gst.h", ++ "gst/video_capture_gst.cc", ++ "gst/video_capture_gst.h", ++ "gst/gst_camera.cpp", ++ "gst/gst_camera.h", + ] + deps += [ "../..:webrtc_common" ] + } +@@ -211,13 +213,6 @@ if (!build_with_chromium) { + "-lm", + ] + } +- if (is_linux) { +- ldflags += [ +- "-lrt", +- "-lXext", +- "-lX11", +- ] +- } + + deps = [ + ":video_capture_internal_impl", +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera.h +new file mode 100644 +index 000000000000..a0d523690487 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera.h +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_H_ ++ ++#include "webrtc/base/refcount.h" ++#include "webrtc/base/scoped_ref_ptr.h" ++#include "webrtc/modules/video_capture/video_capture_defines.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++ ++class CameraFrame ++{ ++public: ++ uint8_t* data; ++ size_t size; ++ uint64_t timestampMs; ++}; ++ ++class CameraListener ++{ ++public: ++ virtual void OnCameraFrame(const CameraFrame* frame) = 0; ++ virtual void OnCameraError(const char *errorDescription) = 0; ++ virtual void OnCameraSettingsChange(VideoCaptureCapability &caps, ++ int sensorMountAngle, ++ bool rearFacing) = 0; ++}; ++ ++class Camera : public rtc::RefCountInterface ++{ ++public: ++ virtual int32_t Init(const char* deviceUniqueId) = 0; ++ virtual int32_t StartCapture(const VideoCaptureCapability& capability) = 0; ++ virtual int32_t StopCapture() = 0; ++ virtual bool CaptureStarted() = 0; ++ ++ void SetListener(CameraListener *listener) { ++ _cameraListener = listener; ++ } ++ ++protected: ++ CameraListener *_cameraListener; ++}; ++ ++} // namespace videocapturemodule ++} // namespace webrtc ++ ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.cc b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.cc +new file mode 100644 +index 000000000000..470d4ccb7817 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.cc +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++ ++#include "base/singleton.h" ++#include "webrtc/modules/video_capture/sfos/camera_manager.h" ++#include "webrtc/modules/video_capture/sfos/gst_droid_camera.h" ++#include "webrtc/modules/video_capture/sfos/v4l2_camera.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++ ++CameraManager* GetCameraManager(camera_type_t cameraType) ++{ ++ switch (cameraType) { ++ case SFOS_CAMERA_TYPE_GST_DROID: ++ return static_cast(Singleton::get()); ++ case SFOS_CAMERA_TYPE_V4L2: ++ return static_cast(Singleton::get()); ++ } ++ return nullptr; ++} ++ ++} // namespace videocapturemodule ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.h +new file mode 100644 +index 000000000000..d8649941ad00 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_MANAGER_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_MANAGER_H_ ++ ++#include "webrtc/system_wrappers/include/trace.h" ++ ++#include "webrtc/modules/video_capture/sfos/camera.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++ ++typedef enum { ++ SFOS_CAMERA_TYPE_GST_DROID, ++ SFOS_CAMERA_TYPE_V4L2, ++} camera_type_t; ++ ++struct CameraInfo ++{ ++ std::string deviceName; ++ std::string deviceUID; ++ std::string productUID; ++}; ++ ++class CameraManager ++{ ++public: ++ virtual uint32_t NumberOfDevices() = 0; ++ virtual int32_t GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8=0, ++ uint32_t productUniqueIdUTF8Length=0, ++ pid_t* pid=0) = 0; ++ virtual bool QueryCapabilities (const char* deviceUniqueIdUTF8, ++ std::vector& caps) = 0; ++ virtual rtc::scoped_refptr OpenCamera (const char* deviceUniqueIdUTF8) = 0; ++}; ++ ++CameraManager* GetCameraManager(camera_type_t type); ++ ++} // namespace videocapturemodule ++} // namespace webrtc ++ ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_CAMERA_MANAGER_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.cc b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.cc +new file mode 100644 +index 000000000000..643916f447f4 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.cc +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mozilla/Preferences.h" ++#include "webrtc/system_wrappers/include/logging.h" ++ ++#include "webrtc/modules/video_capture/sfos/device_info_sfos.h" ++ ++namespace webrtc ++{ ++namespace videocapturemodule ++{ ++VideoCaptureModule::DeviceInfo* ++VideoCaptureImpl::CreateDeviceInfo() ++{ ++ return new videocapturemodule::DeviceInfoSFOS(); ++} ++ ++DeviceInfoSFOS::DeviceInfoSFOS() ++ : DeviceInfoImpl() ++{ ++} ++ ++int32_t DeviceInfoSFOS::Init() ++{ ++ return 0; ++} ++ ++DeviceInfoSFOS::~DeviceInfoSFOS() ++{ ++} ++ ++std::vector DeviceInfoSFOS::supportedCameraTypes() ++{ ++ return {SFOS_CAMERA_TYPE_GST_DROID}; ++} ++ ++void DeviceInfoSFOS::FindCameras() ++{ ++ _deviceList.clear(); ++ _deviceMap.clear(); ++ ++ for (camera_type_t cameraType : supportedCameraTypes()) { ++ auto cameraManager = GetCameraManager(cameraType); ++ for (uint32_t i = 0; i < cameraManager->NumberOfDevices(); i++ ) { ++ char deviceName[128]; ++ char deviceUID[128]; ++ char productUID[128]; ++ ++ if (0 == cameraManager->GetDeviceName(i, deviceName, sizeof(deviceName), ++ deviceUID, sizeof(deviceUID), ++ productUID, sizeof(productUID), 0)) { ++ CameraInfo info{deviceName, deviceUID, productUID}; ++ _deviceList.push_back(info); ++ ++ auto iter = _deviceMap.find(info.deviceName); ++ if (iter == _deviceMap.end()) { ++ _deviceMap.emplace(info.deviceName, cameraType); ++ } ++ } ++ } ++ } ++} ++ ++uint32_t DeviceInfoSFOS::NumberOfDevices() ++{ ++ LOG_F(LS_VERBOSE); ++ ++ FindCameras(); ++ return _deviceList.size(); ++} ++ ++int32_t DeviceInfoSFOS::GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8, ++ uint32_t productUniqueIdUTF8Length, ++ pid_t* /*pid*/) ++{ ++ LOG_F(LS_INFO); ++ ++ if (!_deviceList.size()) { ++ FindCameras(); ++ } ++ ++ if (deviceNumber > _deviceList.size()) { ++ return -1; ++ } ++ ++ CameraInfo info = _deviceList[deviceNumber]; ++ ++ strncpy(deviceNameUTF8, info.deviceName.c_str(), deviceNameLength); ++ strncpy(deviceUniqueIdUTF8, info.deviceUID.c_str(), deviceUniqueIdUTF8Length); ++ strncpy(productUniqueIdUTF8, info.productUID.c_str(), productUniqueIdUTF8Length); ++ ++ return 0; ++} ++ ++int32_t DeviceInfoSFOS::CreateCapabilityMap( ++ const char* deviceUniqueIdUTF8) ++{ ++ const int32_t deviceUniqueIdUTF8Length = ++ (int32_t) strlen((char*) deviceUniqueIdUTF8); ++ if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) { ++ LOG_F(LS_ERROR) << "Device name too long"; ++ return -1; ++ } ++ ++ FillCapabilities(deviceUniqueIdUTF8); ++ ++ // Store the new used device name. The parent class needs this for some reason ++ _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; ++ _lastUsedDeviceName = (char*) realloc(_lastUsedDeviceName, ++ _lastUsedDeviceNameLength + 1); ++ memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1); ++ ++ LOG_F(LS_INFO) << "Capability map for device " << deviceUniqueIdUTF8 ++ << " size " << _captureCapabilities.size(); ++ ++ return _captureCapabilities.size(); ++} ++ ++void DeviceInfoSFOS::FillCapabilities(const char *devName) ++{ ++ _captureCapabilities.clear(); ++ auto iter = _deviceMap.find(devName); ++ if (iter == _deviceMap.end()) { ++ return; ++ } ++ ++ const camera_type_t cameraType = iter->second; ++ auto cameraManager = GetCameraManager(cameraType); ++ if (cameraManager) { ++ cameraManager->QueryCapabilities(devName, _captureCapabilities); ++ } ++} ++ ++} // namespace videocapturemodule ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.h +new file mode 100644 +index 000000000000..7f57ace03256 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_DEVICE_INFO_SFOS_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_DEVICE_INFO_SFOS_H_ ++ ++#include "webrtc/modules/video_capture/device_info_impl.h" ++#include "webrtc/modules/video_capture/video_capture_impl.h" ++#include "webrtc/base/platform_thread.h" ++ ++#include "webrtc/modules/video_capture/sfos/camera_manager.h" ++ ++namespace webrtc ++{ ++namespace videocapturemodule ++{ ++class DeviceInfoSFOS: public DeviceInfoImpl ++{ ++public: ++ DeviceInfoSFOS(); ++ virtual ~DeviceInfoSFOS(); ++ virtual uint32_t NumberOfDevices(); ++ virtual int32_t GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8=0, ++ uint32_t productUniqueIdUTF8Length=0, ++ pid_t* pid=0); ++ virtual int32_t CreateCapabilityMap (const char* deviceUniqueIdUTF8); ++ virtual int32_t DisplayCaptureSettingsDialogBox( ++ const char* /*deviceUniqueIdUTF8*/, ++ const char* /*dialogTitleUTF8*/, ++ void* /*parentWindow*/, ++ uint32_t /*positionX*/, ++ uint32_t /*positionY*/) { return -1; } ++ void FillCapabilities(const char* devName); ++ int32_t Init(); ++private: ++ void FindCameras(); ++ std::vector _deviceList; ++ std::map _deviceMap; ++ static std::vector supportedCameraTypes(); ++}; ++} // namespace videocapturemodule ++} // namespace webrtc ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_DEVICE_INFO_SFOS_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.cc b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.cc +new file mode 100644 +index 000000000000..1d6c4f012005 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.cc +@@ -0,0 +1,496 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "webrtc/base/refcount.h" ++#include "webrtc/base/scoped_ref_ptr.h" ++#include "webrtc/system_wrappers/include/trace.h" ++ ++#include "webrtc/modules/video_capture/sfos/gst_droid_camera.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++ ++#define EXPECTED_CAPTURE_DELAY_PREF "media.getusermedia.camera.expected_capture_delay" ++ ++#define GST_DROID_CAMERA_PRIMARY "primary" ++#define GST_DROID_CAMERA_SECONDARY "secondary" ++#define GST_DROID_CAMERA_VENDOR "gst-droid" ++ ++GstDroidCamera::GstDroidCamera() ++{ ++ LOG_F(LS_VERBOSE); ++} ++ ++int32_t GstDroidCamera::Init(const char* deviceUniqueIdUTF8) ++{ ++ GstElement *sink; ++ int32_t ret = -1; ++ int cameraNr; ++ ++ LOG_F(LS_INFO) << deviceUniqueIdUTF8; ++ ++ if (!gst_is_initialized ()) { ++ gst_init(NULL, NULL); ++ } ++ ++ _captureContext = g_main_context_new(); ++ if (!_captureContext) { ++ return -1; ++ } ++ ++ g_main_context_push_thread_default (_captureContext); ++ ++ /* TODO: Make the pipeline configurable */ ++ _pipeline = gst_parse_launch ( ++ "droidcamsrc mode=2 exposure-mode=auto iso_speed=0 name=src" ++ " src.vfsrc" ++ " ! capsfilter name=camera_capsfilter" ++ " ! videorate drop-only=true" ++ " ! capsfilter name=rate_capsfilter" ++ " ! queue ! appsink name=videosource", NULL ); ++ ++ if (!_pipeline) { ++ g_object_unref(_mainLoop); ++ goto init_error; ++ } ++ ++ _camera = gst_bin_get_by_name (GST_BIN (_pipeline), "src"); ++ if (!_camera) { ++ gst_object_unref (_capsFilterRate); ++ gst_object_unref (_capsFilterDimensions); ++ gst_object_unref (_pipeline); ++ goto init_error; ++ } ++ ++ // primary = 0, secondary = 1 ++ cameraNr = strcmp(deviceUniqueIdUTF8, GST_DROID_CAMERA_PRIMARY) ? 1 : 0; ++ g_object_set (G_OBJECT (_camera), "camera-device", cameraNr, NULL); ++ ++ // Primary camera is a rear-facing camera ++ _rearFacing = cameraNr == 0; ++ ++ sink = gst_bin_get_by_name (GST_BIN( _pipeline ), "videosource"); ++ if (!sink) { ++ gst_object_unref (_pipeline); ++ goto init_error; ++ } ++ ++ g_object_set (G_OBJECT (sink), "sync", TRUE, NULL); ++ g_object_set (G_OBJECT (sink), "emit-signals", TRUE, NULL); ++ g_signal_connect (sink, "new-sample", G_CALLBACK (NewSampleCallback), this); ++ gst_object_unref (sink); ++ ++ _capsFilterDimensions = gst_bin_get_by_name (GST_BIN (_pipeline), "camera_capsfilter"); ++ if (!_capsFilterDimensions) { ++ LOG_F(LS_ERROR) << "camera_capsfilter is not found"; ++ gst_object_unref (_pipeline); ++ goto init_error; ++ } ++ ++ _capsFilterRate = gst_bin_get_by_name (GST_BIN (_pipeline), "rate_capsfilter"); ++ if (!_capsFilterRate) { ++ LOG_F(LS_ERROR) << "rate_capsfilter is not found"; ++ gst_object_unref (_capsFilterDimensions); ++ gst_object_unref (_pipeline); ++ goto init_error; ++ } ++ ++ _bus = gst_element_get_bus (_pipeline); ++ gst_bus_add_signal_watch (_bus); ++ g_signal_connect (G_OBJECT (_bus), "message::error", G_CALLBACK (ErrorCallback), this); ++ ++ _mainLoop = g_main_loop_new (_captureContext, TRUE); ++ if (!_mainLoop) { ++ gst_bus_remove_signal_watch (_bus); ++ gst_object_unref (_bus); ++ gst_object_unref (_capsFilterDimensions); ++ gst_object_unref (_capsFilterRate); ++ gst_object_unref (_pipeline); ++ goto init_error; ++ } ++ ++ ret = 0; ++init_error: ++ g_main_context_pop_thread_default (_captureContext); ++ ++ return ret; ++} ++ ++GstDroidCamera::~GstDroidCamera() ++{ ++ StopCapture(); ++ ++ gst_element_set_state (_pipeline, GST_STATE_NULL); ++ ++ gst_bus_remove_signal_watch(_bus); ++ gst_object_unref (_bus); ++ gst_object_unref (_capsFilterDimensions); ++ gst_object_unref (_capsFilterRate); ++ gst_object_unref (_pipeline); ++ g_main_loop_unref(_mainLoop); ++ g_main_context_unref(_captureContext); ++ ++ LOG_F(LS_VERBOSE); ++} ++ ++int32_t GstDroidCamera::StartCapture( ++ const VideoCaptureCapability& capability) ++{ ++ GstCaps *caps; ++ gchar caps_string[128]; ++ ++ LOG_F(LS_VERBOSE); ++ ++ if (_captureStarted) { ++ if (capability.width == _currentCaptureSettings.width && ++ capability.height == _currentCaptureSettings.height && ++ capability.maxFPS == _currentCaptureSettings.maxFPS && ++ capability.rawType == _currentCaptureSettings.rawType) { ++ return 0; ++ } else { ++ StopCapture(); ++ } ++ } ++ ++ /* Configure frame size */ ++ g_snprintf (caps_string, sizeof(caps_string), ++ "video/x-raw(memory:DroidMediaQueueBuffer), width=%u, height=%u", ++ capability.width, capability.height); ++ ++ caps = gst_caps_from_string (caps_string); ++ if (caps) { ++ LOG_F(LS_VERBOSE) << "Applying caps: " << caps_string; ++ g_object_set (G_OBJECT (_capsFilterDimensions), "caps", caps, NULL); ++ } else { ++ LOG_F(LS_ERROR) << "Cannot set image resolution"; ++ } ++ gst_caps_unref (caps); ++ ++ /* Configure frame rate */ ++ g_snprintf (caps_string, sizeof(caps_string), ++ "video/x-raw(memory:DroidMediaQueueBuffer), framerate=%u/1", ++ capability.maxFPS); ++ ++ caps = gst_caps_from_string (caps_string); ++ if (caps) { ++ LOG_F(LS_VERBOSE) << "Applying caps: " << caps_string; ++ g_object_set (G_OBJECT (_capsFilterRate), "caps", caps, NULL); ++ } else { ++ LOG_F(LS_ERROR) << "Cannot set frame rate"; ++ } ++ gst_caps_unref (caps); ++ ++ _currentCaptureSettings.rawType = kVideoUnknown; ++ _currentCaptureSettings.width = capability.width; ++ _currentCaptureSettings.height = capability.height; ++ _currentCaptureSettings.maxFPS = capability.maxFPS; ++ /* FIXME: Is it possible to read capture delay from the HW? */ ++ _currentCaptureSettings.expectedCaptureDelay = ++ mozilla::Preferences::GetUint(EXPECTED_CAPTURE_DELAY_PREF, 500); ++ ++ /* Pre-calculate frame size for YV12 */ ++ _frameSize = capability.width * capability.height * 3 / 2; ++ ++ if (!_captureThread) { ++ _captureThread.reset(new rtc::PlatformThread( ++ GstDroidCamera::CaptureThread, this, "CaptureThread")); ++ _captureThread->Start(); ++ _captureThread->SetPriority(rtc::kHighPriority); ++ } ++ ++ _currentCaptureSettingsChanged = true; ++ _captureStarted = true; ++ gst_element_set_state (_pipeline, GST_STATE_PLAYING); ++ ++ return 0; ++} ++ ++int32_t GstDroidCamera::StopCapture() ++{ ++ LOG_F(LS_VERBOSE); ++ ++ gst_element_set_state (_pipeline, GST_STATE_PAUSED); ++ g_main_loop_quit (_mainLoop); ++ ++ if (_captureThread) { ++ _captureThread->Stop(); ++ _captureThread.reset(); ++ } ++ ++ _captureStarted = false; ++ ++ return 0; ++} ++ ++bool GstDroidCamera::CaptureStarted() ++{ ++ return _captureStarted; ++} ++ ++bool GstDroidCamera::CaptureThread(void* obj) ++{ ++ return reinterpret_cast(obj)->CaptureProcess(); ++} ++ ++bool GstDroidCamera::CaptureProcess() ++{ ++ g_main_loop_run(_mainLoop); ++ return true; ++} ++ ++RawVideoType GstDroidCamera::FourccToRawVideoType (const char *fourccStr) ++{ ++ if (fourccStr) { ++ switch (GST_STR_FOURCC (fourccStr)) { ++ case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): ++ return kVideoYV12; ++ case GST_MAKE_FOURCC ('N', 'V', '1', '2'): ++ return kVideoNV12; ++ case GST_MAKE_FOURCC ('I', '4', '2', '0'): ++ return kVideoI420; ++ case GST_MAKE_FOURCC ('N', 'V', '2', '1'): ++ return kVideoNV21; ++ case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): ++ return kVideoYUY2; ++ case GST_MAKE_FOURCC ('L', '5', '6', '5'): ++ return kVideoRGB565; ++ case GST_MAKE_FOURCC ('B', 'G', 'R', 'A'): ++ return kVideoBGRA; ++ case GST_MAKE_FOURCC ('M', 'J', 'P', 'G'): ++ return kVideoMJPEG; ++ // Gstreamer reports ENCODED format. Assume it's YV12 ++ case GST_MAKE_FOURCC ('E', 'N', 'C', 'O'): ++ return kVideoYV12; ++ default: ++ break; ++ } ++ } ++ return kVideoUnknown; ++} ++ ++void GstDroidCamera::ProcessSample (GstSample *sample) ++{ ++ GstDroidCameraFrame frame; ++ ++ if (frame.Map(sample, _frameSize)) { ++ if (_currentCaptureSettingsChanged) { ++ GstCaps *caps = gst_sample_get_caps (sample); ++ GstStructure *structure = gst_caps_get_structure(caps, 0); ++ ++ gchar *caps_str = gst_caps_to_string (caps); ++ LOG_F(LS_INFO) << "Received caps: " << caps_str; ++ g_free (caps_str); ++ ++ const char *format = gst_structure_get_string (structure, "format"); ++ if (!format) { ++ LOG_F(LS_ERROR) << "Cannot get frame format"; ++ } else if (!gst_structure_get_int (structure, "width", &_currentCaptureSettings.width)) { ++ LOG_F(LS_ERROR) << "Cannot get frame width"; ++ } else if (!gst_structure_get_int (structure, "height", &_currentCaptureSettings.height)) { ++ LOG_F(LS_ERROR) << "Cannot get frame height"; ++ } else { ++ _currentCaptureSettings.rawType = GstDroidCamera::FourccToRawVideoType (format); ++ _currentCaptureSettingsChanged = false; ++ if (_cameraListener) { ++ int angle = 0; ++ ++ uint32_t overrideColorFormat = mozilla::Preferences::GetUint("media.getusermedia.camera.override_color_format", (uint32_t)0); ++ if (overrideColorFormat) { ++ _currentCaptureSettings.rawType = static_cast(overrideColorFormat); ++ LOG_F(LS_INFO) << "Override color-format: " << _currentCaptureSettings.rawType; ++ } ++ ++ /* It doesn't matter if the camera lacks this property. */ ++ g_object_get (G_OBJECT (_camera), "sensor-mount-angle", &angle, NULL); ++ _cameraListener->OnCameraSettingsChange (_currentCaptureSettings, angle, _rearFacing); ++ } ++ } ++ ++ LOG_F(LS_INFO) << "Parsed capture settings: " ++ << _currentCaptureSettings.width << "x" << _currentCaptureSettings.height ++ << "color-format " << _currentCaptureSettings.rawType; ++ } ++ if (_cameraListener) { ++ _cameraListener->OnCameraFrame(&frame); ++ } ++ } ++} ++ ++GstFlowReturn GstDroidCamera::NewSampleCallback (GstElement *sink, void *data) ++{ ++ GstSample *sample; ++ ++ g_signal_emit_by_name (sink, "pull-sample", &sample); ++ if (sample) { ++ GstDroidCamera *instance = reinterpret_cast(data); ++ instance->ProcessSample(sample); ++ gst_sample_unref (sample); ++ } ++ return GST_FLOW_OK; ++} ++ ++bool GstDroidCamera::StopCaptureCallback (void *data) ++{ ++ GstDroidCamera *instance = reinterpret_cast(data); ++ instance->StopCapture(); ++ ++ return false; ++} ++ ++void GstDroidCamera::ProcessError (GstMessage *msg) ++{ ++ GError *err; ++ gchar *debug_info; ++ ++ gst_message_parse_error (msg, &err, &debug_info); ++ LOG_F(LS_ERROR) << "Error received from element " << GST_OBJECT_NAME (msg->src) ++ << ": " << err->message; ++ LOG_F(LS_ERROR) << "Debugging information: " << (debug_info ? debug_info : "none"); ++ ++ if (_cameraListener) { ++ _cameraListener->OnCameraError(err->message); ++ } ++ ++ g_clear_error (&err); ++ g_free (debug_info); ++ ++ /* Stop the thread from the main context to avoid deadlock */ ++ g_idle_add(G_SOURCE_FUNC (GstDroidCamera::StopCaptureCallback), this); ++} ++ ++void GstDroidCamera::ErrorCallback (GstBus *bus, GstMessage *msg, void *data) ++{ ++ GstDroidCamera *instance = reinterpret_cast(data); ++ instance->ProcessError (msg); ++} ++ ++GstDroidCameraFrame::GstDroidCameraFrame() ++{ ++} ++ ++GstDroidCameraFrame::~GstDroidCameraFrame() ++{ ++ if (_buffer) { ++ gst_buffer_unmap (_buffer, &_info); ++ gst_sample_unref(_sample); ++ } ++} ++ ++bool GstDroidCameraFrame::Map(GstSample *sample, unsigned frameSize) ++{ ++ _sample = gst_sample_ref (sample); ++ _buffer = gst_sample_get_buffer (sample); ++ if (_buffer && gst_buffer_map (_buffer, &_info, GST_MAP_READ)) { ++ data = static_cast(_info.data); ++ size = frameSize; ++ timestampMs = GST_BUFFER_DTS_OR_PTS (_buffer) / 1000 / 1000; ++ return true; ++ } ++ return false; ++} ++ ++GstDroidCameraManager::GstDroidCameraManager() ++{ ++ LOG_F(LS_VERBOSE); ++} ++ ++GstDroidCameraManager::~GstDroidCameraManager() ++{ ++ LOG_F(LS_VERBOSE); ++} ++ ++uint32_t GstDroidCameraManager::NumberOfDevices() ++{ ++ /* FIXME: Is gst-droid able to enumerate cameras? */ ++ return 2; ++} ++ ++int32_t GstDroidCameraManager::GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8, ++ uint32_t productUniqueIdUTF8Length, ++ pid_t* pid) ++{ ++ /* FIXME: Is gst-droid able to enumerate cameras? */ ++ switch (deviceNumber) { ++ case 0: ++ if (deviceNameLength > sizeof(GST_DROID_CAMERA_SECONDARY)) ++ strcpy(deviceNameUTF8, GST_DROID_CAMERA_SECONDARY); ++ if (deviceUniqueIdUTF8Length > sizeof(GST_DROID_CAMERA_SECONDARY)) ++ strcpy(deviceUniqueIdUTF8, GST_DROID_CAMERA_SECONDARY); ++ break; ++ case 1: ++ if (deviceNameLength > sizeof(GST_DROID_CAMERA_PRIMARY)) ++ strcpy(deviceNameUTF8, GST_DROID_CAMERA_PRIMARY); ++ if (deviceUniqueIdUTF8Length > sizeof(GST_DROID_CAMERA_PRIMARY)) ++ strcpy(deviceUniqueIdUTF8, GST_DROID_CAMERA_PRIMARY); ++ break; ++ default: ++ return -1; ++ } ++ ++ if (productUniqueIdUTF8Length > sizeof(GST_DROID_CAMERA_VENDOR)) ++ strcpy(productUniqueIdUTF8, GST_DROID_CAMERA_VENDOR); ++ ++ return 0; ++} ++ ++bool GstDroidCameraManager::QueryCapabilities (const char* deviceUniqueIdUTF8, ++ std::vector& caps) ++{ ++ VideoCaptureCapability cap; ++ unsigned captureDelay = mozilla::Preferences::GetUint(EXPECTED_CAPTURE_DELAY_PREF, 500); ++ ++ LOG_F(LS_VERBOSE) << deviceUniqueIdUTF8; ++ ++ /* This is the minimum WebRTC must support */ ++ cap.width = 320; ++ cap.height = 240; ++ cap.expectedCaptureDelay = captureDelay; ++ cap.rawType = kVideoYV12; ++ cap.maxFPS = 15; ++ caps.push_back(cap); ++ cap.maxFPS = 30; ++ caps.push_back(cap); ++ ++ return true; ++} ++ ++rtc::scoped_refptr GstDroidCameraManager::OpenCamera (const char* deviceUniqueIdUTF8) ++{ ++ LOG_F(LS_INFO) << deviceUniqueIdUTF8; ++ ++ rtc::scoped_refptr camera(new rtc::RefCountedObject()); ++ ++ if (!camera->Init(deviceUniqueIdUTF8)) { ++ return camera; ++ } else { ++ LOG_F(LS_ERROR) << "Cannot initialize camera"; ++ return nullptr; ++ } ++} ++ ++} // namespace videocapturemodule ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.h +new file mode 100644 +index 000000000000..95de756484b9 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.h +@@ -0,0 +1,111 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_GST_CAMERA_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_GST_CAMERA_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "webrtc/base/platform_thread.h" ++#include "webrtc/common_types.h" ++ ++#include "webrtc/modules/video_capture/sfos/camera.h" ++ ++namespace webrtc ++{ ++namespace videocapturemodule ++{ ++ ++class GstDroidCameraFrame : public CameraFrame { ++public: ++ GstDroidCameraFrame(); ++ ~GstDroidCameraFrame(); ++ bool Map(GstSample *sample, unsigned frameSize); ++ ++private: ++ GstSample *_sample; ++ GstBuffer *_buffer; ++ GstMapInfo _info; ++}; ++ ++class GstDroidCamera : public Camera ++{ ++public: ++ GstDroidCamera(); ++ virtual ~GstDroidCamera(); ++ int32_t Init(const char* deviceUniqueId); ++ int32_t StartCapture(const VideoCaptureCapability& capability); ++ int32_t StopCapture(); ++ bool CaptureStarted(); ++ ++private: ++ // Called from GLib ++ static GstFlowReturn NewSampleCallback (GstElement *sink, void *data); ++ static void ErrorCallback(GstBus *bus, GstMessage *msg, void *data); ++ static bool StopCaptureCallback(void *data); ++ ++ static RawVideoType FourccToRawVideoType(const char *fourccStr); ++ ++ void ProcessSample(GstSample *sample); ++ void ProcessError(GstMessage *msg); ++ ++ static bool CaptureThread (void*); ++ bool CaptureProcess(); ++ ++ // FIXME: Figure out what to do with the comment below ++ // TODO(pbos): Stop using unique_ptr and resetting the thread. ++ std::unique_ptr _captureThread; ++ ++ VideoCaptureCapability _currentCaptureSettings; ++ bool _currentCaptureSettingsChanged = false; ++ bool _captureStarted = false; ++ ++ // Pre-calculated frame size ++ uint32_t _frameSize; ++ ++ bool _rearFacing = false; ++ ++ // GStreamer stuff ++ GstBus *_bus; ++ GstElement *_pipeline; ++ GstElement *_camera; ++ GstElement *_capsFilterDimensions; ++ GstElement *_capsFilterRate; ++ GMainContext *_captureContext; ++ GMainLoop *_mainLoop; ++}; ++ ++class GstDroidCameraManager : public CameraManager ++{ ++public: ++ GstDroidCameraManager(); ++ virtual ~GstDroidCameraManager(); ++ uint32_t NumberOfDevices() override; ++ int32_t GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8=0, ++ uint32_t productUniqueIdUTF8Length=0, ++ pid_t* pid=0) override; ++ bool QueryCapabilities (const char* deviceUniqueIdUTF8, ++ std::vector& caps) override; ++ rtc::scoped_refptr OpenCamera (const char* deviceUniqueIdUTF8) override; ++}; ++ ++} // namespace videocapturemodule ++} // namespace webrtc ++ ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_SFOS_CAMERA_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.cc b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.cc +new file mode 100644 +index 000000000000..9978d2bf35eb +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.cc +@@ -0,0 +1,764 @@ ++/* ++ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//v4l includes ++#if defined(__NetBSD__) || defined(__OpenBSD__) ++#include ++#elif defined(__sun) ++#include ++#else ++#include ++#endif ++ ++#include ++ ++#include "webrtc/base/refcount.h" ++#include "webrtc/base/scoped_ref_ptr.h" ++#include "webrtc/modules/video_capture/linux/video_capture_linux.h" ++#include "webrtc/system_wrappers/include/critical_section_wrapper.h" ++#include "webrtc/system_wrappers/include/trace.h" ++ ++#include "webrtc/modules/video_capture/sfos/v4l2_camera.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++ ++V4L2Camera::V4L2Camera() ++ : _captureCritSect(CriticalSectionWrapper::CreateCriticalSection()), ++ _deviceId(-1), ++ _deviceFd(-1), ++ _buffersAllocatedByDevice(-1), ++ _currentWidth(-1), ++ _currentHeight(-1), ++ _currentFrameRate(-1), ++ _captureStarted(false), ++ _captureVideoType(kVideoI420), ++ _pool(NULL) ++{ ++} ++ ++int32_t V4L2Camera::Init(const char* deviceUniqueIdUTF8) ++{ ++ int device_index; ++ if (sscanf(deviceUniqueIdUTF8,"fake_%d", &device_index) == 1) ++ { ++ _deviceId = device_index; ++ return 0; ++ } ++ ++ int fd; ++ char device[32]; ++ bool found = false; ++ ++ /* detect /dev/video [0-63] entries */ ++ int n; ++ for (n = 0; n < 64; n++) ++ { ++ snprintf(device, sizeof(device), "/dev/video%d", n); ++ if ((fd = open(device, O_RDONLY)) != -1) ++ { ++ // query device capabilities ++ struct v4l2_capability cap; ++ if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) ++ { ++ if (cap.bus_info[0] != 0) ++ { ++ if (strncmp((const char*) cap.bus_info, ++ (const char*) deviceUniqueIdUTF8, ++ strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id ++ { ++ close(fd); ++ found = true; ++ break; // fd matches with device unique id supplied ++ } ++ } ++ } ++ close(fd); // close since this is not the matching device ++ } ++ } ++ if (!found) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, ++ 0, "no matching device found"); ++ return -1; ++ } ++ _deviceId = n; //store the device id ++ return 0; ++} ++ ++V4L2Camera::~V4L2Camera() ++{ ++ StopCapture(); ++ if (_captureCritSect) ++ { ++ delete _captureCritSect; ++ } ++ if (_deviceFd != -1) ++ close(_deviceFd); ++} ++ ++int32_t V4L2Camera::StartCapture( ++ const VideoCaptureCapability& capability) ++{ ++ if (_captureStarted) ++ { ++ if (capability.width == _currentWidth && ++ capability.height == _currentHeight && ++ _captureVideoType == capability.rawType) ++ { ++ return 0; ++ } ++ else ++ { ++ StopCapture(); ++ } ++ } ++ ++ CriticalSectionScoped cs(_captureCritSect); ++ //first open /dev/video device ++ char device[21]; ++ snprintf(device, sizeof(device), "/dev/video%d", (int) _deviceId); ++ ++ if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "error in opening %s errono = %d", device, errno); ++ return -1; ++ } ++ ++ // Supported video formats in preferred order. ++ // If the requested resolution is larger than VGA, we prefer MJPEG. Go for ++ // I420 otherwise. ++ const int nFormats = 5; ++ unsigned int fmts[nFormats]; ++ if (capability.width > 640 || capability.height > 480) { ++ fmts[0] = V4L2_PIX_FMT_MJPEG; ++ fmts[1] = V4L2_PIX_FMT_YUV420; ++ fmts[2] = V4L2_PIX_FMT_YUYV; ++ fmts[3] = V4L2_PIX_FMT_UYVY; ++ fmts[4] = V4L2_PIX_FMT_JPEG; ++ } else { ++ fmts[0] = V4L2_PIX_FMT_YUV420; ++ fmts[1] = V4L2_PIX_FMT_YUYV; ++ fmts[2] = V4L2_PIX_FMT_UYVY; ++ fmts[3] = V4L2_PIX_FMT_MJPEG; ++ fmts[4] = V4L2_PIX_FMT_JPEG; ++ } ++ ++ // Enumerate image formats. ++ struct v4l2_fmtdesc fmt; ++ int fmtsIdx = nFormats; ++ memset(&fmt, 0, sizeof(fmt)); ++ fmt.index = 0; ++ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0, ++ "Video Capture enumerats supported image formats:"); ++ while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) { ++ WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0, ++ " { pixelformat = %c%c%c%c, description = '%s' }", ++ fmt.pixelformat & 0xFF, (fmt.pixelformat>>8) & 0xFF, ++ (fmt.pixelformat>>16) & 0xFF, (fmt.pixelformat>>24) & 0xFF, ++ fmt.description); ++ // Match the preferred order. ++ for (int i = 0; i < nFormats; i++) { ++ if (fmt.pixelformat == fmts[i] && i < fmtsIdx) ++ fmtsIdx = i; ++ } ++ // Keep enumerating. ++ fmt.index++; ++ } ++ ++ if (fmtsIdx == nFormats) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "no supporting video formats found"); ++ return -1; ++ } else { ++ WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0, ++ "We prefer format %c%c%c%c", ++ fmts[fmtsIdx] & 0xFF, (fmts[fmtsIdx]>>8) & 0xFF, ++ (fmts[fmtsIdx]>>16) & 0xFF, (fmts[fmtsIdx]>>24) & 0xFF); ++ } ++ ++ struct v4l2_format video_fmt; ++ memset(&video_fmt, 0, sizeof(struct v4l2_format)); ++ video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ video_fmt.fmt.pix.sizeimage = 0; ++ video_fmt.fmt.pix.width = capability.width; ++ video_fmt.fmt.pix.height = capability.height; ++ video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx]; ++ ++ if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ++ _captureVideoType = kVideoYUY2; ++ else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ++ _captureVideoType = kVideoI420; ++ else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ++ _captureVideoType = kVideoUYVY; ++ else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || ++ video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) ++ _captureVideoType = kVideoMJPEG; ++ ++ //set format and frame size now ++ if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "error in VIDIOC_S_FMT, errno = %d", errno); ++ return -1; ++ } ++ ++ // initialize current width and height ++ _currentWidth = video_fmt.fmt.pix.width; ++ _currentHeight = video_fmt.fmt.pix.height; ++ ++ // Trying to set frame rate, before check driver capability. ++ bool driver_framerate_support = true; ++ struct v4l2_streamparm streamparms; ++ memset(&streamparms, 0, sizeof(streamparms)); ++ streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "error in VIDIOC_G_PARM errno = %d", errno); ++ driver_framerate_support = false; ++ // continue ++ } else { ++ // check the capability flag is set to V4L2_CAP_TIMEPERFRAME. ++ if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { ++ // driver supports the feature. Set required framerate. ++ memset(&streamparms, 0, sizeof(streamparms)); ++ streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ streamparms.parm.capture.timeperframe.numerator = 1; ++ streamparms.parm.capture.timeperframe.denominator = capability.maxFPS; ++ if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "Failed to set the framerate. errno=%d", errno); ++ driver_framerate_support = false; ++ } else { ++ _currentFrameRate = capability.maxFPS; ++ } ++ } ++ } ++ // If driver doesn't support framerate control, need to hardcode. ++ // Hardcoding the value based on the frame size. ++ if (!driver_framerate_support) { ++ if(_currentWidth >= 800 && _captureVideoType != kVideoMJPEG) { ++ _currentFrameRate = 15; ++ } else { ++ _currentFrameRate = 30; ++ } ++ } ++ ++ if (!AllocateVideoBuffers()) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "failed to allocate video capture buffers"); ++ return -1; ++ } ++ ++ //start capture thread; ++ if (!_captureThread) ++ { ++ _captureThread.reset(new rtc::PlatformThread( ++ V4L2Camera::CaptureThread, this, "CaptureThread")); ++ _captureThread->Start(); ++ _captureThread->SetPriority(rtc::kHighPriority); ++ } ++ ++ // Needed to start UVC camera - from the uvcview application ++ enum v4l2_buf_type type; ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "Failed to turn on stream"); ++ return -1; ++ } ++ ++ _captureStarted = true; ++ return 0; ++} ++ ++int32_t V4L2Camera::StopCapture() ++{ ++ if (_captureThread) { ++ // Make sure the capture thread stop stop using the critsect. ++ _captureThread->Stop(); ++ _captureThread.reset(); ++ } ++ ++ CriticalSectionScoped cs(_captureCritSect); ++ if (_captureStarted) ++ { ++ _captureStarted = false; ++ ++ DeAllocateVideoBuffers(); ++ close(_deviceFd); ++ _deviceFd = -1; ++ } ++ ++ return 0; ++} ++ ++//critical section protected by the caller ++ ++bool V4L2Camera::AllocateVideoBuffers() ++{ ++ struct v4l2_requestbuffers rbuffer; ++ memset(&rbuffer, 0, sizeof(v4l2_requestbuffers)); ++ ++ rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ rbuffer.memory = V4L2_MEMORY_MMAP; ++ rbuffer.count = kNoOfV4L2Bufffers; ++ ++ if (ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer) < 0) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "Could not get buffers from device. errno = %d", errno); ++ return false; ++ } ++ ++ if (rbuffer.count > kNoOfV4L2Bufffers) ++ rbuffer.count = kNoOfV4L2Bufffers; ++ ++ _buffersAllocatedByDevice = rbuffer.count; ++ ++ //Map the buffers ++ _pool = new Buffer[rbuffer.count]; ++ ++ for (unsigned int i = 0; i < rbuffer.count; i++) ++ { ++ struct v4l2_buffer buffer; ++ memset(&buffer, 0, sizeof(v4l2_buffer)); ++ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ buffer.memory = V4L2_MEMORY_MMAP; ++ buffer.index = i; ++ ++ if (ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer) < 0) ++ { ++ return false; ++ } ++ ++ _pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, ++ _deviceFd, buffer.m.offset); ++ ++ if (MAP_FAILED == _pool[i].start) ++ { ++ for (unsigned int j = 0; j < i; j++) ++ munmap(_pool[j].start, _pool[j].length); ++ return false; ++ } ++ ++ _pool[i].length = buffer.length; ++ ++ if (ioctl(_deviceFd, VIDIOC_QBUF, &buffer) < 0) ++ { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool V4L2Camera::DeAllocateVideoBuffers() ++{ ++ // unmap buffers ++ for (int i = 0; i < _buffersAllocatedByDevice; i++) ++ munmap(_pool[i].start, _pool[i].length); ++ ++ delete[] _pool; ++ ++ // turn off stream ++ enum v4l2_buf_type type; ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ if (ioctl(_deviceFd, VIDIOC_STREAMOFF, &type) < 0) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "VIDIOC_STREAMOFF error. errno: %d", errno); ++ } ++ ++ return true; ++} ++ ++bool V4L2Camera::CaptureStarted() ++{ ++ return _captureStarted; ++} ++ ++bool V4L2Camera::CaptureThread(void* obj) ++{ ++ return static_cast (obj)->CaptureProcess(); ++} ++bool V4L2Camera::CaptureProcess() ++{ ++ int retVal = 0; ++ fd_set rSet; ++ struct timeval timeout; ++ ++ _captureCritSect->Enter(); ++ ++ FD_ZERO(&rSet); ++ FD_SET(_deviceFd, &rSet); ++ timeout.tv_sec = 1; ++ timeout.tv_usec = 0; ++ ++ retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout); ++ if (retVal < 0 && errno != EINTR) // continue if interrupted ++ { ++ // select failed ++ _captureCritSect->Leave(); ++ return false; ++ } ++ else if (retVal == 0) ++ { ++ // select timed out ++ _captureCritSect->Leave(); ++ return true; ++ } ++ else if (!FD_ISSET(_deviceFd, &rSet)) ++ { ++ // not event on camera handle ++ _captureCritSect->Leave(); ++ return true; ++ } ++ ++ if (_captureStarted) ++ { ++ struct v4l2_buffer buf; ++ memset(&buf, 0, sizeof(struct v4l2_buffer)); ++ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ buf.memory = V4L2_MEMORY_MMAP; ++ // dequeue a buffer - repeat until dequeued properly! ++ while (ioctl(_deviceFd, VIDIOC_DQBUF, &buf) < 0) ++ { ++ if (errno != EINTR) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "could not sync on a buffer on device %s", strerror(errno)); ++ _captureCritSect->Leave(); ++ return true; ++ } ++ } ++ ++ CameraFrame frame; ++ ++ frame.data = (uint8_t *)_pool[buf.index].start; ++ frame.size = buf.bytesused; ++ frame.timestampMs = 0; ++ ++ if (_cameraListener) { ++ _cameraListener->OnCameraFrame(&frame); ++ } ++ ++ // enqueue the buffer again ++ if (ioctl(_deviceFd, VIDIOC_QBUF, &buf) == -1) ++ { ++ WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, 0, ++ "Failed to enqueue capture buffer"); ++ } ++ } ++ _captureCritSect->Leave(); ++ usleep(0); ++ return true; ++} ++ ++int32_t V4L2Camera::CaptureSettings(VideoCaptureCapability& settings) ++{ ++ settings.width = _currentWidth; ++ settings.height = _currentHeight; ++ settings.maxFPS = _currentFrameRate; ++ settings.rawType=_captureVideoType; ++ ++ return 0; ++} ++ ++V4L2CameraManager::V4L2CameraManager() ++{ ++} ++ ++V4L2CameraManager::~V4L2CameraManager() ++{ ++} ++ ++uint32_t V4L2CameraManager::NumberOfDevices() ++{ ++ WEBRTC_TRACE(webrtc::kTraceApiCall, ++ webrtc::kTraceVideoCapture, 0, "%s", __FUNCTION__); ++ ++ uint32_t count = 0; ++ char device[20]; ++ int fd = -1; ++ ++ /* detect /dev/video [0-63]VideoCaptureModule entries */ ++ for (int n = 0; n < 64; n++) ++ { ++ snprintf(device, sizeof(device), "/dev/video%d", n); ++ if ((fd = open(device, O_RDONLY)) != -1) ++ { ++ close(fd); ++ count++; ++ } ++ } ++ ++ return count; ++} ++ ++int32_t V4L2CameraManager::GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8, ++ uint32_t productUniqueIdUTF8Length, ++ pid_t* pid) ++{ ++ WEBRTC_TRACE(webrtc::kTraceApiCall, ++ webrtc::kTraceVideoCapture, 0, "%s", __FUNCTION__); ++ ++ // Travel through /dev/video [0-63] ++ uint32_t count = 0; ++ char device[20]; ++ int fd = -1; ++ bool found = false; ++ int device_index; ++ for (device_index = 0; device_index < 64; device_index++) ++ { ++ snprintf(device, sizeof(device), "/dev/video%d", device_index); ++ if ((fd = open(device, O_RDONLY)) != -1) ++ { ++ if (count == deviceNumber) { ++ // Found the device ++ found = true; ++ break; ++ } else { ++ close(fd); ++ count++; ++ } ++ } ++ } ++ ++ if (!found) ++ return -1; ++ ++ // query device capabilities ++ struct v4l2_capability cap; ++ if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "error in querying the device capability for device %s. errno = %d", ++ device, errno); ++ close(fd); ++ return -1; ++ } ++ ++ close(fd); ++ ++ char cameraName[64]; ++ memset(deviceNameUTF8, 0, deviceNameLength); ++ memcpy(cameraName, cap.card, sizeof(cap.card)); ++ ++ if (deviceNameLength >= strlen(cameraName)) ++ { ++ memcpy(deviceNameUTF8, cameraName, strlen(cameraName)); ++ } ++ else ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "buffer passed is too small"); ++ return -1; ++ } ++ ++ if (cap.bus_info[0] != 0) // may not available in all drivers ++ { ++ // copy device id ++ if (deviceUniqueIdUTF8Length >= strlen((const char*) cap.bus_info)) ++ { ++ memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length); ++ memcpy(deviceUniqueIdUTF8, cap.bus_info, ++ strlen((const char*) cap.bus_info)); ++ } ++ else ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "buffer passed is too small"); ++ return -1; ++ } ++ } else { ++ // if there's no bus info to use for uniqueId, invent one - and it has to be repeatable ++ if (snprintf(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length, "fake_%u", device_index) >= ++ (int) deviceUniqueIdUTF8Length) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, ++ "buffer passed is too small"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++rtc::scoped_refptr V4L2CameraManager::OpenCamera (const char* deviceUniqueIdUTF8) ++{ ++ LOG_F(LS_INFO) << deviceUniqueIdUTF8; ++ ++ rtc::scoped_refptr camera(new rtc::RefCountedObject()); ++ ++ if (!camera->Init(deviceUniqueIdUTF8)) { ++ return camera; ++ } else { ++ LOG_F(LS_ERROR) << "Cannot initialize camera"; ++ return nullptr; ++ } ++} ++ ++bool V4L2CameraManager::QueryCapabilities(const char* deviceUniqueIdUTF8, ++ std::vector& caps) ++{ ++ int fd; ++ char device[32]; ++ bool found = false; ++ int device_index; ++ ++ const int32_t deviceUniqueIdUTF8Length = ++ (int32_t) strlen((char*) deviceUniqueIdUTF8); ++ if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, ++ webrtc::kTraceVideoCapture, 0, "Device name too long"); ++ return false; ++ } ++ WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0, ++ "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8); ++ ++ /* detect /dev/video [0-63] entries */ ++ if (sscanf(deviceUniqueIdUTF8,"fake_%d",&device_index) == 1) ++ { ++ snprintf(device, sizeof(device), "/dev/video%d", device_index); ++ fd = open(device, O_RDONLY); ++ if (fd != -1) { ++ found = true; ++ } ++ } else { ++ /* detect /dev/video [0-63] entries */ ++ for (int n = 0; n < 64; ++n) ++ { ++ snprintf(device, sizeof(device), "/dev/video%d", n); ++ fd = open(device, O_RDONLY); ++ if (fd == -1) ++ continue; ++ ++ // query device capabilities ++ struct v4l2_capability cap; ++ if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) ++ { ++ if (cap.bus_info[0] != 0) ++ { ++ if (strncmp((const char*) cap.bus_info, ++ (const char*) deviceUniqueIdUTF8, ++ strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id ++ { ++ found = true; ++ break; // fd matches with device unique id supplied ++ } ++ } ++ // else can't be a match as the test for fake_* above would have matched it ++ } ++ close(fd); // close since this is not the matching device ++ } ++ } ++ if (!found) ++ { ++ WEBRTC_TRACE(webrtc::kTraceError, ++ webrtc::kTraceVideoCapture, 0, "no matching device found"); ++ return false; ++ } ++ ++ // now fd will point to the matching device ++ // reset old capability list. ++ caps.clear(); ++ ++ FillCapabilities(fd, caps); ++ close(fd); ++ ++ WEBRTC_TRACE(webrtc::kTraceInfo, ++ webrtc::kTraceVideoCapture, ++ 0, ++ "CreateCapabilityMap %u", ++ static_cast(caps.size())); ++ ++ return true; ++} ++ ++int32_t V4L2CameraManager::FillCapabilities(int fd, std::vector& caps) ++{ ++ struct v4l2_fmtdesc fmt; ++ struct v4l2_frmsizeenum frmsize; ++ struct v4l2_frmivalenum frmival; ++ ++ fmt.index = 0; ++ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) { ++ frmsize.pixel_format = fmt.pixelformat; ++ frmsize.index = 0; ++ while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) { ++ if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { ++ frmival.index = 0; ++ frmival.pixel_format = fmt.pixelformat; ++ frmival.width = frmsize.discrete.width; ++ frmival.height = frmsize.discrete.height; ++ if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) { ++ if (fmt.pixelformat == V4L2_PIX_FMT_YUYV || ++ fmt.pixelformat == V4L2_PIX_FMT_YUV420 || ++ fmt.pixelformat == V4L2_PIX_FMT_MJPEG) { ++ ++ VideoCaptureCapability cap; ++ cap.width = frmsize.discrete.width; ++ cap.height = frmsize.discrete.height; ++ cap.expectedCaptureDelay = 120; ++ ++ if (fmt.pixelformat == V4L2_PIX_FMT_YUYV) ++ { ++ cap.rawType = kVideoYUY2; ++ } ++ else if (fmt.pixelformat == V4L2_PIX_FMT_YUV420) ++ { ++ cap.rawType = kVideoI420; ++ } ++ else if (fmt.pixelformat == V4L2_PIX_FMT_MJPEG) ++ { ++ cap.rawType = kVideoMJPEG; ++ } ++ ++ cap.maxFPS = frmival.discrete.denominator / frmival.discrete.numerator; ++ caps.push_back(cap); ++ } ++ } ++ } ++ frmsize.index++; ++ } ++ fmt.index++; ++ } ++ ++ WEBRTC_TRACE(webrtc::kTraceInfo, ++ webrtc::kTraceVideoCapture, ++ 0, ++ "CreateCapabilityMap %u", ++ static_cast(caps.size())); ++ return caps.size(); ++} ++} // namespace videocapturemodule ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.h +new file mode 100644 +index 000000000000..4d8c6a953994 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.h +@@ -0,0 +1,95 @@ ++/* ++ * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_V4L2_CAMERA_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_V4L2_CAMERA_H_ ++ ++#include ++ ++#include "webrtc/base/platform_thread.h" ++#include "webrtc/common_types.h" ++#include "webrtc/modules/video_capture/video_capture_impl.h" ++ ++#include "webrtc/modules/video_capture/sfos/camera.h" ++ ++namespace webrtc ++{ ++class CriticalSectionWrapper; ++namespace videocapturemodule ++{ ++ ++class V4L2Camera: public Camera ++{ ++public: ++ V4L2Camera(); ++ virtual ~V4L2Camera(); ++ virtual int32_t Init(const char* deviceUniqueId); ++ virtual int32_t StartCapture(const VideoCaptureCapability& capability); ++ virtual int32_t StopCapture(); ++ virtual bool CaptureStarted(); ++ virtual int32_t CaptureSettings(VideoCaptureCapability& settings); ++ ++private: ++ enum {kNoOfV4L2Bufffers=4}; ++ ++ static bool CaptureThread(void*); ++ bool CaptureProcess(); ++ bool AllocateVideoBuffers(); ++ bool DeAllocateVideoBuffers(); ++ ++ // TODO(pbos): Stop using unique_ptr and resetting the thread. ++ std::unique_ptr _captureThread; ++ CriticalSectionWrapper* _captureCritSect; ++ ++ int32_t _deviceId; ++ int32_t _deviceFd; ++ ++ int32_t _buffersAllocatedByDevice; ++ int32_t _currentWidth; ++ int32_t _currentHeight; ++ int32_t _currentFrameRate; ++ bool _captureStarted; ++ RawVideoType _captureVideoType; ++ struct Buffer ++ { ++ void *start; ++ size_t length; ++ }; ++ Buffer *_pool; ++}; ++ ++class V4L2CameraManager : public CameraManager ++{ ++public: ++ V4L2CameraManager(); ++ virtual ~V4L2CameraManager(); ++ uint32_t NumberOfDevices(); ++ int32_t GetDeviceName( ++ uint32_t deviceNumber, ++ char* deviceNameUTF8, ++ uint32_t deviceNameLength, ++ char* deviceUniqueIdUTF8, ++ uint32_t deviceUniqueIdUTF8Length, ++ char* productUniqueIdUTF8=0, ++ uint32_t productUniqueIdUTF8Length=0, ++ pid_t* pid=0); ++ bool QueryCapabilities (const char* deviceUniqueIdUTF8, ++ std::vector& caps); ++ rtc::scoped_refptr OpenCamera (const char* deviceUniqueIdUTF8); ++private: ++ int32_t FillCapabilities(int fd, std::vector& caps); ++ unsigned _numberOfDevices; ++}; ++ ++} // namespace videocapturemodule ++} // namespace webrtc ++ ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_V4L2_CAMERA_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.cc b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.cc +new file mode 100644 +index 000000000000..fd3c8bd35632 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.cc +@@ -0,0 +1,174 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "webrtc/system_wrappers/include/trace.h" ++#include "webrtc/system_wrappers/include/clock.h" ++ ++#include "webrtc/modules/video_capture/sfos/video_capture_sfos.h" ++ ++namespace webrtc { ++namespace videocapturemodule { ++rtc::scoped_refptr VideoCaptureImpl::Create( ++ const char* deviceUniqueId) { ++ rtc::scoped_refptr implementation( ++ new rtc::RefCountedObject()); ++ ++ if (implementation->Init(deviceUniqueId) != 0) ++ return nullptr; ++ ++ return implementation; ++} ++ ++VideoCaptureModuleSFOS::VideoCaptureModuleSFOS() ++ : VideoCaptureImpl() ++{ ++ mozilla::hal::ScreenConfiguration screenConfig; ++ ++ mozilla::hal::RegisterScreenConfigurationObserver (this); ++ mozilla::hal::GetCurrentScreenConfiguration (&screenConfig); ++ _screenRotationAngle = ScreenOrientationToAngle (screenConfig.orientation()); ++} ++ ++int32_t VideoCaptureModuleSFOS::Init(const char* deviceUniqueIdUTF8) ++{ ++ int len = strlen(deviceUniqueIdUTF8); ++ ++ /* Fill the current device name */ ++ _deviceUniqueId = new (std::nothrow) char[len + 1]; ++ if (_deviceUniqueId) { ++ memcpy(_deviceUniqueId, deviceUniqueIdUTF8, len + 1); ++ } else { ++ return -1; ++ } ++ ++ auto cameraManager = GetCameraManager(SFOS_CAMERA_TYPE_GST_DROID); ++ if (cameraManager) { ++ _camera = cameraManager->OpenCamera(deviceUniqueIdUTF8); ++ if (_camera) { ++ _camera->SetListener(this); ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ ++VideoCaptureModuleSFOS::~VideoCaptureModuleSFOS() ++{ ++ mozilla::hal::UnregisterScreenConfigurationObserver(this); ++} ++ ++int32_t VideoCaptureModuleSFOS::StartCapture( ++ const VideoCaptureCapability& capability) ++{ ++ _requestedCapability = capability; ++ _startNtpTimeMs = webrtc::Clock::GetRealTimeClock()->CurrentNtpInMilliseconds(); ++ return _camera->StartCapture(capability); ++} ++ ++int32_t VideoCaptureModuleSFOS::StopCapture() ++{ ++ return _camera->StopCapture(); ++} ++ ++bool VideoCaptureModuleSFOS::CaptureStarted() ++{ ++ return _camera->CaptureStarted(); ++} ++ ++void VideoCaptureModuleSFOS::Notify(const mozilla::hal::ScreenConfiguration& aConfiguration) ++{ ++ LOG_F (LS_INFO) << "VideoCaptureModuleSFOS::Notify ScreenConfiguration.orientation: " << aConfiguration.orientation(); ++ _screenRotationAngle = ScreenOrientationToAngle (aConfiguration.orientation()); ++ UpdateCaptureRotation (); ++} ++ ++int VideoCaptureModuleSFOS::ScreenOrientationToAngle (mozilla::dom::ScreenOrientationInternal orientation) ++{ ++ switch (orientation) { ++ /* FIXME: What is the default orientation? */ ++ case mozilla::dom::eScreenOrientation_Default: ++ case mozilla::dom::eScreenOrientation_PortraitPrimary: ++ return 0; ++ case mozilla::dom::eScreenOrientation_LandscapePrimary: ++ return 90; ++ case mozilla::dom::eScreenOrientation_PortraitSecondary: ++ return 180; ++ case mozilla::dom::eScreenOrientation_LandscapeSecondary: ++ return 270; ++ default: ++ return 0; ++ } ++} ++ ++void VideoCaptureModuleSFOS::OnCameraFrame(const CameraFrame* frame) ++{ ++ LOG_F(LS_VERBOSE) << "frame ts=" << (frame->timestampMs + _startNtpTimeMs); ++ IncomingFrame(frame->data, frame->size, _currentCaptureSettings, frame->timestampMs + _startNtpTimeMs); ++} ++ ++void VideoCaptureModuleSFOS::OnCameraError(const char *errorDescription) ++{ ++} ++ ++void VideoCaptureModuleSFOS::OnCameraSettingsChange(VideoCaptureCapability& caps, ++ int sensorMountAngle, ++ bool rearFacing) ++{ ++ _currentCaptureSettings = caps; ++ _sensorMountAngle = sensorMountAngle; ++ _rearFacingCamera = rearFacing; ++ UpdateCaptureRotation(); ++} ++ ++void VideoCaptureModuleSFOS::UpdateCaptureRotation () ++{ ++ VideoRotation rotation; ++ int rotateAngle = 360 + _sensorMountAngle + (_rearFacingCamera ? -_screenRotationAngle : _screenRotationAngle); ++ ++ switch (rotateAngle % 360) { ++ case 90: ++ rotation = kVideoRotation_90; ++ break; ++ case 180: ++ rotation = kVideoRotation_180; ++ break; ++ case 270: ++ rotation = kVideoRotation_270; ++ break; ++ default: ++ rotation = kVideoRotation_0; ++ break; ++ } ++ ++ LOG_F (LS_INFO) << "Sensor mount angle=" << _sensorMountAngle ++ << " Screen rotation=" << _screenRotationAngle ++ << " Capture rotation=" << rotateAngle; ++ VideoCaptureImpl::SetCaptureRotation (rotation); ++} ++ ++int32_t VideoCaptureModuleSFOS::CaptureSettings(VideoCaptureCapability& settings) ++{ ++ settings = _requestedCapability; ++ return 0; ++} ++} // namespace videocapturemodule ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.h b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.h +new file mode 100644 +index 000000000000..d48beb62b885 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.h +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (c) 2021 Open Mobile Platform LLC. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_VIDEO_CAPTURE_SFOS_H_ ++#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_VIDEO_CAPTURE_SFOS_H_ ++ ++#include ++ ++#include ++#include ++ ++#include "mozilla/Hal.h" ++#include "mozilla/dom/ScreenOrientation.h" ++ ++#include "webrtc/common_types.h" ++#include "webrtc/base/refcount.h" ++#include "webrtc/base/scoped_ref_ptr.h" ++#include "webrtc/modules/video_capture/video_capture_impl.h" ++#include "webrtc/modules/video_capture/sfos/camera.h" ++ ++namespace webrtc ++{ ++class CriticalSectionWrapper; ++namespace videocapturemodule ++{ ++class VideoCaptureModuleSFOS ++ : public VideoCaptureImpl, ++ public CameraListener, ++ public mozilla::hal::ScreenConfigurationObserver ++{ ++public: ++ VideoCaptureModuleSFOS(); ++ virtual ~VideoCaptureModuleSFOS(); ++ virtual int32_t Init(const char* deviceUniqueId); ++ virtual int32_t StartCapture(const VideoCaptureCapability& capability); ++ virtual int32_t StopCapture(); ++ virtual bool CaptureStarted(); ++ virtual int32_t CaptureSettings(VideoCaptureCapability& settings); ++ ++ void OnCameraFrame(const CameraFrame* frame); ++ void OnCameraError(const char *errorDescription); ++ void OnCameraSettingsChange(VideoCaptureCapability& caps, int sensorOrientationAngle, bool rearFacing); ++ ++ virtual void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration) override; ++ ++private: ++ int ScreenOrientationToAngle(mozilla::dom::ScreenOrientationInternal orientation); ++ void UpdateCaptureRotation(); ++ ++ VideoCaptureCapability _currentCaptureSettings; ++ int _screenRotationAngle = 0; ++ int _sensorMountAngle = 0; ++ bool _rearFacingCamera = false; ++ uint64_t _startNtpTimeMs = 0; ++ rtc::scoped_refptr _camera; ++}; ++} // namespace videocapturemodule ++} // namespace webrtc ++ ++#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_SFOS_VIDEO_CAPTURE_SFOS_H_ +diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build +index 32d4302d2550..6de18acb7332 100644 +--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build ++++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_internal_impl_gn/moz.build +@@ -128,11 +128,22 @@ if CONFIG["OS_TARGET"] == "Linux": + "rt" + ] + ++ OS_LIBS += CONFIG["LIBGSTREAMER_LIBS"] ++ + UNIFIED_SOURCES += [ +- "/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc", +- "/media/webrtc/trunk/webrtc/modules/video_capture/linux/video_capture_linux.cc" ++ "/media/webrtc/trunk/webrtc/modules/video_capture/sfos/camera_manager.cc", ++ "/media/webrtc/trunk/webrtc/modules/video_capture/sfos/device_info_sfos.cc", ++ "/media/webrtc/trunk/webrtc/modules/video_capture/sfos/gst_droid_camera.cc", ++ "/media/webrtc/trunk/webrtc/modules/video_capture/sfos/v4l2_camera.cc", ++ "/media/webrtc/trunk/webrtc/modules/video_capture/sfos/video_capture_sfos.cc" + ] + ++ CXXFLAGS += CONFIG["LIBGSTREAMER_CFLAGS"] ++ ++ # Provide path to Hal.h and friends ++ CXXFLAGS += CONFIG["NSPR_CFLAGS"] ++ CXXFLAGS += CONFIG["MOZ_PIXMAN_CFLAGS"] ++ + if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" +diff --git a/old-configure.in b/old-configure.in +index 5f0b6a1ecd6e..2a4b9415b04e 100644 +--- a/old-configure.in ++++ b/old-configure.in +@@ -2552,6 +2552,17 @@ if test -n "$MOZ_WEBRTC"; then + if test -n "$MOZ_X11"; then + MOZ_WEBRTC_X11_LIBS="-lXext -lXdamage -lXfixes -lXcomposite" + fi ++ ++ MOZ_ENABLE_WEBRTC_GST_GLUE= ++ PKG_CHECK_MODULES(LIBGSTREAMER, gstreamer-1.0, ++ MOZ_ENABLE_WEBRTC_GST_GLUE=1, ++ MOZ_ENABLE_WEBRTC_GST_GLUE=) ++ if test "$MOZ_ENABLE_WEBRTC_GST_GLUE"; then ++ MOZ_ENABLE_WEBRTC_GST_GLUE=1 ++ AC_DEFINE(MOZ_ENABLE_WEBRTC_GST_GLUE) ++ AC_SUBST(MOZ_ENABLE_WEBRTC_GST_GLUE) ++ fi ++ + fi + + dnl ======================================================== +-- +2.17.1 + diff --git a/rpm/xulrunner-qt5.spec b/rpm/xulrunner-qt5.spec index 43b08a778a6fd..3d8128d5e7a7f 100644 --- a/rpm/xulrunner-qt5.spec +++ b/rpm/xulrunner-qt5.spec @@ -120,7 +120,8 @@ Patch65: 0065-Fix-flipped-FBO-textures-when-rendering-to-an-offscr.patch Patch66: 0066-sailfishos-webrtc-Adapt-build-configuration-for-Sail.patch Patch67: 0067-sailfishos-webrtc-Regenerate-moz.build-files.-JB-537.patch Patch68: 0068-sailfishos-webrtc-Disable-desktop-sharing-feature-on.patch -Patch69: 0069-sailfishos-webrtc-Disable-enumeration-of-video-devic.patch +Patch69: 0069-sailfishos-webrtc-Enable-GMP-for-encoding.-JB-53982.patch +Patch70: 0070-sailfishos-webrtc-Implement-video-capture-module.-JB.patch BuildRequires: rust BuildRequires: rust-std-static @@ -149,6 +150,7 @@ BuildRequires: pkgconfig(libswscale) BuildRequires: pkgconfig(Qt5Positioning) BuildRequires: pkgconfig(contentaction5) BuildRequires: pkgconfig(dconf) +BuildRequires: pkgconfig(gstreamer-1.0) BuildRequires: qt5-qttools BuildRequires: qt5-default BuildRequires: autoconf213