Bug 1297337 - implement mediaDevices.ondevicechange for Linux; r=jesup draft
authorMunro Chiang <mchiang@mozilla.com>
Thu, 01 Sep 2016 11:06:49 +0800
changeset 415271 ee71fea0aa49452bcd403678b3c22c3fe3dd297c
parent 411433 77940cbf0c2a9f52c209fbbde5b2e7d4c74a1501
child 415902 7c9b7a9f96e82f6f752d600d9cbd3b3ce8f3a486
push id29841
push usermchiang@mozilla.com
push dateTue, 20 Sep 2016 03:52:04 +0000
reviewersjesup
bugs1297337
milestone51.0a1
Bug 1297337 - implement mediaDevices.ondevicechange for Linux; r=jesup MozReview-Commit-ID: 6cEq7xVUkhf
dom/media/tests/mochitest/mochitest.ini
media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc
media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.h
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -36,17 +36,17 @@ skip-if = toolkit == 'gonk' || buildapp 
 [test_dataChannel_basicVideo.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_bug1013809.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_dataChannel_noOffer.html]
 [test_enumerateDevices.html]
 skip-if = buildapp == 'mulet'
 [test_ondevicechange.html]
-skip-if = toolkit == 'gonk' || buildapp == 'mulet' || os == 'linux' || os == 'win' || os == 'android'
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || os == 'win' || os == 'android'
 [test_getUserMedia_audioCapture.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g emulator seems to be too slow (Bug 1016498 and 1008080), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_getUserMedia_addTrackRemoveTrack.html]
 skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
 [test_getUserMedia_addtrack_removetrack_events.html]
 [test_getUserMedia_basicAudio.html]
 skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
 [test_getUserMedia_basicVideo.html]
--- 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
@@ -24,16 +24,18 @@
 #include <sys/videodev2.h>
 #else
 #include <linux/videodev2.h>
 #endif
 
 #include "webrtc/system_wrappers/interface/ref_count.h"
 #include "webrtc/system_wrappers/interface/trace.h"
 
+#define EVENT_SIZE  ( sizeof (struct inotify_event) )
+#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
 
 namespace webrtc
 {
 namespace videocapturemodule
 {
 VideoCaptureModule::DeviceInfo*
 VideoCaptureImpl::CreateDeviceInfo(const int32_t id)
 {
@@ -42,28 +44,153 @@ VideoCaptureImpl::CreateDeviceInfo(const
     if (!deviceInfo)
     {
         deviceInfo = NULL;
     }
 
     return deviceInfo;
 }
 
+void DeviceInfoLinux::HandleEvent(inotify_event* event)
+{
+    switch (event->mask) {
+        case IN_CREATE:
+            DeviceChange();
+            break;
+        case IN_DELETE:
+            DeviceChange();
+            break;
+        default:
+            char* cur_event_filename = NULL;
+            int cur_event_wd = event->wd;
+            if (event->len) {
+                cur_event_filename = event->name;
+            }
+
+            WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
+                "UNKNOWN EVENT OCCURRED for file \"%s\" on WD #%i\n",
+                cur_event_filename, cur_event_wd);
+            break;
+    }
+}
+
+int DeviceInfoLinux::EventCheck()
+{
+    struct timeval timeout;
+    fd_set rfds;
+
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 100000;
+
+    FD_ZERO(&rfds);
+    FD_SET(_fd, &rfds);
+
+    return select(_fd+1, &rfds, NULL, NULL, &timeout);
+}
+
+int DeviceInfoLinux::HandleEvents()
+{
+    char buffer[BUF_LEN];
+
+    ssize_t r = read(_fd, buffer, BUF_LEN);
+
+    if (r <= 0) {
+        return r;
+    }
+
+    ssize_t buffer_i = 0;
+    inotify_event* pevent;
+    size_t eventSize;
+    int count = 0;
+
+    while (buffer_i < r)
+    {
+        pevent = (inotify_event *) (&buffer[buffer_i]);
+        eventSize = sizeof(inotify_event) + pevent->len;
+        char event[sizeof(inotify_event) + FILENAME_MAX + 1] // null-terminated
+            __attribute__ ((aligned(__alignof__(struct inotify_event))));
+
+        memcpy(event, pevent, eventSize);
+
+        HandleEvent((inotify_event*)(event));
+
+        buffer_i += eventSize;
+        count++;
+    }
+
+    return count;
+}
+
+int DeviceInfoLinux::ProcessInotifyEvents()
+{
+    while (0 == _isShutdown.Value()) {
+        if (EventCheck() > 0) {
+            if (HandleEvents() < 0) {
+                break;
+            }
+        }
+    }
+    return 0;
+}
+
+bool DeviceInfoLinux::InotifyEventThread(void* obj)
+{
+    return static_cast<DeviceInfoLinux*> (obj)->InotifyProcess();
+}
+
+bool DeviceInfoLinux::InotifyProcess()
+{
+    _fd = inotify_init();
+    if (_fd >= 0) {
+        _wd_v4l = inotify_add_watch(_fd, "/dev/v4l/by-path/", IN_CREATE | IN_DELETE);
+        _wd_snd = inotify_add_watch(_fd, "/dev/snd/by-path/", IN_CREATE | IN_DELETE);
+        ProcessInotifyEvents();
+
+        if (_wd_v4l >= 0) {
+          inotify_rm_watch(_fd, _wd_v4l);
+        }
+
+        if (_wd_snd >= 0) {
+          inotify_rm_watch(_fd, _wd_snd);
+        }
+
+        close(_fd);
+        return true;
+    } else {
+        return false;
+    }
+}
+
 DeviceInfoLinux::DeviceInfoLinux(const int32_t id)
     : DeviceInfoImpl(id)
+    , _isShutdown(0)
 {
+    _inotifyEventThread = ThreadWrapper::CreateThread(
+        InotifyEventThread, this, "InotifyEventThread");
+
+    if (_inotifyEventThread)
+    {
+        _inotifyEventThread->Start();
+        _inotifyEventThread->SetPriority(kHighPriority);
+    }
 }
 
 int32_t DeviceInfoLinux::Init()
 {
     return 0;
 }
 
 DeviceInfoLinux::~DeviceInfoLinux()
 {
+    ++_isShutdown;
+
+    if (_inotifyEventThread) {
+        _inotifyEventThread->Stop();
+        _inotifyEventThread.reset();
+    }
 }
 
 uint32_t DeviceInfoLinux::NumberOfDevices()
 {
     WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__);
 
     uint32_t count = 0;
     char device[20];
--- a/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.h
@@ -8,16 +8,19 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_
 #define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_
 
 #include "webrtc/modules/video_capture/device_info_impl.h"
 #include "webrtc/modules/video_capture/video_capture_impl.h"
+#include "webrtc/system_wrappers/interface/thread_wrapper.h"
+#include "webrtc/system_wrappers/interface/atomic32.h"
+#include <sys/inotify.h>
 
 namespace webrtc
 {
 namespace videocapturemodule
 {
 class DeviceInfoLinux: public DeviceInfoImpl
 {
 public:
@@ -42,12 +45,22 @@ public:
         void* /*parentWindow*/,
         uint32_t /*positionX*/,
         uint32_t /*positionY*/) { return -1;}
     int32_t FillCapabilities(int fd);
     int32_t Init();
 private:
 
     bool IsDeviceNameMatches(const char* name, const char* deviceUniqueIdUTF8);
+
+    void HandleEvent(inotify_event* event);
+    int EventCheck();
+    int HandleEvents();
+    int ProcessInotifyEvents();
+    rtc::scoped_ptr<ThreadWrapper> _inotifyEventThread;
+    static bool InotifyEventThread(void*);
+    bool InotifyProcess();
+    int _fd, _wd_v4l, _wd_snd; /* accessed on InotifyEventThread thread */
+    Atomic32 _isShutdown;
 };
 }  // namespace videocapturemodule
 }  // namespace webrtc
 #endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_