aboutsummaryrefslogtreecommitdiff
path: root/talk/media/devices
diff options
context:
space:
mode:
Diffstat (limited to 'talk/media/devices')
-rw-r--r--talk/media/devices/carbonvideorenderer.cc190
-rw-r--r--talk/media/devices/carbonvideorenderer.h74
-rw-r--r--talk/media/devices/deviceinfo.h42
-rw-r--r--talk/media/devices/devicemanager.cc371
-rw-r--r--talk/media/devices/devicemanager.h211
-rw-r--r--talk/media/devices/devicemanager_unittest.cc470
-rw-r--r--talk/media/devices/dummydevicemanager.cc38
-rw-r--r--talk/media/devices/dummydevicemanager.h51
-rw-r--r--talk/media/devices/dummydevicemanager_unittest.cc104
-rw-r--r--talk/media/devices/fakedevicemanager.h239
-rw-r--r--talk/media/devices/filevideocapturer.cc385
-rw-r--r--talk/media/devices/filevideocapturer.h160
-rw-r--r--talk/media/devices/filevideocapturer_unittest.cc204
-rw-r--r--talk/media/devices/gdivideorenderer.cc279
-rwxr-xr-xtalk/media/devices/gdivideorenderer.h63
-rwxr-xr-xtalk/media/devices/gtkvideorenderer.cc179
-rwxr-xr-xtalk/media/devices/gtkvideorenderer.h74
-rw-r--r--talk/media/devices/libudevsymboltable.cc72
-rw-r--r--talk/media/devices/libudevsymboltable.h79
-rw-r--r--talk/media/devices/linuxdeviceinfo.cc174
-rw-r--r--talk/media/devices/linuxdevicemanager.cc410
-rw-r--r--talk/media/devices/linuxdevicemanager.h55
-rw-r--r--talk/media/devices/macdeviceinfo.cc56
-rw-r--r--talk/media/devices/macdevicemanager.cc196
-rw-r--r--talk/media/devices/macdevicemanager.h56
-rw-r--r--talk/media/devices/macdevicemanagermm.mm192
-rw-r--r--talk/media/devices/mobiledevicemanager.cc77
-rw-r--r--talk/media/devices/v4llookup.cc92
-rw-r--r--talk/media/devices/v4llookup.h70
-rw-r--r--talk/media/devices/videorendererfactory.h69
-rw-r--r--talk/media/devices/win32deviceinfo.cc62
-rw-r--r--talk/media/devices/win32devicemanager.cc414
-rw-r--r--talk/media/devices/win32devicemanager.h60
-rw-r--r--talk/media/devices/yuvframescapturer.cc199
-rw-r--r--talk/media/devices/yuvframescapturer.h98
35 files changed, 5565 insertions, 0 deletions
diff --git a/talk/media/devices/carbonvideorenderer.cc b/talk/media/devices/carbonvideorenderer.cc
new file mode 100644
index 0000000000..846135d925
--- /dev/null
+++ b/talk/media/devices/carbonvideorenderer.cc
@@ -0,0 +1,190 @@
+/*
+ * libjingle
+ * Copyright 2011 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Implementation of CarbonVideoRenderer
+
+#include "talk/media/devices/carbonvideorenderer.h"
+
+#include "talk/media/base/videocommon.h"
+#include "talk/media/base/videoframe.h"
+#include "webrtc/base/logging.h"
+
+namespace cricket {
+
+CarbonVideoRenderer::CarbonVideoRenderer(int x, int y)
+ : image_width_(0),
+ image_height_(0),
+ x_(x),
+ y_(y),
+ image_ref_(NULL),
+ window_ref_(NULL) {
+}
+
+CarbonVideoRenderer::~CarbonVideoRenderer() {
+ if (window_ref_) {
+ DisposeWindow(window_ref_);
+ }
+}
+
+// Called from the main event loop. All renderering needs to happen on
+// the main thread.
+OSStatus CarbonVideoRenderer::DrawEventHandler(EventHandlerCallRef handler,
+ EventRef event,
+ void* data) {
+ OSStatus status = noErr;
+ CarbonVideoRenderer* renderer = static_cast<CarbonVideoRenderer*>(data);
+ if (renderer != NULL) {
+ if (!renderer->DrawFrame()) {
+ LOG(LS_ERROR) << "Failed to draw frame.";
+ }
+ }
+ return status;
+}
+
+bool CarbonVideoRenderer::DrawFrame() {
+ // Grab the image lock to make sure it is not changed why we'll draw it.
+ rtc::CritScope cs(&image_crit_);
+
+ if (image_.get() == NULL) {
+ // Nothing to draw, just return.
+ return true;
+ }
+ int width = image_width_;
+ int height = image_height_;
+ CGDataProviderRef provider =
+ CGDataProviderCreateWithData(NULL, image_.get(), width * height * 4,
+ NULL);
+ CGColorSpaceRef color_space_ref = CGColorSpaceCreateDeviceRGB();
+ CGBitmapInfo bitmap_info = kCGBitmapByteOrderDefault;
+ CGColorRenderingIntent rendering_intent = kCGRenderingIntentDefault;
+ CGImageRef image_ref = CGImageCreate(width, height, 8, 32, width * 4,
+ color_space_ref, bitmap_info, provider,
+ NULL, false, rendering_intent);
+ CGDataProviderRelease(provider);
+
+ if (image_ref == NULL) {
+ return false;
+ }
+ CGContextRef context;
+ SetPortWindowPort(window_ref_);
+ if (QDBeginCGContext(GetWindowPort(window_ref_), &context) != noErr) {
+ CGImageRelease(image_ref);
+ return false;
+ }
+ Rect window_bounds;
+ GetWindowPortBounds(window_ref_, &window_bounds);
+
+ // Anchor the image to the top left corner.
+ int x = 0;
+ int y = window_bounds.bottom - CGImageGetHeight(image_ref);
+ CGRect dst_rect = CGRectMake(x, y, CGImageGetWidth(image_ref),
+ CGImageGetHeight(image_ref));
+ CGContextDrawImage(context, dst_rect, image_ref);
+ CGContextFlush(context);
+ QDEndCGContext(GetWindowPort(window_ref_), &context);
+ CGImageRelease(image_ref);
+ return true;
+}
+
+bool CarbonVideoRenderer::SetSize(int width, int height, int reserved) {
+ if (width != image_width_ || height != image_height_) {
+ // Grab the image lock while changing its size.
+ rtc::CritScope cs(&image_crit_);
+ image_width_ = width;
+ image_height_ = height;
+ image_.reset(new uint8_t[width * height * 4]);
+ memset(image_.get(), 255, width * height * 4);
+ }
+ return true;
+}
+
+bool CarbonVideoRenderer::RenderFrame(const VideoFrame* video_frame) {
+ if (!video_frame) {
+ return false;
+ }
+ {
+ const VideoFrame* frame = video_frame->GetCopyWithRotationApplied();
+
+ if (!SetSize(frame->GetWidth(), frame->GetHeight(), 0)) {
+ return false;
+ }
+
+ // Grab the image lock so we are not trashing up the image being drawn.
+ rtc::CritScope cs(&image_crit_);
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR,
+ image_.get(),
+ frame->GetWidth() * frame->GetHeight() * 4,
+ frame->GetWidth() * 4);
+ }
+
+ // Trigger a repaint event for the whole window.
+ Rect bounds;
+ InvalWindowRect(window_ref_, GetWindowPortBounds(window_ref_, &bounds));
+ return true;
+}
+
+bool CarbonVideoRenderer::Initialize() {
+ OSStatus err;
+ WindowAttributes attributes =
+ kWindowStandardDocumentAttributes |
+ kWindowLiveResizeAttribute |
+ kWindowFrameworkScaledAttribute |
+ kWindowStandardHandlerAttribute;
+
+ struct Rect bounds;
+ bounds.top = y_;
+ bounds.bottom = 480;
+ bounds.left = x_;
+ bounds.right = 640;
+ err = CreateNewWindow(kDocumentWindowClass, attributes,
+ &bounds, &window_ref_);
+ if (!window_ref_ || err != noErr) {
+ LOG(LS_ERROR) << "CreateNewWindow failed, error code: " << err;
+ return false;
+ }
+ static const EventTypeSpec event_spec = {
+ kEventClassWindow,
+ kEventWindowDrawContent
+ };
+
+ err = InstallWindowEventHandler(
+ window_ref_,
+ NewEventHandlerUPP(CarbonVideoRenderer::DrawEventHandler),
+ GetEventTypeCount(event_spec),
+ &event_spec,
+ this,
+ NULL);
+ if (err != noErr) {
+ LOG(LS_ERROR) << "Failed to install event handler, error code: " << err;
+ return false;
+ }
+ SelectWindow(window_ref_);
+ ShowWindow(window_ref_);
+ return true;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/carbonvideorenderer.h b/talk/media/devices/carbonvideorenderer.h
new file mode 100644
index 0000000000..52c974060c
--- /dev/null
+++ b/talk/media/devices/carbonvideorenderer.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2011 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Definition of class CarbonVideoRenderer that implements the abstract class
+// cricket::VideoRenderer via Carbon.
+
+#ifndef TALK_MEDIA_DEVICES_CARBONVIDEORENDERER_H_
+#define TALK_MEDIA_DEVICES_CARBONVIDEORENDERER_H_
+
+#include <Carbon/Carbon.h>
+
+#include "talk/media/base/videorenderer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace cricket {
+
+class CarbonVideoRenderer : public VideoRenderer {
+ public:
+ CarbonVideoRenderer(int x, int y);
+ virtual ~CarbonVideoRenderer();
+
+ // Implementation of pure virtual methods of VideoRenderer.
+ // These two methods may be executed in different threads.
+ // SetSize is called before RenderFrame.
+ virtual bool SetSize(int width, int height, int reserved);
+ virtual bool RenderFrame(const VideoFrame* frame);
+
+ // Needs to be called on the main thread.
+ bool Initialize();
+
+ private:
+ bool DrawFrame();
+
+ static OSStatus DrawEventHandler(EventHandlerCallRef handler,
+ EventRef event,
+ void* data);
+ rtc::scoped_ptr<uint8_t[]> image_;
+ rtc::CriticalSection image_crit_;
+ int image_width_;
+ int image_height_;
+ int x_;
+ int y_;
+ CGImageRef image_ref_;
+ WindowRef window_ref_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_CARBONVIDEORENDERER_H_
diff --git a/talk/media/devices/deviceinfo.h b/talk/media/devices/deviceinfo.h
new file mode 100644
index 0000000000..86382f6145
--- /dev/null
+++ b/talk/media/devices/deviceinfo.h
@@ -0,0 +1,42 @@
+/*
+ * libjingle
+ * Copyright 2012 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_DEVICEINFO_H_
+#define TALK_MEDIA_DEVICES_DEVICEINFO_H_
+
+#include <string>
+
+#include "talk/media/devices/devicemanager.h"
+
+namespace cricket {
+
+bool GetUsbId(const Device& device, std::string* usb_id);
+bool GetUsbVersion(const Device& device, std::string* usb_version);
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_DEVICEINFO_H_
diff --git a/talk/media/devices/devicemanager.cc b/talk/media/devices/devicemanager.cc
new file mode 100644
index 0000000000..1d7ac5baf1
--- /dev/null
+++ b/talk/media/devices/devicemanager.cc
@@ -0,0 +1,371 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/devicemanager.h"
+
+#include "talk/media/base/mediacommon.h"
+#include "talk/media/base/videocapturerfactory.h"
+#include "talk/media/devices/deviceinfo.h"
+#include "talk/media/devices/filevideocapturer.h"
+#include "talk/media/devices/yuvframescapturer.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/windowpicker.h"
+#include "webrtc/base/windowpickerfactory.h"
+
+#ifdef HAVE_WEBRTC_VIDEO
+#include "talk/media/webrtc/webrtcvideocapturerfactory.h"
+#endif // HAVE_WEBRTC_VIDEO
+
+namespace {
+
+bool StringMatchWithWildcard(
+ const std::pair<const std::basic_string<char>, cricket::VideoFormat> key,
+ const std::string& val) {
+ return rtc::string_match(val.c_str(), key.first.c_str());
+}
+
+} // namespace
+
+namespace cricket {
+
+// Initialize to empty string.
+const char DeviceManagerInterface::kDefaultDeviceName[] = "";
+
+DeviceManager::DeviceManager()
+ : initialized_(false),
+ window_picker_(rtc::WindowPickerFactory::CreateWindowPicker()) {
+#ifdef HAVE_WEBRTC_VIDEO
+ SetVideoDeviceCapturerFactory(new WebRtcVideoDeviceCapturerFactory());
+#endif // HAVE_WEBRTC_VIDEO
+}
+
+DeviceManager::~DeviceManager() {
+ if (initialized()) {
+ Terminate();
+ }
+}
+
+bool DeviceManager::Init() {
+ if (!initialized()) {
+ if (!watcher()->Start()) {
+ return false;
+ }
+ set_initialized(true);
+ }
+ return true;
+}
+
+void DeviceManager::Terminate() {
+ if (initialized()) {
+ watcher()->Stop();
+ set_initialized(false);
+ }
+}
+
+int DeviceManager::GetCapabilities() {
+ std::vector<Device> devices;
+ int caps = VIDEO_RECV;
+ if (GetAudioInputDevices(&devices) && !devices.empty()) {
+ caps |= AUDIO_SEND;
+ }
+ if (GetAudioOutputDevices(&devices) && !devices.empty()) {
+ caps |= AUDIO_RECV;
+ }
+ if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
+ caps |= VIDEO_SEND;
+ }
+ return caps;
+}
+
+bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) {
+ return GetAudioDevices(true, devices);
+}
+
+bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) {
+ return GetAudioDevices(false, devices);
+}
+
+bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(true, name, out);
+}
+
+bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(false, name, out);
+}
+
+bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
+ devices->clear();
+#if defined(ANDROID) || defined(IOS)
+ // On Android and iOS, we treat the camera(s) as a single device. Even if
+ // there are multiple cameras, that's abstracted away at a higher level.
+ Device dev("camera", "1"); // name and ID
+ devices->push_back(dev);
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool DeviceManager::GetVideoCaptureDevice(const std::string& name,
+ Device* out) {
+ // If the name is empty, return the default device.
+ if (name.empty() || name == kDefaultDeviceName) {
+ return GetDefaultVideoCaptureDevice(out);
+ }
+
+ std::vector<Device> devices;
+ if (!GetVideoCaptureDevices(&devices)) {
+ return false;
+ }
+
+ for (std::vector<Device>::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ if (name == it->name) {
+ *out = *it;
+ return true;
+ }
+ }
+
+ // If |name| is a valid name for a file or yuvframedevice,
+ // return a fake video capturer device.
+ if (GetFakeVideoCaptureDevice(name, out)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool DeviceManager::GetFakeVideoCaptureDevice(const std::string& name,
+ Device* out) const {
+ if (rtc::Filesystem::IsFile(name)) {
+ *out = FileVideoCapturer::CreateFileVideoCapturerDevice(name);
+ return true;
+ }
+
+ if (name == YuvFramesCapturer::kYuvFrameDeviceName) {
+ *out = YuvFramesCapturer::CreateYuvFramesCapturerDevice();
+ return true;
+ }
+
+ return false;
+}
+
+void DeviceManager::SetVideoCaptureDeviceMaxFormat(
+ const std::string& usb_id,
+ const VideoFormat& max_format) {
+ max_formats_[usb_id] = max_format;
+}
+
+void DeviceManager::ClearVideoCaptureDeviceMaxFormat(
+ const std::string& usb_id) {
+ max_formats_.erase(usb_id);
+}
+
+VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const {
+ VideoCapturer* capturer = MaybeConstructFakeVideoCapturer(device);
+ if (capturer) {
+ return capturer;
+ }
+
+ if (!video_device_capturer_factory_) {
+ LOG(LS_ERROR) << "No video capturer factory for devices.";
+ return NULL;
+ }
+ capturer = video_device_capturer_factory_->Create(device);
+ if (!capturer) {
+ return NULL;
+ }
+ LOG(LS_INFO) << "Created VideoCapturer for " << device.name;
+ VideoFormat video_format;
+ bool has_max = GetMaxFormat(device, &video_format);
+ capturer->set_enable_camera_list(has_max);
+ if (has_max) {
+ capturer->ConstrainSupportedFormats(video_format);
+ }
+ return capturer;
+}
+
+VideoCapturer* DeviceManager::MaybeConstructFakeVideoCapturer(
+ const Device& device) const {
+ // TODO(hellner): Throw out the creation of a file video capturer once the
+ // refactoring is completed.
+ if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
+ FileVideoCapturer* capturer = new FileVideoCapturer;
+ if (!capturer->Init(device)) {
+ delete capturer;
+ return NULL;
+ }
+ LOG(LS_INFO) << "Created file video capturer " << device.name;
+ capturer->set_repeat(FileVideoCapturer::kForever);
+ return capturer;
+ }
+
+ if (YuvFramesCapturer::IsYuvFramesCapturerDevice(device)) {
+ YuvFramesCapturer* capturer = new YuvFramesCapturer();
+ capturer->Init();
+ return capturer;
+ }
+ return NULL;
+}
+
+bool DeviceManager::GetWindows(
+ std::vector<rtc::WindowDescription>* descriptions) {
+ if (!window_picker_) {
+ return false;
+ }
+ return window_picker_->GetWindowList(descriptions);
+}
+
+bool DeviceManager::GetDesktops(
+ std::vector<rtc::DesktopDescription>* descriptions) {
+ if (!window_picker_) {
+ return false;
+ }
+ return window_picker_->GetDesktopList(descriptions);
+}
+
+VideoCapturer* DeviceManager::CreateScreenCapturer(
+ const ScreencastId& screenid) const {
+ if (!screen_capturer_factory_) {
+ LOG(LS_ERROR) << "No video capturer factory for screens.";
+ return NULL;
+ }
+ return screen_capturer_factory_->Create(screenid);
+}
+
+bool DeviceManager::GetAudioDevices(bool input,
+ std::vector<Device>* devs) {
+ devs->clear();
+#if defined(ANDROID)
+ // Under Android, 0 is always required for the playout device and 0 is the
+ // default for the recording device.
+ devs->push_back(Device("default-device", 0));
+ return true;
+#else
+ // Other platforms either have their own derived class implementation
+ // (desktop) or don't use device manager for audio devices (iOS).
+ return false;
+#endif
+}
+
+bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name,
+ Device* out) {
+ // If the name is empty, return the default device id.
+ if (name.empty() || name == kDefaultDeviceName) {
+ *out = Device(name, -1);
+ return true;
+ }
+
+ std::vector<Device> devices;
+ bool ret = is_input ? GetAudioInputDevices(&devices) :
+ GetAudioOutputDevices(&devices);
+ if (ret) {
+ ret = false;
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (devices[i].name == name) {
+ *out = devices[i];
+ ret = true;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
+ bool ret = false;
+ // We just return the first device.
+ std::vector<Device> devices;
+ ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
+ if (ret) {
+ *device = devices[0];
+ }
+ return ret;
+}
+
+bool DeviceManager::IsInWhitelist(const std::string& key,
+ VideoFormat* video_format) const {
+ std::map<std::string, VideoFormat>::const_iterator found =
+ std::search_n(max_formats_.begin(), max_formats_.end(), 1, key,
+ StringMatchWithWildcard);
+ if (found == max_formats_.end()) {
+ return false;
+ }
+ *video_format = found->second;
+ return true;
+}
+
+bool DeviceManager::GetMaxFormat(const Device& device,
+ VideoFormat* video_format) const {
+ // Match USB ID if available. Failing that, match device name.
+ std::string usb_id;
+ if (GetUsbId(device, &usb_id) && IsInWhitelist(usb_id, video_format)) {
+ return true;
+ }
+ return IsInWhitelist(device.name, video_format);
+}
+
+bool DeviceManager::ShouldDeviceBeIgnored(const std::string& device_name,
+ const char* const exclusion_list[]) {
+ // If exclusion_list is empty return directly.
+ if (!exclusion_list)
+ return false;
+
+ int i = 0;
+ while (exclusion_list[i]) {
+ if (strnicmp(device_name.c_str(), exclusion_list[i],
+ strlen(exclusion_list[i])) == 0) {
+ LOG(LS_INFO) << "Ignoring device " << device_name;
+ return true;
+ }
+ ++i;
+ }
+ return false;
+}
+
+bool DeviceManager::FilterDevices(std::vector<Device>* devices,
+ const char* const exclusion_list[]) {
+ if (!devices) {
+ return false;
+ }
+
+ for (std::vector<Device>::iterator it = devices->begin();
+ it != devices->end(); ) {
+ if (ShouldDeviceBeIgnored(it->name, exclusion_list)) {
+ it = devices->erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return true;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/devicemanager.h b/talk/media/devices/devicemanager.h
new file mode 100644
index 0000000000..982c679531
--- /dev/null
+++ b/talk/media/devices/devicemanager.h
@@ -0,0 +1,211 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_DEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_DEVICEMANAGER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "talk/media/base/device.h"
+#include "talk/media/base/screencastid.h"
+#include "talk/media/base/videocapturerfactory.h"
+#include "talk/media/base/videocommon.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/window.h"
+
+namespace rtc {
+
+class DesktopDescription;
+class WindowDescription;
+class WindowPicker;
+
+}
+namespace cricket {
+
+class VideoCapturer;
+
+// DeviceManagerInterface - interface to manage the audio and
+// video devices on the system.
+class DeviceManagerInterface {
+ public:
+ virtual ~DeviceManagerInterface() { }
+
+ // Initialization
+ virtual bool Init() = 0;
+ virtual void Terminate() = 0;
+
+ // Capabilities
+ virtual int GetCapabilities() = 0;
+
+ // Device enumeration
+ virtual bool GetAudioInputDevices(std::vector<Device>* devices) = 0;
+ virtual bool GetAudioOutputDevices(std::vector<Device>* devices) = 0;
+
+ virtual bool GetAudioInputDevice(const std::string& name, Device* out) = 0;
+ virtual bool GetAudioOutputDevice(const std::string& name, Device* out) = 0;
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs) = 0;
+ virtual bool GetVideoCaptureDevice(const std::string& name, Device* out) = 0;
+
+ // If the device manager needs to create video capturers, here is
+ // how to control which video capturers are created. These take
+ // ownership of the factories.
+ virtual void SetVideoDeviceCapturerFactory(
+ VideoDeviceCapturerFactory* video_device_capturer_factory) = 0;
+ virtual void SetScreenCapturerFactory(
+ ScreenCapturerFactory* screen_capturer_factory) = 0;
+
+ // Caps the capture format according to max format for capturers created
+ // by CreateVideoCapturer(). See ConstrainSupportedFormats() in
+ // videocapturer.h for more detail.
+ // Note that once a VideoCapturer has been created, calling this API will
+ // not affect it.
+ virtual void SetVideoCaptureDeviceMaxFormat(
+ const std::string& usb_id,
+ const VideoFormat& max_format) = 0;
+ virtual void ClearVideoCaptureDeviceMaxFormat(const std::string& usb_id) = 0;
+
+ // Device creation
+ virtual VideoCapturer* CreateVideoCapturer(const Device& device) const = 0;
+
+ virtual bool GetWindows(
+ std::vector<rtc::WindowDescription>* descriptions) = 0;
+ virtual bool GetDesktops(
+ std::vector<rtc::DesktopDescription>* descriptions) = 0;
+ virtual VideoCapturer* CreateScreenCapturer(
+ const ScreencastId& screenid) const = 0;
+
+ sigslot::signal0<> SignalDevicesChange;
+
+ static const char kDefaultDeviceName[];
+};
+
+class DeviceWatcher {
+ public:
+ explicit DeviceWatcher(DeviceManagerInterface* dm) {}
+ virtual ~DeviceWatcher() {}
+ virtual bool Start() { return true; }
+ virtual void Stop() {}
+};
+
+class DeviceManagerFactory {
+ public:
+ static DeviceManagerInterface* Create();
+
+ private:
+ DeviceManagerFactory() {}
+};
+
+class DeviceManager : public DeviceManagerInterface {
+ public:
+ DeviceManager();
+ virtual ~DeviceManager();
+
+ // Initialization
+ virtual bool Init();
+ virtual void Terminate();
+
+ // Capabilities
+ virtual int GetCapabilities();
+
+ // Device enumeration
+ virtual bool GetAudioInputDevices(std::vector<Device>* devices);
+ virtual bool GetAudioOutputDevices(std::vector<Device>* devices);
+
+ virtual bool GetAudioInputDevice(const std::string& name, Device* out);
+ virtual bool GetAudioOutputDevice(const std::string& name, Device* out);
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+ virtual bool GetVideoCaptureDevice(const std::string& name, Device* out);
+
+ virtual void SetVideoDeviceCapturerFactory(
+ VideoDeviceCapturerFactory* video_device_capturer_factory) {
+ video_device_capturer_factory_.reset(video_device_capturer_factory);
+ }
+ virtual void SetScreenCapturerFactory(
+ ScreenCapturerFactory* screen_capturer_factory) {
+ screen_capturer_factory_.reset(screen_capturer_factory);
+ }
+
+
+ virtual void SetVideoCaptureDeviceMaxFormat(const std::string& usb_id,
+ const VideoFormat& max_format);
+ virtual void ClearVideoCaptureDeviceMaxFormat(const std::string& usb_id);
+
+ // TODO(pthatcher): Rename to CreateVideoDeviceCapturer.
+ virtual VideoCapturer* CreateVideoCapturer(const Device& device) const;
+
+ virtual bool GetWindows(
+ std::vector<rtc::WindowDescription>* descriptions);
+ virtual bool GetDesktops(
+ std::vector<rtc::DesktopDescription>* descriptions);
+ virtual VideoCapturer* CreateScreenCapturer(
+ const ScreencastId& screenid) const;
+
+ // The exclusion_list MUST be a NULL terminated list.
+ static bool FilterDevices(std::vector<Device>* devices,
+ const char* const exclusion_list[]);
+ bool initialized() const { return initialized_; }
+
+ protected:
+ virtual bool GetAudioDevices(bool input, std::vector<Device>* devs);
+ virtual bool GetAudioDevice(bool is_input, const std::string& name,
+ Device* out);
+ virtual bool GetDefaultVideoCaptureDevice(Device* device);
+ bool IsInWhitelist(const std::string& key, VideoFormat* video_format) const;
+ virtual bool GetMaxFormat(const Device& device,
+ VideoFormat* video_format) const;
+
+ void set_initialized(bool initialized) { initialized_ = initialized; }
+
+ void set_watcher(DeviceWatcher* watcher) { watcher_.reset(watcher); }
+ DeviceWatcher* watcher() { return watcher_.get(); }
+
+ private:
+ // The exclusion_list MUST be a NULL terminated list.
+ static bool ShouldDeviceBeIgnored(const std::string& device_name,
+ const char* const exclusion_list[]);
+ bool GetFakeVideoCaptureDevice(const std::string& name, Device* out) const;
+ VideoCapturer* MaybeConstructFakeVideoCapturer(const Device& device) const;
+
+ bool initialized_;
+ rtc::scoped_ptr<
+ VideoDeviceCapturerFactory> video_device_capturer_factory_;
+ rtc::scoped_ptr<
+ ScreenCapturerFactory> screen_capturer_factory_;
+ std::map<std::string, VideoFormat> max_formats_;
+ rtc::scoped_ptr<DeviceWatcher> watcher_;
+ rtc::scoped_ptr<rtc::WindowPicker> window_picker_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_DEVICEMANAGER_H_
diff --git a/talk/media/devices/devicemanager_unittest.cc b/talk/media/devices/devicemanager_unittest.cc
new file mode 100644
index 0000000000..f259c7d0d3
--- /dev/null
+++ b/talk/media/devices/devicemanager_unittest.cc
@@ -0,0 +1,470 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/devicemanager.h"
+
+#ifdef WIN32
+#include <objbase.h>
+#include "webrtc/base/win32.h"
+#endif
+#include <string>
+
+#include "talk/media/base/fakevideocapturer.h"
+#include "talk/media/base/screencastid.h"
+#include "talk/media/base/testutils.h"
+#include "talk/media/base/videocapturerfactory.h"
+#include "talk/media/devices/filevideocapturer.h"
+#include "talk/media/devices/v4llookup.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/windowpickerfactory.h"
+
+#ifdef LINUX
+// TODO(juberti): Figure out why this doesn't compile on Windows.
+#include "webrtc/base/fileutils_mock.h"
+#endif // LINUX
+
+using rtc::Pathname;
+using rtc::FileTimeType;
+using rtc::scoped_ptr;
+using cricket::Device;
+using cricket::DeviceManager;
+using cricket::DeviceManagerFactory;
+using cricket::DeviceManagerInterface;
+
+const cricket::VideoFormat kVgaFormat(640, 480,
+ cricket::VideoFormat::FpsToInterval(30),
+ cricket::FOURCC_I420);
+const cricket::VideoFormat kHdFormat(1280, 720,
+ cricket::VideoFormat::FpsToInterval(30),
+ cricket::FOURCC_I420);
+
+class FakeVideoDeviceCapturerFactory :
+ public cricket::VideoDeviceCapturerFactory {
+ public:
+ FakeVideoDeviceCapturerFactory() {}
+ virtual ~FakeVideoDeviceCapturerFactory() {}
+
+ virtual cricket::VideoCapturer* Create(const cricket::Device& device) {
+ return new cricket::FakeVideoCapturer;
+ }
+};
+
+class FakeScreenCapturerFactory : public cricket::ScreenCapturerFactory {
+ public:
+ FakeScreenCapturerFactory() {}
+ virtual ~FakeScreenCapturerFactory() {}
+
+ virtual cricket::VideoCapturer* Create(
+ const cricket::ScreencastId& screenid) {
+ return new cricket::FakeVideoCapturer;
+ }
+};
+
+class DeviceManagerTestFake : public testing::Test {
+ public:
+ virtual void SetUp() {
+ dm_.reset(DeviceManagerFactory::Create());
+ EXPECT_TRUE(dm_->Init());
+ DeviceManager* device_manager = static_cast<DeviceManager*>(dm_.get());
+ device_manager->SetVideoDeviceCapturerFactory(
+ new FakeVideoDeviceCapturerFactory());
+ device_manager->SetScreenCapturerFactory(
+ new FakeScreenCapturerFactory());
+ }
+
+ virtual void TearDown() {
+ dm_->Terminate();
+ }
+
+ protected:
+ scoped_ptr<DeviceManagerInterface> dm_;
+};
+
+
+// Test that we startup/shutdown properly.
+TEST(DeviceManagerTest, StartupShutdown) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ EXPECT_TRUE(dm->Init());
+ dm->Terminate();
+}
+
+// Test CoInitEx behavior
+#ifdef WIN32
+TEST(DeviceManagerTest, CoInitialize) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> devices;
+ // Ensure that calls to video device work if COM is not yet initialized.
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&devices));
+ dm->Terminate();
+ // Ensure that the ref count is correct.
+ EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
+ CoUninitialize();
+ // Ensure that Init works in COINIT_APARTMENTTHREADED setting.
+ EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
+ EXPECT_TRUE(dm->Init());
+ dm->Terminate();
+ CoUninitialize();
+ // Ensure that the ref count is correct.
+ EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
+ CoUninitialize();
+ // Ensure that Init works in COINIT_MULTITHREADED setting.
+ EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
+ EXPECT_TRUE(dm->Init());
+ dm->Terminate();
+ CoUninitialize();
+ // Ensure that the ref count is correct.
+ EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
+ CoUninitialize();
+}
+#endif
+
+// Test enumerating devices (although we may not find any).
+TEST(DeviceManagerTest, GetDevices) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> audio_ins, audio_outs, video_ins;
+ std::vector<cricket::Device> video_in_devs;
+ cricket::Device def_video;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins));
+ EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs));
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_in_devs));
+ EXPECT_EQ(video_ins.size(), video_in_devs.size());
+ // If we have any video devices, we should be able to pick a default.
+ EXPECT_TRUE(dm->GetVideoCaptureDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &def_video)
+ != video_ins.empty());
+}
+
+// Test that we return correct ids for default and bogus devices.
+TEST(DeviceManagerTest, GetAudioDeviceIds) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ Device device;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetAudioInputDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+ EXPECT_EQ("-1", device.id);
+ EXPECT_TRUE(dm->GetAudioOutputDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+ EXPECT_EQ("-1", device.id);
+ EXPECT_FALSE(dm->GetAudioInputDevice("_NOT A REAL DEVICE_", &device));
+ EXPECT_FALSE(dm->GetAudioOutputDevice("_NOT A REAL DEVICE_", &device));
+}
+
+// Test that we get the video capture device by name properly.
+TEST(DeviceManagerTest, GetVideoDeviceIds) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ Device device;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_FALSE(dm->GetVideoCaptureDevice("_NOT A REAL DEVICE_", &device));
+ std::vector<Device> video_ins;
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ if (!video_ins.empty()) {
+ // Get the default device with the parameter kDefaultDeviceName.
+ EXPECT_TRUE(dm->GetVideoCaptureDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+
+ // Get the first device with the parameter video_ins[0].name.
+ EXPECT_TRUE(dm->GetVideoCaptureDevice(video_ins[0].name, &device));
+ EXPECT_EQ(device.name, video_ins[0].name);
+ EXPECT_EQ(device.id, video_ins[0].id);
+ }
+}
+
+TEST(DeviceManagerTest, GetVideoDeviceIds_File) {
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ EXPECT_TRUE(dm->Init());
+ Device device;
+ const std::string test_file =
+ cricket::GetTestFilePath("captured-320x240-2s-48.frames");
+ EXPECT_TRUE(dm->GetVideoCaptureDevice(test_file, &device));
+ EXPECT_TRUE(cricket::FileVideoCapturer::IsFileVideoCapturerDevice(device));
+}
+
+TEST(DeviceManagerTest, VerifyDevicesListsAreCleared) {
+ const std::string imaginary("_NOT A REAL DEVICE_");
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> audio_ins, audio_outs, video_ins;
+ audio_ins.push_back(Device(imaginary, imaginary));
+ audio_outs.push_back(Device(imaginary, imaginary));
+ video_ins.push_back(Device(imaginary, imaginary));
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins));
+ EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs));
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ for (size_t i = 0; i < audio_ins.size(); ++i) {
+ EXPECT_NE(imaginary, audio_ins[i].name);
+ }
+ for (size_t i = 0; i < audio_outs.size(); ++i) {
+ EXPECT_NE(imaginary, audio_outs[i].name);
+ }
+ for (size_t i = 0; i < video_ins.size(); ++i) {
+ EXPECT_NE(imaginary, video_ins[i].name);
+ }
+}
+
+static bool CompareDeviceList(std::vector<Device>& devices,
+ const char* const device_list[], int list_size) {
+ if (list_size != static_cast<int>(devices.size())) {
+ return false;
+ }
+ for (int i = 0; i < list_size; ++i) {
+ if (devices[i].name.compare(device_list[i]) != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST(DeviceManagerTest, VerifyFilterDevices) {
+ static const char* const kTotalDevicesName[] = {
+ "Google Camera Adapters are tons of fun.",
+ "device1",
+ "device2",
+ "device3",
+ "device4",
+ "device5",
+ "Google Camera Adapter 0",
+ "Google Camera Adapter 1",
+ };
+ static const char* const kFilteredDevicesName[] = {
+ "device2",
+ "device4",
+ "Google Camera Adapter",
+ NULL,
+ };
+ static const char* const kDevicesName[] = {
+ "device1",
+ "device3",
+ "device5",
+ };
+ std::vector<Device> devices;
+ for (int i = 0; i < ARRAY_SIZE(kTotalDevicesName); ++i) {
+ devices.push_back(Device(kTotalDevicesName[i], i));
+ }
+ EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName,
+ ARRAY_SIZE(kTotalDevicesName)));
+ // Return false if given NULL as the exclusion list.
+ EXPECT_TRUE(DeviceManager::FilterDevices(&devices, NULL));
+ // The devices should not change.
+ EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName,
+ ARRAY_SIZE(kTotalDevicesName)));
+ EXPECT_TRUE(DeviceManager::FilterDevices(&devices, kFilteredDevicesName));
+ EXPECT_TRUE(CompareDeviceList(devices, kDevicesName,
+ ARRAY_SIZE(kDevicesName)));
+}
+
+#ifdef LINUX
+class FakeV4LLookup : public cricket::V4LLookup {
+ public:
+ explicit FakeV4LLookup(std::vector<std::string> device_paths)
+ : device_paths_(device_paths) {}
+
+ protected:
+ bool CheckIsV4L2Device(const std::string& device) {
+ return std::find(device_paths_.begin(), device_paths_.end(), device)
+ != device_paths_.end();
+ }
+
+ private:
+ std::vector<std::string> device_paths_;
+};
+
+TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_6) {
+ std::vector<std::string> devices;
+ devices.push_back("/dev/video0");
+ devices.push_back("/dev/video5");
+ cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
+
+ std::vector<rtc::FakeFileSystem::File> files;
+ files.push_back(rtc::FakeFileSystem::File("/dev/video0", ""));
+ files.push_back(rtc::FakeFileSystem::File("/dev/video5", ""));
+ files.push_back(rtc::FakeFileSystem::File(
+ "/sys/class/video4linux/video0/name", "Video Device 1"));
+ files.push_back(rtc::FakeFileSystem::File(
+ "/sys/class/video4linux/video1/model", "Bad Device"));
+ files.push_back(
+ rtc::FakeFileSystem::File("/sys/class/video4linux/video5/model",
+ "Video Device 2"));
+ rtc::FilesystemScope fs(new rtc::FakeFileSystem(files));
+
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> video_ins;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ EXPECT_EQ(2u, video_ins.size());
+ EXPECT_EQ("Video Device 1", video_ins.at(0).name);
+ EXPECT_EQ("Video Device 2", video_ins.at(1).name);
+}
+
+TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_4) {
+ std::vector<std::string> devices;
+ devices.push_back("/dev/video0");
+ devices.push_back("/dev/video5");
+ cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
+
+ std::vector<rtc::FakeFileSystem::File> files;
+ files.push_back(rtc::FakeFileSystem::File("/dev/video0", ""));
+ files.push_back(rtc::FakeFileSystem::File("/dev/video5", ""));
+ files.push_back(rtc::FakeFileSystem::File(
+ "/proc/video/dev/video0",
+ "param1: value1\nname: Video Device 1\n param2: value2\n"));
+ files.push_back(rtc::FakeFileSystem::File(
+ "/proc/video/dev/video1",
+ "param1: value1\nname: Bad Device\n param2: value2\n"));
+ files.push_back(rtc::FakeFileSystem::File(
+ "/proc/video/dev/video5",
+ "param1: value1\nname: Video Device 2\n param2: value2\n"));
+ rtc::FilesystemScope fs(new rtc::FakeFileSystem(files));
+
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> video_ins;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ EXPECT_EQ(2u, video_ins.size());
+ EXPECT_EQ("Video Device 1", video_ins.at(0).name);
+ EXPECT_EQ("Video Device 2", video_ins.at(1).name);
+}
+
+TEST(DeviceManagerTest, GetVideoCaptureDevices_KUnknown) {
+ std::vector<std::string> devices;
+ devices.push_back("/dev/video0");
+ devices.push_back("/dev/video5");
+ cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
+
+ std::vector<rtc::FakeFileSystem::File> files;
+ files.push_back(rtc::FakeFileSystem::File("/dev/video0", ""));
+ files.push_back(rtc::FakeFileSystem::File("/dev/video1", ""));
+ files.push_back(rtc::FakeFileSystem::File("/dev/video5", ""));
+ rtc::FilesystemScope fs(new rtc::FakeFileSystem(files));
+
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ std::vector<Device> video_ins;
+ EXPECT_TRUE(dm->Init());
+ EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
+ EXPECT_EQ(2u, video_ins.size());
+ EXPECT_EQ("/dev/video0", video_ins.at(0).name);
+ EXPECT_EQ("/dev/video5", video_ins.at(1).name);
+}
+#endif // LINUX
+
+// TODO(noahric): These are flaky on windows on headless machines.
+#ifndef WIN32
+TEST(DeviceManagerTest, GetWindows) {
+ if (!rtc::WindowPickerFactory::IsSupported()) {
+ LOG(LS_INFO) << "skipping test: window capturing is not supported with "
+ << "current configuration.";
+ return;
+ }
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ dm->SetScreenCapturerFactory(new FakeScreenCapturerFactory());
+ std::vector<rtc::WindowDescription> descriptions;
+ EXPECT_TRUE(dm->Init());
+ if (!dm->GetWindows(&descriptions) || descriptions.empty()) {
+ LOG(LS_INFO) << "skipping test: window capturing. Does not have any "
+ << "windows to capture.";
+ return;
+ }
+ scoped_ptr<cricket::VideoCapturer> capturer(dm->CreateScreenCapturer(
+ cricket::ScreencastId(descriptions.front().id())));
+ EXPECT_FALSE(capturer.get() == NULL);
+ // TODO(hellner): creating a window capturer and immediately deleting it
+ // results in "Continuous Build and Test Mainline - Mac opt" failure (crash).
+ // Remove the following line as soon as this has been resolved.
+ rtc::Thread::Current()->ProcessMessages(1);
+}
+
+TEST(DeviceManagerTest, GetDesktops) {
+ if (!rtc::WindowPickerFactory::IsSupported()) {
+ LOG(LS_INFO) << "skipping test: desktop capturing is not supported with "
+ << "current configuration.";
+ return;
+ }
+ scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
+ dm->SetScreenCapturerFactory(new FakeScreenCapturerFactory());
+ std::vector<rtc::DesktopDescription> descriptions;
+ EXPECT_TRUE(dm->Init());
+ if (!dm->GetDesktops(&descriptions) || descriptions.empty()) {
+ LOG(LS_INFO) << "skipping test: desktop capturing. Does not have any "
+ << "desktops to capture.";
+ return;
+ }
+ scoped_ptr<cricket::VideoCapturer> capturer(dm->CreateScreenCapturer(
+ cricket::ScreencastId(descriptions.front().id())));
+ EXPECT_FALSE(capturer.get() == NULL);
+}
+#endif // !WIN32
+
+TEST_F(DeviceManagerTestFake, CaptureConstraintsWhitelisted) {
+ const Device device("white", "white_id");
+ dm_->SetVideoCaptureDeviceMaxFormat(device.name, kHdFormat);
+ scoped_ptr<cricket::VideoCapturer> capturer(
+ dm_->CreateVideoCapturer(device));
+ cricket::VideoFormat best_format;
+ capturer->set_enable_camera_list(true);
+ EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format));
+ EXPECT_EQ(kHdFormat, best_format);
+}
+
+TEST_F(DeviceManagerTestFake, CaptureConstraintsNotWhitelisted) {
+ const Device device("regular", "regular_id");
+ scoped_ptr<cricket::VideoCapturer> capturer(
+ dm_->CreateVideoCapturer(device));
+ cricket::VideoFormat best_format;
+ capturer->set_enable_camera_list(true);
+ EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format));
+ EXPECT_EQ(kHdFormat, best_format);
+}
+
+TEST_F(DeviceManagerTestFake, CaptureConstraintsUnWhitelisted) {
+ const Device device("un_white", "un_white_id");
+ dm_->SetVideoCaptureDeviceMaxFormat(device.name, kHdFormat);
+ dm_->ClearVideoCaptureDeviceMaxFormat(device.name);
+ scoped_ptr<cricket::VideoCapturer> capturer(
+ dm_->CreateVideoCapturer(device));
+ cricket::VideoFormat best_format;
+ capturer->set_enable_camera_list(true);
+ EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format));
+ EXPECT_EQ(kHdFormat, best_format);
+}
+
+TEST_F(DeviceManagerTestFake, CaptureConstraintsWildcard) {
+ const Device device("any_device", "any_device");
+ dm_->SetVideoCaptureDeviceMaxFormat("*", kHdFormat);
+ scoped_ptr<cricket::VideoCapturer> capturer(
+ dm_->CreateVideoCapturer(device));
+ cricket::VideoFormat best_format;
+ capturer->set_enable_camera_list(true);
+ EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format));
+ EXPECT_EQ(kHdFormat, best_format);
+}
diff --git a/talk/media/devices/dummydevicemanager.cc b/talk/media/devices/dummydevicemanager.cc
new file mode 100644
index 0000000000..736258b752
--- /dev/null
+++ b/talk/media/devices/dummydevicemanager.cc
@@ -0,0 +1,38 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/dummydevicemanager.h"
+
+namespace cricket {
+
+const char DeviceManagerInterface::kDefaultDeviceName[] = "";
+
+DeviceManagerInterface* DeviceManagerFactory::Create() {
+ return new DummyDeviceManager();
+}
+
+}; // namespace cricket
diff --git a/talk/media/devices/dummydevicemanager.h b/talk/media/devices/dummydevicemanager.h
new file mode 100644
index 0000000000..7da81853c7
--- /dev/null
+++ b/talk/media/devices/dummydevicemanager.h
@@ -0,0 +1,51 @@
+/*
+ * libjingle
+ * Copyright 2011 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_DUMMYDEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_DUMMYDEVICEMANAGER_H_
+
+#include <vector>
+
+#include "talk/media/base/mediacommon.h"
+#include "talk/media/devices/fakedevicemanager.h"
+
+namespace cricket {
+
+class DummyDeviceManager : public FakeDeviceManager {
+ public:
+ DummyDeviceManager() {
+ std::vector<std::string> devices;
+ devices.push_back(DeviceManagerInterface::kDefaultDeviceName);
+ SetAudioInputDevices(devices);
+ SetAudioOutputDevices(devices);
+ SetVideoCaptureDevices(devices);
+ }
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_DUMMYDEVICEMANAGER_H_
diff --git a/talk/media/devices/dummydevicemanager_unittest.cc b/talk/media/devices/dummydevicemanager_unittest.cc
new file mode 100644
index 0000000000..9f4eb41b53
--- /dev/null
+++ b/talk/media/devices/dummydevicemanager_unittest.cc
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/dummydevicemanager.h"
+#include "webrtc/base/gunit.h"
+
+using cricket::Device;
+using cricket::DummyDeviceManager;
+
+// Test that we startup/shutdown properly.
+TEST(DummyDeviceManagerTest, StartupShutdown) {
+ DummyDeviceManager dm;
+ EXPECT_TRUE(dm.Init());
+ dm.Terminate();
+}
+
+// Test enumerating capabilities.
+TEST(DummyDeviceManagerTest, GetCapabilities) {
+ DummyDeviceManager dm;
+ int capabilities = dm.GetCapabilities();
+ EXPECT_EQ((cricket::AUDIO_SEND | cricket::AUDIO_RECV |
+ cricket::VIDEO_SEND | cricket::VIDEO_RECV), capabilities);
+}
+
+// Test enumerating devices.
+TEST(DummyDeviceManagerTest, GetDevices) {
+ DummyDeviceManager dm;
+ EXPECT_TRUE(dm.Init());
+ std::vector<Device> audio_ins, audio_outs, video_ins;
+ EXPECT_TRUE(dm.GetAudioInputDevices(&audio_ins));
+ EXPECT_TRUE(dm.GetAudioOutputDevices(&audio_outs));
+ EXPECT_TRUE(dm.GetVideoCaptureDevices(&video_ins));
+}
+
+// Test that we return correct ids for default and bogus devices.
+TEST(DummyDeviceManagerTest, GetAudioDeviceIds) {
+ DummyDeviceManager dm;
+ Device device;
+ EXPECT_TRUE(dm.Init());
+ EXPECT_TRUE(dm.GetAudioInputDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+ EXPECT_EQ("-1", device.id);
+ EXPECT_TRUE(dm.GetAudioOutputDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+ EXPECT_EQ("-1", device.id);
+ EXPECT_FALSE(dm.GetAudioInputDevice("_NOT A REAL DEVICE_", &device));
+ EXPECT_FALSE(dm.GetAudioOutputDevice("_NOT A REAL DEVICE_", &device));
+}
+
+// Test that we get the video capture device by name properly.
+TEST(DummyDeviceManagerTest, GetVideoDeviceIds) {
+ DummyDeviceManager dm;
+ Device device;
+ EXPECT_TRUE(dm.Init());
+ EXPECT_FALSE(dm.GetVideoCaptureDevice("_NOT A REAL DEVICE_", &device));
+ EXPECT_TRUE(dm.GetVideoCaptureDevice(
+ cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
+}
+
+TEST(DummyDeviceManagerTest, VerifyDevicesListsAreCleared) {
+ const std::string imaginary("_NOT A REAL DEVICE_");
+ DummyDeviceManager dm;
+ std::vector<Device> audio_ins, audio_outs, video_ins;
+ audio_ins.push_back(Device(imaginary, imaginary));
+ audio_outs.push_back(Device(imaginary, imaginary));
+ video_ins.push_back(Device(imaginary, imaginary));
+ EXPECT_TRUE(dm.Init());
+ EXPECT_TRUE(dm.GetAudioInputDevices(&audio_ins));
+ EXPECT_TRUE(dm.GetAudioOutputDevices(&audio_outs));
+ EXPECT_TRUE(dm.GetVideoCaptureDevices(&video_ins));
+ for (size_t i = 0; i < audio_ins.size(); ++i) {
+ EXPECT_NE(imaginary, audio_ins[i].name);
+ }
+ for (size_t i = 0; i < audio_outs.size(); ++i) {
+ EXPECT_NE(imaginary, audio_outs[i].name);
+ }
+ for (size_t i = 0; i < video_ins.size(); ++i) {
+ EXPECT_NE(imaginary, video_ins[i].name);
+ }
+}
diff --git a/talk/media/devices/fakedevicemanager.h b/talk/media/devices/fakedevicemanager.h
new file mode 100644
index 0000000000..a4b2b86e44
--- /dev/null
+++ b/talk/media/devices/fakedevicemanager.h
@@ -0,0 +1,239 @@
+/*
+ * libjingle
+ * Copyright 2008 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_FAKEDEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_FAKEDEVICEMANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/base/fakevideocapturer.h"
+#include "talk/media/base/mediacommon.h"
+#include "talk/media/devices/devicemanager.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/window.h"
+#include "webrtc/base/windowpicker.h"
+
+namespace cricket {
+
+class FakeDeviceManager : public DeviceManagerInterface {
+ public:
+ FakeDeviceManager() {}
+ virtual bool Init() {
+ return true;
+ }
+ virtual void Terminate() {
+ }
+ virtual int GetCapabilities() {
+ std::vector<Device> devices;
+ int caps = VIDEO_RECV;
+ if (!input_devices_.empty()) {
+ caps |= AUDIO_SEND;
+ }
+ if (!output_devices_.empty()) {
+ caps |= AUDIO_RECV;
+ }
+ if (!vidcap_devices_.empty()) {
+ caps |= VIDEO_SEND;
+ }
+ return caps;
+ }
+ virtual bool GetAudioInputDevices(std::vector<Device>* devs) {
+ *devs = input_devices_;
+ return true;
+ }
+ virtual bool GetAudioOutputDevices(std::vector<Device>* devs) {
+ *devs = output_devices_;
+ return true;
+ }
+ virtual bool GetAudioInputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(true, name, out);
+ }
+ virtual bool GetAudioOutputDevice(const std::string& name, Device* out) {
+ return GetAudioDevice(false, name, out);
+ }
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs) {
+ *devs = vidcap_devices_;
+ return true;
+ }
+ virtual void SetVideoDeviceCapturerFactory(
+ VideoDeviceCapturerFactory* video_device_capturer_factory) {
+ }
+ virtual void SetScreenCapturerFactory(
+ ScreenCapturerFactory* screen_capturer_factory) {
+ screen_capturer_factory_.reset(screen_capturer_factory);
+ }
+ virtual void SetVideoCaptureDeviceMaxFormat(const std::string& usb_id,
+ const VideoFormat& max_format) {
+ max_formats_[usb_id] = max_format;
+ }
+ bool IsMaxFormatForDevice(const std::string& usb_id,
+ const VideoFormat& max_format) const {
+ std::map<std::string, VideoFormat>::const_iterator found =
+ max_formats_.find(usb_id);
+ return (found != max_formats_.end()) ?
+ max_format == found->second :
+ false;
+ }
+ virtual void ClearVideoCaptureDeviceMaxFormat(const std::string& usb_id) {
+ max_formats_.erase(usb_id);
+ }
+ virtual VideoCapturer* CreateVideoCapturer(const Device& device) const {
+ return new FakeVideoCapturer();
+ }
+ virtual VideoCapturer* CreateScreenCapturer(
+ const ScreencastId& screenid) const {
+ if (!screen_capturer_factory_) {
+ return new FakeVideoCapturer();
+ }
+ return screen_capturer_factory_->Create(screenid);
+ }
+ virtual bool GetWindows(
+ std::vector<rtc::WindowDescription>* descriptions) {
+ descriptions->clear();
+ const uint32_t id = 1u; // Note that 0 is not a valid ID.
+ const rtc::WindowId window_id =
+ rtc::WindowId::Cast(id);
+ std::string title = "FakeWindow";
+ rtc::WindowDescription window_description(window_id, title);
+ descriptions->push_back(window_description);
+ return true;
+ }
+ virtual VideoCapturer* CreateWindowCapturer(rtc::WindowId window) {
+ if (!window.IsValid()) {
+ return NULL;
+ }
+ return new FakeVideoCapturer;
+ }
+ virtual bool GetDesktops(
+ std::vector<rtc::DesktopDescription>* descriptions) {
+ descriptions->clear();
+ const int id = 0;
+ const int valid_index = 0;
+ const rtc::DesktopId desktop_id =
+ rtc::DesktopId::Cast(id, valid_index);
+ std::string title = "FakeDesktop";
+ rtc::DesktopDescription desktop_description(desktop_id, title);
+ descriptions->push_back(desktop_description);
+ return true;
+ }
+ virtual VideoCapturer* CreateDesktopCapturer(rtc::DesktopId desktop) {
+ if (!desktop.IsValid()) {
+ return NULL;
+ }
+ return new FakeVideoCapturer;
+ }
+
+ virtual bool GetDefaultVideoCaptureDevice(Device* device) {
+ if (vidcap_devices_.empty()) {
+ return false;
+ }
+ *device = vidcap_devices_[0];
+ return true;
+ }
+
+#ifdef OSX
+ bool QtKitToSgDevice(const std::string& qtkit_name, Device* out) {
+ out->name = qtkit_name;
+ out->id = "sg:" + qtkit_name;
+ return true;
+ }
+#endif
+
+ void SetAudioInputDevices(const std::vector<std::string>& devices) {
+ input_devices_.clear();
+ for (size_t i = 0; i < devices.size(); ++i) {
+ input_devices_.push_back(Device(devices[i],
+ static_cast<int>(i)));
+ }
+ SignalDevicesChange();
+ }
+ void SetAudioOutputDevices(const std::vector<std::string>& devices) {
+ output_devices_.clear();
+ for (size_t i = 0; i < devices.size(); ++i) {
+ output_devices_.push_back(Device(devices[i],
+ static_cast<int>(i)));
+ }
+ SignalDevicesChange();
+ }
+ void SetVideoCaptureDevices(const std::vector<std::string>& devices) {
+ vidcap_devices_.clear();
+ for (size_t i = 0; i < devices.size(); ++i) {
+ vidcap_devices_.push_back(Device(devices[i],
+ static_cast<int>(i)));
+ }
+ SignalDevicesChange();
+ }
+ virtual bool GetVideoCaptureDevice(const std::string& name,
+ Device* out) {
+ if (vidcap_devices_.empty())
+ return false;
+
+ // If the name is empty, return the default device.
+ if (name.empty() || name == kDefaultDeviceName) {
+ *out = vidcap_devices_[0];
+ return true;
+ }
+
+ return FindDeviceByName(vidcap_devices_, name, out);
+ }
+ bool GetAudioDevice(bool is_input, const std::string& name,
+ Device* out) {
+ // If the name is empty, return the default device.
+ if (name.empty() || name == kDefaultDeviceName) {
+ *out = Device(name, -1);
+ return true;
+ }
+
+ return FindDeviceByName((is_input ? input_devices_ : output_devices_),
+ name, out);
+ }
+ static bool FindDeviceByName(const std::vector<Device>& devices,
+ const std::string& name,
+ Device* out) {
+ for (std::vector<Device>::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ if (name == it->name) {
+ *out = *it;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ std::vector<Device> input_devices_;
+ std::vector<Device> output_devices_;
+ std::vector<Device> vidcap_devices_;
+ std::map<std::string, VideoFormat> max_formats_;
+ rtc::scoped_ptr<
+ ScreenCapturerFactory> screen_capturer_factory_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_FAKEDEVICEMANAGER_H_
diff --git a/talk/media/devices/filevideocapturer.cc b/talk/media/devices/filevideocapturer.cc
new file mode 100644
index 0000000000..72398e0b88
--- /dev/null
+++ b/talk/media/devices/filevideocapturer.cc
@@ -0,0 +1,385 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Implementation of VideoRecorder and FileVideoCapturer.
+
+#include "talk/media/devices/filevideocapturer.h"
+
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
+
+namespace cricket {
+
+/////////////////////////////////////////////////////////////////////
+// Implementation of class VideoRecorder
+/////////////////////////////////////////////////////////////////////
+bool VideoRecorder::Start(const std::string& filename, bool write_header) {
+ Stop();
+ write_header_ = write_header;
+ int err;
+ if (!video_file_.Open(filename, "wb", &err)) {
+ LOG(LS_ERROR) << "Unable to open file " << filename << " err=" << err;
+ return false;
+ }
+ return true;
+}
+
+void VideoRecorder::Stop() {
+ video_file_.Close();
+}
+
+bool VideoRecorder::RecordFrame(const CapturedFrame& frame) {
+ if (rtc::SS_CLOSED == video_file_.GetState()) {
+ LOG(LS_ERROR) << "File not opened yet";
+ return false;
+ }
+
+ uint32_t size = 0;
+ if (!frame.GetDataSize(&size)) {
+ LOG(LS_ERROR) << "Unable to calculate the data size of the frame";
+ return false;
+ }
+
+ if (write_header_) {
+ // Convert the frame header to bytebuffer.
+ rtc::ByteBuffer buffer;
+ buffer.WriteUInt32(frame.width);
+ buffer.WriteUInt32(frame.height);
+ buffer.WriteUInt32(frame.fourcc);
+ buffer.WriteUInt32(frame.pixel_width);
+ buffer.WriteUInt32(frame.pixel_height);
+ // Elapsed time is deprecated.
+ const uint64_t dummy_elapsed_time = 0;
+ buffer.WriteUInt64(dummy_elapsed_time);
+ buffer.WriteUInt64(frame.time_stamp);
+ buffer.WriteUInt32(size);
+
+ // Write the bytebuffer to file.
+ if (rtc::SR_SUCCESS != video_file_.Write(buffer.Data(),
+ buffer.Length(),
+ NULL,
+ NULL)) {
+ LOG(LS_ERROR) << "Failed to write frame header";
+ return false;
+ }
+ }
+ // Write the frame data to file.
+ if (rtc::SR_SUCCESS != video_file_.Write(frame.data,
+ size,
+ NULL,
+ NULL)) {
+ LOG(LS_ERROR) << "Failed to write frame data";
+ return false;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////
+// Definition of private class FileReadThread that periodically reads
+// frames from a file.
+///////////////////////////////////////////////////////////////////////
+class FileVideoCapturer::FileReadThread
+ : public rtc::Thread, public rtc::MessageHandler {
+ public:
+ explicit FileReadThread(FileVideoCapturer* capturer)
+ : capturer_(capturer),
+ finished_(false) {
+ }
+
+ virtual ~FileReadThread() {
+ Stop();
+ }
+
+ // Override virtual method of parent Thread. Context: Worker Thread.
+ virtual void Run() {
+ // Read the first frame and start the message pump. The pump runs until
+ // Stop() is called externally or Quit() is called by OnMessage().
+ int waiting_time_ms = 0;
+ if (capturer_ && capturer_->ReadFrame(true, &waiting_time_ms)) {
+ PostDelayed(waiting_time_ms, this);
+ Thread::Run();
+ }
+
+ rtc::CritScope cs(&crit_);
+ finished_ = true;
+ }
+
+ // Override virtual method of parent MessageHandler. Context: Worker Thread.
+ virtual void OnMessage(rtc::Message* /*pmsg*/) {
+ int waiting_time_ms = 0;
+ if (capturer_ && capturer_->ReadFrame(false, &waiting_time_ms)) {
+ PostDelayed(waiting_time_ms, this);
+ } else {
+ Quit();
+ }
+ }
+
+ // Check if Run() is finished.
+ bool Finished() const {
+ rtc::CritScope cs(&crit_);
+ return finished_;
+ }
+
+ private:
+ FileVideoCapturer* capturer_;
+ mutable rtc::CriticalSection crit_;
+ bool finished_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(FileReadThread);
+};
+
+/////////////////////////////////////////////////////////////////////
+// Implementation of class FileVideoCapturer
+/////////////////////////////////////////////////////////////////////
+static const int64_t kNumNanoSecsPerMilliSec = 1000000;
+const char* FileVideoCapturer::kVideoFileDevicePrefix = "video-file:";
+
+FileVideoCapturer::FileVideoCapturer()
+ : frame_buffer_size_(0),
+ file_read_thread_(NULL),
+ repeat_(0),
+ last_frame_timestamp_ns_(0),
+ ignore_framerate_(false) {
+}
+
+FileVideoCapturer::~FileVideoCapturer() {
+ Stop();
+ delete[] static_cast<char*>(captured_frame_.data);
+}
+
+bool FileVideoCapturer::Init(const Device& device) {
+ if (!FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
+ return false;
+ }
+ std::string filename(device.name);
+ if (IsRunning()) {
+ LOG(LS_ERROR) << "The file video capturer is already running";
+ return false;
+ }
+ // Open the file.
+ int err;
+ if (!video_file_.Open(filename, "rb", &err)) {
+ LOG(LS_ERROR) << "Unable to open the file " << filename << " err=" << err;
+ return false;
+ }
+ // Read the first frame's header to determine the supported format.
+ CapturedFrame frame;
+ if (rtc::SR_SUCCESS != ReadFrameHeader(&frame)) {
+ LOG(LS_ERROR) << "Failed to read the first frame header";
+ video_file_.Close();
+ return false;
+ }
+ // Seek back to the start of the file.
+ if (!video_file_.SetPosition(0)) {
+ LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
+ video_file_.Close();
+ return false;
+ }
+
+ // Enumerate the supported formats. We have only one supported format. We set
+ // the frame interval to kMinimumInterval here. In Start(), if the capture
+ // format's interval is greater than kMinimumInterval, we use the interval;
+ // otherwise, we use the timestamp in the file to control the interval.
+ VideoFormat format(frame.width, frame.height, VideoFormat::kMinimumInterval,
+ frame.fourcc);
+ std::vector<VideoFormat> supported;
+ supported.push_back(format);
+
+ // TODO(thorcarpenter): Report the actual file video format as the supported
+ // format. Do not use kMinimumInterval as it conflicts with video adaptation.
+ SetId(device.id);
+ SetSupportedFormats(supported);
+
+ // TODO(wuwang): Design an E2E integration test for video adaptation,
+ // then remove the below call to disable the video adapter.
+ set_enable_video_adapter(false);
+ return true;
+}
+
+bool FileVideoCapturer::Init(const std::string& filename) {
+ return Init(FileVideoCapturer::CreateFileVideoCapturerDevice(filename));
+}
+
+CaptureState FileVideoCapturer::Start(const VideoFormat& capture_format) {
+ if (IsRunning()) {
+ LOG(LS_ERROR) << "The file video capturer is already running";
+ return CS_FAILED;
+ }
+
+ if (rtc::SS_CLOSED == video_file_.GetState()) {
+ LOG(LS_ERROR) << "File not opened yet";
+ return CS_NO_DEVICE;
+ } else if (!video_file_.SetPosition(0)) {
+ LOG(LS_ERROR) << "Failed to seek back to beginning of the file";
+ return CS_FAILED;
+ }
+
+ SetCaptureFormat(&capture_format);
+ // Create a thread to read the file.
+ file_read_thread_ = new FileReadThread(this);
+ bool ret = file_read_thread_->Start();
+ if (ret) {
+ LOG(LS_INFO) << "File video capturer '" << GetId() << "' started";
+ return CS_RUNNING;
+ } else {
+ LOG(LS_ERROR) << "File video capturer '" << GetId() << "' failed to start";
+ return CS_FAILED;
+ }
+}
+
+bool FileVideoCapturer::IsRunning() {
+ return file_read_thread_ && !file_read_thread_->Finished();
+}
+
+void FileVideoCapturer::Stop() {
+ if (file_read_thread_) {
+ file_read_thread_->Stop();
+ file_read_thread_ = NULL;
+ LOG(LS_INFO) << "File video capturer '" << GetId() << "' stopped";
+ }
+ SetCaptureFormat(NULL);
+}
+
+bool FileVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
+ if (!fourccs) {
+ return false;
+ }
+
+ fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
+ return true;
+}
+
+rtc::StreamResult FileVideoCapturer::ReadFrameHeader(
+ CapturedFrame* frame) {
+ // We first read kFrameHeaderSize bytes from the file stream to a memory
+ // buffer, then construct a bytebuffer from the memory buffer, and finally
+ // read the frame header from the bytebuffer.
+ char header[CapturedFrame::kFrameHeaderSize];
+ rtc::StreamResult sr;
+ size_t bytes_read;
+ int error;
+ sr = video_file_.Read(header,
+ CapturedFrame::kFrameHeaderSize,
+ &bytes_read,
+ &error);
+ LOG(LS_VERBOSE) << "Read frame header: stream_result = " << sr
+ << ", bytes read = " << bytes_read << ", error = " << error;
+ if (rtc::SR_SUCCESS == sr) {
+ if (CapturedFrame::kFrameHeaderSize != bytes_read) {
+ return rtc::SR_EOS;
+ }
+ rtc::ByteBuffer buffer(header, CapturedFrame::kFrameHeaderSize);
+ buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->width));
+ buffer.ReadUInt32(reinterpret_cast<uint32_t*>(&frame->height));
+ buffer.ReadUInt32(&frame->fourcc);
+ buffer.ReadUInt32(&frame->pixel_width);
+ buffer.ReadUInt32(&frame->pixel_height);
+ // Elapsed time is deprecated.
+ uint64_t dummy_elapsed_time;
+ buffer.ReadUInt64(&dummy_elapsed_time);
+ buffer.ReadUInt64(reinterpret_cast<uint64_t*>(&frame->time_stamp));
+ buffer.ReadUInt32(&frame->data_size);
+ }
+
+ return sr;
+}
+
+// Executed in the context of FileReadThread.
+bool FileVideoCapturer::ReadFrame(bool first_frame, int* wait_time_ms) {
+ uint32_t start_read_time_ms = rtc::Time();
+
+ // 1. Signal the previously read frame to downstream.
+ if (!first_frame) {
+ captured_frame_.time_stamp =
+ kNumNanoSecsPerMilliSec * static_cast<int64_t>(start_read_time_ms);
+ SignalFrameCaptured(this, &captured_frame_);
+ }
+
+ // 2. Read the next frame.
+ if (rtc::SS_CLOSED == video_file_.GetState()) {
+ LOG(LS_ERROR) << "File not opened yet";
+ return false;
+ }
+ // 2.1 Read the frame header.
+ rtc::StreamResult result = ReadFrameHeader(&captured_frame_);
+ if (rtc::SR_EOS == result) { // Loop back if repeat.
+ if (repeat_ != kForever) {
+ if (repeat_ > 0) {
+ --repeat_;
+ } else {
+ return false;
+ }
+ }
+
+ if (video_file_.SetPosition(0)) {
+ result = ReadFrameHeader(&captured_frame_);
+ }
+ }
+ if (rtc::SR_SUCCESS != result) {
+ LOG(LS_ERROR) << "Failed to read the frame header";
+ return false;
+ }
+ // 2.2 Reallocate memory for the frame data if necessary.
+ if (frame_buffer_size_ < captured_frame_.data_size) {
+ frame_buffer_size_ = captured_frame_.data_size;
+ delete[] static_cast<char*>(captured_frame_.data);
+ captured_frame_.data = new char[frame_buffer_size_];
+ }
+ // 2.3 Read the frame adata.
+ if (rtc::SR_SUCCESS != video_file_.Read(captured_frame_.data,
+ captured_frame_.data_size,
+ NULL, NULL)) {
+ LOG(LS_ERROR) << "Failed to read frame data";
+ return false;
+ }
+
+ // 3. Decide how long to wait for the next frame.
+ *wait_time_ms = 0;
+
+ // If the capture format's interval is not kMinimumInterval, we use it to
+ // control the rate; otherwise, we use the timestamp in the file to control
+ // the rate.
+ if (!first_frame && !ignore_framerate_) {
+ int64_t interval_ns =
+ GetCaptureFormat()->interval > VideoFormat::kMinimumInterval
+ ? GetCaptureFormat()->interval
+ : captured_frame_.time_stamp - last_frame_timestamp_ns_;
+ int interval_ms = static_cast<int>(interval_ns / kNumNanoSecsPerMilliSec);
+ interval_ms -= rtc::Time() - start_read_time_ms;
+ if (interval_ms > 0) {
+ *wait_time_ms = interval_ms;
+ }
+ }
+ // Keep the original timestamp read from the file.
+ last_frame_timestamp_ns_ = captured_frame_.time_stamp;
+ return true;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/filevideocapturer.h b/talk/media/devices/filevideocapturer.h
new file mode 100644
index 0000000000..cc41c39b5d
--- /dev/null
+++ b/talk/media/devices/filevideocapturer.h
@@ -0,0 +1,160 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// This file contains two classes, VideoRecorder and FileVideoCapturer.
+// VideoRecorder records the captured frames into a file. The file stores a
+// sequence of captured frames; each frame has a header defined in struct
+// CapturedFrame, followed by the frame data.
+//
+// FileVideoCapturer, a subclass of VideoCapturer, is a simulated video capturer
+// that periodically reads images from a previously recorded file.
+
+#ifndef TALK_MEDIA_DEVICES_FILEVIDEOCAPTURER_H_
+#define TALK_MEDIA_DEVICES_FILEVIDEOCAPTURER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/base/videocapturer.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+class FileStream;
+}
+
+namespace cricket {
+
+// Utility class to record the frames captured by a video capturer into a file.
+class VideoRecorder {
+ public:
+ VideoRecorder() {}
+ ~VideoRecorder() { Stop(); }
+
+ // Start the recorder by opening the specified file. Return true if the file
+ // is opened successfully. write_header should normally be true; false means
+ // write raw frame pixel data to file without any headers.
+ bool Start(const std::string& filename, bool write_header);
+ // Stop the recorder by closing the file.
+ void Stop();
+ // Record a video frame to the file. Return true if the frame is written to
+ // the file successfully. This method needs to be called after Start() and
+ // before Stop().
+ bool RecordFrame(const CapturedFrame& frame);
+
+ private:
+ rtc::FileStream video_file_;
+ bool write_header_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(VideoRecorder);
+};
+
+// Simulated video capturer that periodically reads frames from a file.
+class FileVideoCapturer : public VideoCapturer {
+ public:
+ static const int kForever = -1;
+
+ FileVideoCapturer();
+ virtual ~FileVideoCapturer();
+
+ // Determines if the given device is actually a video file, to be captured
+ // with a FileVideoCapturer.
+ static bool IsFileVideoCapturerDevice(const Device& device) {
+ return rtc::starts_with(device.id.c_str(), kVideoFileDevicePrefix);
+ }
+
+ // Creates a fake device for the given filename.
+ static Device CreateFileVideoCapturerDevice(const std::string& filename) {
+ std::stringstream id;
+ id << kVideoFileDevicePrefix << filename;
+ return Device(filename, id.str());
+ }
+
+ // Set how many times to repeat reading the file. Repeat forever if the
+ // parameter is kForever; no repeat if the parameter is 0 or
+ // less than -1.
+ void set_repeat(int repeat) { repeat_ = repeat; }
+
+ // If ignore_framerate is true, file is read as quickly as possible. If
+ // false, read rate is controlled by the timestamps in the video file
+ // (thus simulating camera capture). Default value set to false.
+ void set_ignore_framerate(bool ignore_framerate) {
+ ignore_framerate_ = ignore_framerate;
+ }
+
+ // Initializes the capturer with the given file.
+ bool Init(const std::string& filename);
+
+ // Initializes the capturer with the given device. This should only be used
+ // if IsFileVideoCapturerDevice returned true for the given device.
+ bool Init(const Device& device);
+
+ // Override virtual methods of parent class VideoCapturer.
+ virtual CaptureState Start(const VideoFormat& capture_format);
+ virtual void Stop();
+ virtual bool IsRunning();
+ virtual bool IsScreencast() const { return false; }
+
+ protected:
+ // Override virtual methods of parent class VideoCapturer.
+ virtual bool GetPreferredFourccs(std::vector<uint32_t>* fourccs);
+
+ // Read the frame header from the file stream, video_file_.
+ rtc::StreamResult ReadFrameHeader(CapturedFrame* frame);
+
+ // Read a frame and determine how long to wait for the next frame. If the
+ // frame is read successfully, Set the output parameter, wait_time_ms and
+ // return true. Otherwise, do not change wait_time_ms and return false.
+ bool ReadFrame(bool first_frame, int* wait_time_ms);
+
+ // Return the CapturedFrame - useful for extracting contents after reading
+ // a frame. Should be used only while still reading a file (i.e. only while
+ // the CapturedFrame object still exists).
+ const CapturedFrame* frame() const {
+ return &captured_frame_;
+ }
+
+ private:
+ class FileReadThread; // Forward declaration, defined in .cc.
+
+ static const char* kVideoFileDevicePrefix;
+ rtc::FileStream video_file_;
+ CapturedFrame captured_frame_;
+ // The number of bytes allocated buffer for captured_frame_.data.
+ uint32_t frame_buffer_size_;
+ FileReadThread* file_read_thread_;
+ int repeat_; // How many times to repeat the file.
+ int64_t last_frame_timestamp_ns_; // Timestamp of last read frame.
+ bool ignore_framerate_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(FileVideoCapturer);
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_FILEVIDEOCAPTURER_H_
diff --git a/talk/media/devices/filevideocapturer_unittest.cc b/talk/media/devices/filevideocapturer_unittest.cc
new file mode 100644
index 0000000000..ccd2407214
--- /dev/null
+++ b/talk/media/devices/filevideocapturer_unittest.cc
@@ -0,0 +1,204 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "talk/media/base/testutils.h"
+#include "talk/media/devices/filevideocapturer.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
+
+namespace {
+
+class FileVideoCapturerTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ capturer_.reset(new cricket::FileVideoCapturer);
+ }
+
+ bool OpenFile(const std::string& filename) {
+ return capturer_->Init(cricket::GetTestFilePath(filename));
+ }
+
+ protected:
+ class VideoCapturerListener : public sigslot::has_slots<> {
+ public:
+ VideoCapturerListener()
+ : frame_count_(0),
+ frame_width_(0),
+ frame_height_(0),
+ resolution_changed_(false) {
+ }
+
+ void OnFrameCaptured(cricket::VideoCapturer* capturer,
+ const cricket::CapturedFrame* frame) {
+ ++frame_count_;
+ if (1 == frame_count_) {
+ frame_width_ = frame->width;
+ frame_height_ = frame->height;
+ } else if (frame_width_ != frame->width ||
+ frame_height_ != frame->height) {
+ resolution_changed_ = true;
+ }
+ }
+
+ int frame_count() const { return frame_count_; }
+ int frame_width() const { return frame_width_; }
+ int frame_height() const { return frame_height_; }
+ bool resolution_changed() const { return resolution_changed_; }
+
+ private:
+ int frame_count_;
+ int frame_width_;
+ int frame_height_;
+ bool resolution_changed_;
+ };
+
+ rtc::scoped_ptr<cricket::FileVideoCapturer> capturer_;
+ cricket::VideoFormat capture_format_;
+};
+
+TEST_F(FileVideoCapturerTest, TestNotOpened) {
+ EXPECT_EQ("", capturer_->GetId());
+ EXPECT_TRUE(capturer_->GetSupportedFormats()->empty());
+ EXPECT_EQ(NULL, capturer_->GetCaptureFormat());
+ EXPECT_FALSE(capturer_->IsRunning());
+}
+
+TEST_F(FileVideoCapturerTest, TestInvalidOpen) {
+ EXPECT_FALSE(OpenFile("NotmeNotme"));
+}
+
+TEST_F(FileVideoCapturerTest, TestOpen) {
+ EXPECT_TRUE(OpenFile("captured-320x240-2s-48.frames"));
+ EXPECT_NE("", capturer_->GetId());
+ EXPECT_TRUE(NULL != capturer_->GetSupportedFormats());
+ EXPECT_EQ(1U, capturer_->GetSupportedFormats()->size());
+ EXPECT_EQ(NULL, capturer_->GetCaptureFormat()); // not started yet
+ EXPECT_FALSE(capturer_->IsRunning());
+}
+
+TEST_F(FileVideoCapturerTest, TestLargeSmallDesiredFormat) {
+ EXPECT_TRUE(OpenFile("captured-320x240-2s-48.frames"));
+ // desired format with large resolution.
+ cricket::VideoFormat desired(
+ 3200, 2400, cricket::VideoFormat::FpsToInterval(30), cricket::FOURCC_ANY);
+ EXPECT_TRUE(capturer_->GetBestCaptureFormat(desired, &capture_format_));
+ EXPECT_EQ(320, capture_format_.width);
+ EXPECT_EQ(240, capture_format_.height);
+
+ // Desired format with small resolution.
+ desired.width = 0;
+ desired.height = 0;
+ EXPECT_TRUE(capturer_->GetBestCaptureFormat(desired, &capture_format_));
+ EXPECT_EQ(320, capture_format_.width);
+ EXPECT_EQ(240, capture_format_.height);
+}
+
+TEST_F(FileVideoCapturerTest, TestSupportedAsDesiredFormat) {
+ EXPECT_TRUE(OpenFile("captured-320x240-2s-48.frames"));
+ // desired format same as the capture format supported by the file
+ cricket::VideoFormat desired = capturer_->GetSupportedFormats()->at(0);
+ EXPECT_TRUE(capturer_->GetBestCaptureFormat(desired, &capture_format_));
+ EXPECT_TRUE(desired == capture_format_);
+
+ // desired format same as the supported capture format except the fourcc
+ desired.fourcc = cricket::FOURCC_ANY;
+ EXPECT_TRUE(capturer_->GetBestCaptureFormat(desired, &capture_format_));
+ EXPECT_NE(capture_format_.fourcc, desired.fourcc);
+
+ // desired format with minimum interval
+ desired.interval = cricket::VideoFormat::kMinimumInterval;
+ EXPECT_TRUE(capturer_->GetBestCaptureFormat(desired, &capture_format_));
+}
+
+TEST_F(FileVideoCapturerTest, TestNoRepeat) {
+ EXPECT_TRUE(OpenFile("captured-320x240-2s-48.frames"));
+ VideoCapturerListener listener;
+ capturer_->SignalFrameCaptured.connect(
+ &listener, &VideoCapturerListener::OnFrameCaptured);
+ capturer_->set_repeat(0);
+ capture_format_ = capturer_->GetSupportedFormats()->at(0);
+ EXPECT_EQ(cricket::CS_RUNNING, capturer_->Start(capture_format_));
+ EXPECT_TRUE_WAIT(!capturer_->IsRunning(), 20000);
+ EXPECT_EQ(48, listener.frame_count());
+}
+
+TEST_F(FileVideoCapturerTest, TestRepeatForever) {
+ // Start the capturer_ with 50 fps and read no less than 150 frames.
+ EXPECT_TRUE(OpenFile("captured-320x240-2s-48.frames"));
+ VideoCapturerListener listener;
+ capturer_->SignalFrameCaptured.connect(
+ &listener, &VideoCapturerListener::OnFrameCaptured);
+ capturer_->set_repeat(cricket::FileVideoCapturer::kForever);
+ capture_format_ = capturer_->GetSupportedFormats()->at(0);
+ capture_format_.interval = cricket::VideoFormat::FpsToInterval(50);
+ EXPECT_EQ(cricket::CS_RUNNING, capturer_->Start(capture_format_));
+ EXPECT_TRUE(NULL != capturer_->GetCaptureFormat());
+ EXPECT_TRUE(capture_format_ == *capturer_->GetCaptureFormat());
+ EXPECT_TRUE_WAIT(!capturer_->IsRunning() ||
+ listener.frame_count() >= 150, 20000);
+ capturer_->Stop();
+ EXPECT_FALSE(capturer_->IsRunning());
+ EXPECT_GE(listener.frame_count(), 150);
+ EXPECT_FALSE(listener.resolution_changed());
+ EXPECT_EQ(listener.frame_width(), capture_format_.width);
+ EXPECT_EQ(listener.frame_height(), capture_format_.height);
+}
+
+// See: https://code.google.com/p/webrtc/issues/detail?id=2409
+TEST_F(FileVideoCapturerTest, DISABLED_TestPartialFrameHeader) {
+ EXPECT_TRUE(OpenFile("1.frame_plus_1.byte"));
+ VideoCapturerListener listener;
+ capturer_->SignalFrameCaptured.connect(
+ &listener, &VideoCapturerListener::OnFrameCaptured);
+ capturer_->set_repeat(0);
+ capture_format_ = capturer_->GetSupportedFormats()->at(0);
+ EXPECT_EQ(cricket::CS_RUNNING, capturer_->Start(capture_format_));
+ EXPECT_TRUE_WAIT(!capturer_->IsRunning(), 1000);
+ EXPECT_EQ(1, listener.frame_count());
+}
+
+TEST_F(FileVideoCapturerTest, TestFileDevices) {
+ cricket::Device not_a_file("I'm a camera", "with an id");
+ EXPECT_FALSE(
+ cricket::FileVideoCapturer::IsFileVideoCapturerDevice(not_a_file));
+ const std::string test_file =
+ cricket::GetTestFilePath("captured-320x240-2s-48.frames");
+ cricket::Device file_device =
+ cricket::FileVideoCapturer::CreateFileVideoCapturerDevice(test_file);
+ EXPECT_TRUE(
+ cricket::FileVideoCapturer::IsFileVideoCapturerDevice(file_device));
+ EXPECT_TRUE(capturer_->Init(file_device));
+ EXPECT_EQ(file_device.id, capturer_->GetId());
+}
+
+} // unnamed namespace
diff --git a/talk/media/devices/gdivideorenderer.cc b/talk/media/devices/gdivideorenderer.cc
new file mode 100644
index 0000000000..69453067e2
--- /dev/null
+++ b/talk/media/devices/gdivideorenderer.cc
@@ -0,0 +1,279 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Implementation of GdiVideoRenderer on Windows
+
+#ifdef WIN32
+
+#include "talk/media/devices/gdivideorenderer.h"
+
+#include "talk/media/base/videocommon.h"
+#include "talk/media/base/videoframe.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/win32window.h"
+
+namespace cricket {
+
+/////////////////////////////////////////////////////////////////////////////
+// Definition of private class VideoWindow. We use a worker thread to manage
+// the window.
+/////////////////////////////////////////////////////////////////////////////
+class GdiVideoRenderer::VideoWindow : public rtc::Win32Window {
+ public:
+ VideoWindow(int x, int y, int width, int height);
+ virtual ~VideoWindow();
+
+ // Called when the video size changes. If it is called the first time, we
+ // create and start the thread. Otherwise, we send kSetSizeMsg to the thread.
+ // Context: non-worker thread.
+ bool SetSize(int width, int height);
+
+ // Called when a new frame is available. Upon this call, we send
+ // kRenderFrameMsg to the window thread. Context: non-worker thread. It may be
+ // better to pass RGB bytes to VideoWindow. However, we pass VideoFrame to put
+ // all the thread synchronization within VideoWindow.
+ bool RenderFrame(const VideoFrame* frame);
+
+ protected:
+ // Override virtual method of rtc::Win32Window. Context: worker Thread.
+ virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result);
+
+ private:
+ enum { kSetSizeMsg = WM_USER, kRenderFrameMsg};
+
+ class WindowThread : public rtc::Thread {
+ public:
+ explicit WindowThread(VideoWindow* window) : window_(window) {}
+
+ virtual ~WindowThread() {
+ Stop();
+ }
+
+ // Override virtual method of rtc::Thread. Context: worker Thread.
+ virtual void Run() {
+ // Initialize the window
+ if (!window_ || !window_->Initialize()) {
+ return;
+ }
+ // Run the message loop
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0) > 0) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ private:
+ VideoWindow* window_;
+ };
+
+ // Context: worker Thread.
+ bool Initialize();
+ void OnPaint();
+ void OnSize(int width, int height, bool frame_changed);
+ void OnRenderFrame(const VideoFrame* frame);
+
+ BITMAPINFO bmi_;
+ rtc::scoped_ptr<uint8_t[]> image_;
+ rtc::scoped_ptr<WindowThread> window_thread_;
+ // The initial position of the window.
+ int initial_x_;
+ int initial_y_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// Implementation of class VideoWindow
+/////////////////////////////////////////////////////////////////////////////
+GdiVideoRenderer::VideoWindow::VideoWindow(
+ int x, int y, int width, int height)
+ : initial_x_(x),
+ initial_y_(y) {
+ memset(&bmi_.bmiHeader, 0, sizeof(bmi_.bmiHeader));
+ bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi_.bmiHeader.biPlanes = 1;
+ bmi_.bmiHeader.biBitCount = 32;
+ bmi_.bmiHeader.biCompression = BI_RGB;
+ bmi_.bmiHeader.biWidth = width;
+ bmi_.bmiHeader.biHeight = -height;
+ bmi_.bmiHeader.biSizeImage = width * height * 4;
+
+ image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]);
+}
+
+GdiVideoRenderer::VideoWindow::~VideoWindow() {
+ // Context: caller Thread. We cannot call Destroy() since the window was
+ // created by another thread. Instead, we send WM_CLOSE message.
+ if (handle()) {
+ SendMessage(handle(), WM_CLOSE, 0, 0);
+ }
+}
+
+bool GdiVideoRenderer::VideoWindow::SetSize(int width, int height) {
+ if (!window_thread_.get()) {
+ // Create and start the window thread.
+ window_thread_.reset(new WindowThread(this));
+ return window_thread_->Start();
+ } else if (width != bmi_.bmiHeader.biWidth ||
+ height != -bmi_.bmiHeader.biHeight) {
+ SendMessage(handle(), kSetSizeMsg, 0, MAKELPARAM(width, height));
+ }
+ return true;
+}
+
+bool GdiVideoRenderer::VideoWindow::RenderFrame(const VideoFrame* video_frame) {
+ if (!handle()) {
+ return false;
+ }
+
+ const VideoFrame* frame = video_frame->GetCopyWithRotationApplied();
+
+ if (!SetSize(static_cast<int>(frame->GetWidth()),
+ static_cast<int>(frame->GetHeight()))) {
+ return false;
+ }
+
+ SendMessage(handle(), kRenderFrameMsg, reinterpret_cast<WPARAM>(frame), 0);
+ return true;
+}
+
+bool GdiVideoRenderer::VideoWindow::OnMessage(UINT uMsg, WPARAM wParam,
+ LPARAM lParam, LRESULT& result) {
+ switch (uMsg) {
+ case WM_PAINT:
+ OnPaint();
+ return true;
+
+ case WM_DESTROY:
+ PostQuitMessage(0); // post WM_QUIT to end the message loop in Run()
+ return false;
+
+ case WM_SIZE: // The window UI was resized.
+ OnSize(LOWORD(lParam), HIWORD(lParam), false);
+ return true;
+
+ case kSetSizeMsg: // The video resolution changed.
+ OnSize(LOWORD(lParam), HIWORD(lParam), true);
+ return true;
+
+ case kRenderFrameMsg:
+ OnRenderFrame(reinterpret_cast<const VideoFrame*>(wParam));
+ return true;
+ }
+ return false;
+}
+
+bool GdiVideoRenderer::VideoWindow::Initialize() {
+ if (!rtc::Win32Window::Create(
+ NULL, L"Video Renderer",
+ WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
+ WS_EX_APPWINDOW,
+ initial_x_, initial_y_,
+ bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight)) {
+ return false;
+ }
+ OnSize(bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight, false);
+ return true;
+}
+
+void GdiVideoRenderer::VideoWindow::OnPaint() {
+ RECT rcClient;
+ GetClientRect(handle(), &rcClient);
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(handle(), &ps);
+ StretchDIBits(hdc,
+ 0, 0, rcClient.right, rcClient.bottom, // destination rect
+ 0, 0, bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight, // source rect
+ image_.get(), &bmi_, DIB_RGB_COLORS, SRCCOPY);
+ EndPaint(handle(), &ps);
+}
+
+void GdiVideoRenderer::VideoWindow::OnSize(int width, int height,
+ bool frame_changed) {
+ // Get window and client sizes
+ RECT rcClient, rcWindow;
+ GetClientRect(handle(), &rcClient);
+ GetWindowRect(handle(), &rcWindow);
+
+ // Find offset between window size and client size
+ POINT ptDiff;
+ ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
+ ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
+
+ // Resize client
+ MoveWindow(handle(), rcWindow.left, rcWindow.top,
+ width + ptDiff.x, height + ptDiff.y, false);
+ UpdateWindow(handle());
+ ShowWindow(handle(), SW_SHOW);
+
+ if (frame_changed && (width != bmi_.bmiHeader.biWidth ||
+ height != -bmi_.bmiHeader.biHeight)) {
+ // Update the bmi and image buffer
+ bmi_.bmiHeader.biWidth = width;
+ bmi_.bmiHeader.biHeight = -height;
+ bmi_.bmiHeader.biSizeImage = width * height * 4;
+ image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]);
+ }
+}
+
+void GdiVideoRenderer::VideoWindow::OnRenderFrame(const VideoFrame* frame) {
+ if (!frame) {
+ return;
+ }
+ // Convert frame to ARGB format, which is accepted by GDI
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(),
+ bmi_.bmiHeader.biSizeImage,
+ bmi_.bmiHeader.biWidth * 4);
+ InvalidateRect(handle(), 0, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Implementation of class GdiVideoRenderer
+/////////////////////////////////////////////////////////////////////////////
+GdiVideoRenderer::GdiVideoRenderer(int x, int y)
+ : initial_x_(x),
+ initial_y_(y) {
+}
+GdiVideoRenderer::~GdiVideoRenderer() {}
+
+bool GdiVideoRenderer::SetSize(int width, int height, int reserved) {
+ if (!window_.get()) { // Create the window for the first frame
+ window_.reset(new VideoWindow(initial_x_, initial_y_, width, height));
+ }
+ return window_->SetSize(width, height);
+}
+
+bool GdiVideoRenderer::RenderFrame(const VideoFrame* frame) {
+ if (!frame || !window_.get()) {
+ return false;
+ }
+ return window_->RenderFrame(frame);
+}
+
+} // namespace cricket
+#endif // WIN32
diff --git a/talk/media/devices/gdivideorenderer.h b/talk/media/devices/gdivideorenderer.h
new file mode 100755
index 0000000000..0e4f6cf7fe
--- /dev/null
+++ b/talk/media/devices/gdivideorenderer.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// Definition of class GdiVideoRenderer that implements the abstract class
+// cricket::VideoRenderer via GDI on Windows.
+
+#ifndef TALK_MEDIA_DEVICES_GDIVIDEORENDERER_H_
+#define TALK_MEDIA_DEVICES_GDIVIDEORENDERER_H_
+
+#ifdef WIN32
+#include "talk/media/base/videorenderer.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace cricket {
+
+class GdiVideoRenderer : public VideoRenderer {
+ public:
+ GdiVideoRenderer(int x, int y);
+ virtual ~GdiVideoRenderer();
+
+ // Implementation of pure virtual methods of VideoRenderer.
+ // These two methods may be executed in different threads.
+ // SetSize is called before RenderFrame.
+ virtual bool SetSize(int width, int height, int reserved);
+ virtual bool RenderFrame(const VideoFrame* frame);
+
+ private:
+ class VideoWindow; // forward declaration, defined in the .cc file
+ rtc::scoped_ptr<VideoWindow> window_;
+ // The initial position of the window.
+ int initial_x_;
+ int initial_y_;
+};
+
+} // namespace cricket
+
+#endif // WIN32
+#endif // TALK_MEDIA_DEVICES_GDIVIDEORENDERER_H_
diff --git a/talk/media/devices/gtkvideorenderer.cc b/talk/media/devices/gtkvideorenderer.cc
new file mode 100755
index 0000000000..d389960e3d
--- /dev/null
+++ b/talk/media/devices/gtkvideorenderer.cc
@@ -0,0 +1,179 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Implementation of GtkVideoRenderer
+
+#include "talk/media/devices/gtkvideorenderer.h"
+
+#include <gdk/gdk.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "talk/media/base/videocommon.h"
+#include "talk/media/base/videoframe.h"
+
+namespace cricket {
+
+class ScopedGdkLock {
+ public:
+ ScopedGdkLock() {
+ gdk_threads_enter();
+ }
+
+ ~ScopedGdkLock() {
+ gdk_threads_leave();
+ }
+};
+
+GtkVideoRenderer::GtkVideoRenderer(int x, int y)
+ : window_(NULL),
+ draw_area_(NULL),
+ initial_x_(x),
+ initial_y_(y),
+ width_(0),
+ height_(0) {
+ g_type_init();
+ // g_thread_init API is deprecated since glib 2.31.0, see release note:
+ // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html
+#if !GLIB_CHECK_VERSION(2, 31, 0)
+ g_thread_init(NULL);
+#endif
+ gdk_threads_init();
+}
+
+GtkVideoRenderer::~GtkVideoRenderer() {
+ if (window_) {
+ ScopedGdkLock lock;
+ gtk_widget_destroy(window_);
+ // Run the Gtk main loop to tear down the window.
+ Pump();
+ }
+ // Don't need to destroy draw_area_ because it is not top-level, so it is
+ // implicitly destroyed by the above.
+}
+
+bool GtkVideoRenderer::SetSize(int width, int height, int reserved) {
+ ScopedGdkLock lock;
+
+ // If the dimension is the same, no-op.
+ if (width_ == width && height_ == height) {
+ return true;
+ }
+
+ // For the first frame, initialize the GTK window
+ if ((!window_ && !Initialize(width, height)) || IsClosed()) {
+ return false;
+ }
+
+ image_.reset(new uint8_t[width * height * 4]);
+ gtk_widget_set_size_request(draw_area_, width, height);
+
+ width_ = width;
+ height_ = height;
+ return true;
+}
+
+bool GtkVideoRenderer::RenderFrame(const VideoFrame* video_frame) {
+ if (!video_frame) {
+ return false;
+ }
+
+ const VideoFrame* frame = video_frame->GetCopyWithRotationApplied();
+
+ // Need to set size as the frame might be rotated.
+ if (!SetSize(frame->GetWidth(), frame->GetHeight(), 0)) {
+ return false;
+ }
+
+ // convert I420 frame to ABGR format, which is accepted by GTK
+ frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR,
+ image_.get(),
+ frame->GetWidth() * frame->GetHeight() * 4,
+ frame->GetWidth() * 4);
+
+ ScopedGdkLock lock;
+
+ if (IsClosed()) {
+ return false;
+ }
+
+ // draw the ABGR image
+ gdk_draw_rgb_32_image(draw_area_->window,
+ draw_area_->style->fg_gc[GTK_STATE_NORMAL],
+ 0,
+ 0,
+ frame->GetWidth(),
+ frame->GetHeight(),
+ GDK_RGB_DITHER_MAX,
+ image_.get(),
+ frame->GetWidth() * 4);
+
+ // Run the Gtk main loop to refresh the window.
+ Pump();
+ return true;
+}
+
+bool GtkVideoRenderer::Initialize(int width, int height) {
+ gtk_init(NULL, NULL);
+ window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ draw_area_ = gtk_drawing_area_new();
+ if (!window_ || !draw_area_) {
+ return false;
+ }
+
+ gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
+ gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer");
+ gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
+ gtk_widget_set_size_request(draw_area_, width, height);
+ gtk_container_add(GTK_CONTAINER(window_), draw_area_);
+ gtk_widget_show_all(window_);
+ gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_);
+
+ image_.reset(new uint8_t[width * height * 4]);
+ return true;
+}
+
+void GtkVideoRenderer::Pump() {
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+}
+
+bool GtkVideoRenderer::IsClosed() const {
+ if (!window_) {
+ // Not initialized yet, so hasn't been closed.
+ return false;
+ }
+
+ if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/gtkvideorenderer.h b/talk/media/devices/gtkvideorenderer.h
new file mode 100755
index 0000000000..0270a7a2c9
--- /dev/null
+++ b/talk/media/devices/gtkvideorenderer.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Definition of class GtkVideoRenderer that implements the abstract class
+// cricket::VideoRenderer via GTK.
+
+#ifndef TALK_MEDIA_DEVICES_GTKVIDEORENDERER_H_
+#define TALK_MEDIA_DEVICES_GTKVIDEORENDERER_H_
+
+#include "talk/media/base/videorenderer.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/scoped_ptr.h"
+
+typedef struct _GtkWidget GtkWidget; // forward declaration, defined in gtk.h
+
+namespace cricket {
+
+class GtkVideoRenderer : public VideoRenderer {
+ public:
+ GtkVideoRenderer(int x, int y);
+ virtual ~GtkVideoRenderer();
+
+ // Implementation of pure virtual methods of VideoRenderer.
+ // These two methods may be executed in different threads.
+ // SetSize is called before RenderFrame.
+ virtual bool SetSize(int width, int height, int reserved);
+ virtual bool RenderFrame(const VideoFrame* frame);
+
+ private:
+ // Initialize the attributes when the first frame arrives.
+ bool Initialize(int width, int height);
+ // Pump the Gtk event loop until there are no events left.
+ void Pump();
+ // Check if the window has been closed.
+ bool IsClosed() const;
+
+ rtc::scoped_ptr<uint8_t[]> image_;
+ GtkWidget* window_;
+ GtkWidget* draw_area_;
+ // The initial position of the window.
+ int initial_x_;
+ int initial_y_;
+
+ int width_;
+ int height_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_GTKVIDEORENDERER_H_
diff --git a/talk/media/devices/libudevsymboltable.cc b/talk/media/devices/libudevsymboltable.cc
new file mode 100644
index 0000000000..351a1e7f5e
--- /dev/null
+++ b/talk/media/devices/libudevsymboltable.cc
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/libudevsymboltable.h"
+
+#include <dlfcn.h>
+
+#include "webrtc/base/logging.h"
+
+namespace cricket {
+
+#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBUDEV_SYMBOLS_CLASS_NAME
+#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBUDEV_SYMBOLS_LIST
+#define LATE_BINDING_SYMBOL_TABLE_DLL_NAME "libudev.so.0"
+#include "webrtc/base/latebindingsymboltable.cc.def"
+#undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME
+#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST
+#undef LATE_BINDING_SYMBOL_TABLE_DLL_NAME
+
+bool IsWrongLibUDevAbiVersion(rtc::DllHandle libudev_0) {
+ rtc::DllHandle libudev_1 = dlopen("libudev.so.1",
+ RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD);
+ bool unsafe_symlink = (libudev_0 == libudev_1);
+ if (unsafe_symlink) {
+ // .0 and .1 are distinct ABIs, so if they point to the same thing then one
+ // of them must be wrong. Probably the old has been symlinked to the new in
+ // a misguided attempt at backwards compatibility.
+ LOG(LS_ERROR) << "libudev.so.0 and libudev.so.1 unsafely point to the"
+ " same thing; not using libudev";
+ } else if (libudev_1) {
+ // If libudev.so.1 is resident but distinct from libudev.so.0, then some
+ // system library loaded the new ABI separately. This is not a problem for
+ // LateBindingSymbolTable because its symbol look-ups are restricted to its
+ // DllHandle, but having libudev.so.0 resident may cause problems for that
+ // system library because symbol names are not namespaced by DLL. (Although
+ // our use of RTLD_LOCAL should avoid most problems.)
+ LOG(LS_WARNING)
+ << "libudev.so.1 is resident but distinct from libudev.so.0";
+ }
+ if (libudev_1) {
+ // Release the refcount that we acquired above. (Does not unload the DLL;
+ // whoever loaded it still needs it.)
+ dlclose(libudev_1);
+ }
+ return unsafe_symlink;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/libudevsymboltable.h b/talk/media/devices/libudevsymboltable.h
new file mode 100644
index 0000000000..f764cd263d
--- /dev/null
+++ b/talk/media/devices/libudevsymboltable.h
@@ -0,0 +1,79 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_LIBUDEVSYMBOLTABLE_H_
+#define TALK_MEDIA_DEVICES_LIBUDEVSYMBOLTABLE_H_
+
+#include <libudev.h>
+
+#include "webrtc/base/latebindingsymboltable.h"
+
+namespace cricket {
+
+#define LIBUDEV_SYMBOLS_CLASS_NAME LibUDevSymbolTable
+// The libudev symbols we need, as an X-Macro list.
+// This list must contain precisely every libudev function that is used in
+// linuxdevicemanager.cc.
+#define LIBUDEV_SYMBOLS_LIST \
+ X(udev_device_get_devnode) \
+ X(udev_device_get_parent_with_subsystem_devtype) \
+ X(udev_device_get_sysattr_value) \
+ X(udev_device_new_from_syspath) \
+ X(udev_device_unref) \
+ X(udev_enumerate_add_match_subsystem) \
+ X(udev_enumerate_get_list_entry) \
+ X(udev_enumerate_new) \
+ X(udev_enumerate_scan_devices) \
+ X(udev_enumerate_unref) \
+ X(udev_list_entry_get_name) \
+ X(udev_list_entry_get_next) \
+ X(udev_monitor_enable_receiving) \
+ X(udev_monitor_filter_add_match_subsystem_devtype) \
+ X(udev_monitor_get_fd) \
+ X(udev_monitor_new_from_netlink) \
+ X(udev_monitor_receive_device) \
+ X(udev_monitor_unref) \
+ X(udev_new) \
+ X(udev_unref)
+
+#define LATE_BINDING_SYMBOL_TABLE_CLASS_NAME LIBUDEV_SYMBOLS_CLASS_NAME
+#define LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST LIBUDEV_SYMBOLS_LIST
+#include "webrtc/base/latebindingsymboltable.h.def"
+#undef LATE_BINDING_SYMBOL_TABLE_CLASS_NAME
+#undef LATE_BINDING_SYMBOL_TABLE_SYMBOLS_LIST
+
+// libudev has changed ABIs to libudev.so.1 in recent distros and lots of users
+// and/or software (including Google Chrome) are symlinking the old to the new.
+// The entire point of ABI versions is that you can't safely do that, and
+// it has caused crashes in the wild. This function checks if the DllHandle that
+// we got back for libudev.so.0 is actually for libudev.so.1. If so, the library
+// cannot safely be used.
+bool IsWrongLibUDevAbiVersion(rtc::DllHandle libudev_0);
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_LIBUDEVSYMBOLTABLE_H_
diff --git a/talk/media/devices/linuxdeviceinfo.cc b/talk/media/devices/linuxdeviceinfo.cc
new file mode 100644
index 0000000000..0b22e15a99
--- /dev/null
+++ b/talk/media/devices/linuxdeviceinfo.cc
@@ -0,0 +1,174 @@
+/*
+ * libjingle
+ * Copyright 2012 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/deviceinfo.h"
+
+#include "talk/media/devices/libudevsymboltable.h"
+#include "webrtc/base/common.h" // for ASSERT
+
+namespace cricket {
+
+class ScopedLibUdev {
+ public:
+ static ScopedLibUdev* Create() {
+ ScopedLibUdev* ret_val = new ScopedLibUdev();
+ if (!ret_val->Init()) {
+ delete ret_val;
+ return NULL;
+ }
+ return ret_val;
+ }
+ ~ScopedLibUdev() {
+ libudev_.Unload();
+ }
+
+ LibUDevSymbolTable* instance() { return &libudev_; }
+
+ private:
+ ScopedLibUdev() {}
+
+ bool Init() {
+ return libudev_.Load() &&
+ !IsWrongLibUDevAbiVersion(libudev_.GetDllHandle());
+ }
+
+ LibUDevSymbolTable libudev_;
+};
+
+class ScopedUdev {
+ public:
+ explicit ScopedUdev(LibUDevSymbolTable* libudev) : libudev_(libudev) {
+ udev_ = libudev_->udev_new()();
+ }
+ ~ScopedUdev() {
+ if (udev_) libudev_->udev_unref()(udev_);
+ }
+
+ udev* instance() { return udev_; }
+
+ private:
+ LibUDevSymbolTable* libudev_;
+ udev* udev_;
+};
+
+class ScopedUdevEnumerate {
+ public:
+ ScopedUdevEnumerate(LibUDevSymbolTable* libudev, udev* udev)
+ : libudev_(libudev) {
+ enumerate_ = libudev_->udev_enumerate_new()(udev);
+ }
+ ~ScopedUdevEnumerate() {
+ if (enumerate_) libudev_->udev_enumerate_unref()(enumerate_);
+ }
+
+ udev_enumerate* instance() { return enumerate_; }
+
+ private:
+ LibUDevSymbolTable* libudev_;
+ udev_enumerate* enumerate_;
+};
+
+bool GetUsbProperty(const Device& device, const char* property_name,
+ std::string* property) {
+ rtc::scoped_ptr<ScopedLibUdev> libudev_context(ScopedLibUdev::Create());
+ if (!libudev_context) {
+ return false;
+ }
+ ScopedUdev udev_context(libudev_context->instance());
+ if (!udev_context.instance()) {
+ return false;
+ }
+ ScopedUdevEnumerate enumerate_context(libudev_context->instance(),
+ udev_context.instance());
+ if (!enumerate_context.instance()) {
+ return false;
+ }
+ libudev_context->instance()->udev_enumerate_add_match_subsystem()(
+ enumerate_context.instance(), "video4linux");
+ libudev_context->instance()->udev_enumerate_scan_devices()(
+ enumerate_context.instance());
+ udev_list_entry* devices =
+ libudev_context->instance()->udev_enumerate_get_list_entry()(
+ enumerate_context.instance());
+ if (!devices) {
+ return false;
+ }
+ udev_list_entry* dev_list_entry = NULL;
+ const char* property_value = NULL;
+ // Macro that expands to a for-loop over the devices.
+ for (dev_list_entry = devices; dev_list_entry != NULL;
+ dev_list_entry = libudev_context->instance()->
+ udev_list_entry_get_next()(dev_list_entry)) {
+ const char* path = libudev_context->instance()->udev_list_entry_get_name()(
+ dev_list_entry);
+ if (!path) continue;
+ udev_device* dev =
+ libudev_context->instance()->udev_device_new_from_syspath()(
+ udev_context.instance(), path);
+ if (!dev) continue;
+ const char* device_node =
+ libudev_context->instance()->udev_device_get_devnode()(dev);
+ if (!device_node || device.id.compare(device_node) != 0) {
+ continue;
+ }
+ dev = libudev_context->instance()->
+ udev_device_get_parent_with_subsystem_devtype()(
+ dev, "usb", "usb_device");
+ if (!dev) continue;
+ property_value = libudev_context->instance()->
+ udev_device_get_sysattr_value()(
+ dev, property_name);
+ break;
+ }
+ if (!property_value) {
+ return false;
+ }
+ property->assign(property_value);
+ return true;
+}
+
+bool GetUsbId(const Device& device, std::string* usb_id) {
+ std::string id_vendor;
+ std::string id_product;
+ if (!GetUsbProperty(device, "idVendor", &id_vendor)) {
+ return false;
+ }
+ if (!GetUsbProperty(device, "idProduct", &id_product)) {
+ return false;
+ }
+ usb_id->clear();
+ usb_id->append(id_vendor);
+ usb_id->append(":");
+ usb_id->append(id_product);
+ return true;
+}
+
+bool GetUsbVersion(const Device& device, std::string* usb_version) {
+ return GetUsbProperty(device, "version", usb_version);
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/linuxdevicemanager.cc b/talk/media/devices/linuxdevicemanager.cc
new file mode 100644
index 0000000000..25be321c5e
--- /dev/null
+++ b/talk/media/devices/linuxdevicemanager.cc
@@ -0,0 +1,410 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/linuxdevicemanager.h"
+
+#include <unistd.h>
+#include "talk/media/base/mediacommon.h"
+#include "talk/media/devices/libudevsymboltable.h"
+#include "talk/media/devices/v4llookup.h"
+#include "webrtc/sound/platformsoundsystem.h"
+#include "webrtc/sound/platformsoundsystemfactory.h"
+#include "webrtc/sound/sounddevicelocator.h"
+#include "webrtc/sound/soundsysteminterface.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/linux.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+
+namespace cricket {
+
+DeviceManagerInterface* DeviceManagerFactory::Create() {
+ return new LinuxDeviceManager();
+}
+
+class LinuxDeviceWatcher
+ : public DeviceWatcher,
+ private rtc::Dispatcher {
+ public:
+ explicit LinuxDeviceWatcher(DeviceManagerInterface* dm);
+ virtual ~LinuxDeviceWatcher();
+ virtual bool Start();
+ virtual void Stop();
+
+ private:
+ virtual uint32_t GetRequestedEvents();
+ virtual void OnPreEvent(uint32_t ff);
+ virtual void OnEvent(uint32_t ff, int err);
+ virtual int GetDescriptor();
+ virtual bool IsDescriptorClosed();
+
+ DeviceManagerInterface* manager_;
+ LibUDevSymbolTable libudev_;
+ struct udev* udev_;
+ struct udev_monitor* udev_monitor_;
+ bool registered_;
+};
+
+static const char* const kFilteredAudioDevicesName[] = {
+#if defined(CHROMEOS)
+ "surround40:",
+ "surround41:",
+ "surround50:",
+ "surround51:",
+ "surround71:",
+ "iec958:", // S/PDIF
+#endif
+ NULL,
+};
+static const char* kFilteredVideoDevicesName[] = {
+ NULL,
+};
+
+LinuxDeviceManager::LinuxDeviceManager()
+ : sound_system_(new rtc::PlatformSoundSystemFactory()) {
+ set_watcher(new LinuxDeviceWatcher(this));
+}
+
+LinuxDeviceManager::~LinuxDeviceManager() {
+}
+
+bool LinuxDeviceManager::GetAudioDevices(bool input,
+ std::vector<Device>* devs) {
+ devs->clear();
+ if (!sound_system_.get()) {
+ return false;
+ }
+ rtc::SoundSystemInterface::SoundDeviceLocatorList list;
+ bool success;
+ if (input) {
+ success = sound_system_->EnumerateCaptureDevices(&list);
+ } else {
+ success = sound_system_->EnumeratePlaybackDevices(&list);
+ }
+ if (!success) {
+ LOG(LS_ERROR) << "Can't enumerate devices";
+ sound_system_.release();
+ return false;
+ }
+ // We have to start the index at 1 because webrtc VoiceEngine puts the default
+ // device at index 0, but Enumerate(Capture|Playback)Devices does not include
+ // a locator for the default device.
+ int index = 1;
+ for (rtc::SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begin();
+ i != list.end();
+ ++i, ++index) {
+ devs->push_back(Device((*i)->name(), index));
+ }
+ rtc::SoundSystemInterface::ClearSoundDeviceLocatorList(&list);
+ sound_system_.release();
+ return FilterDevices(devs, kFilteredAudioDevicesName);
+}
+
+static const std::string kVideoMetaPathK2_4("/proc/video/dev/");
+static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/");
+
+enum MetaType { M2_4, M2_6, NONE };
+
+static void ScanDeviceDirectory(const std::string& devdir,
+ std::vector<Device>* devices) {
+ rtc::scoped_ptr<rtc::DirectoryIterator> directoryIterator(
+ rtc::Filesystem::IterateDirectory());
+
+ if (directoryIterator->Iterate(rtc::Pathname(devdir))) {
+ do {
+ std::string filename = directoryIterator->Name();
+ std::string device_name = devdir + filename;
+ if (!directoryIterator->IsDots()) {
+ if (filename.find("video") == 0 &&
+ V4LLookup::IsV4L2Device(device_name)) {
+ devices->push_back(Device(device_name, device_name));
+ }
+ }
+ } while (directoryIterator->Next());
+ }
+}
+
+static std::string GetVideoDeviceNameK2_6(const std::string& device_meta_path) {
+ std::string device_name;
+
+ rtc::scoped_ptr<rtc::FileStream> device_meta_stream(
+ rtc::Filesystem::OpenFile(device_meta_path, "r"));
+
+ if (device_meta_stream) {
+ if (device_meta_stream->ReadLine(&device_name) != rtc::SR_SUCCESS) {
+ LOG(LS_ERROR) << "Failed to read V4L2 device meta " << device_meta_path;
+ }
+ device_meta_stream->Close();
+ }
+
+ return device_name;
+}
+
+static std::string Trim(const std::string& s, const std::string& drop = " \t") {
+ std::string::size_type first = s.find_first_not_of(drop);
+ std::string::size_type last = s.find_last_not_of(drop);
+
+ if (first == std::string::npos || last == std::string::npos)
+ return std::string("");
+
+ return s.substr(first, last - first + 1);
+}
+
+static std::string GetVideoDeviceNameK2_4(const std::string& device_meta_path) {
+ rtc::ConfigParser::MapVector all_values;
+
+ rtc::ConfigParser config_parser;
+ rtc::FileStream* file_stream =
+ rtc::Filesystem::OpenFile(device_meta_path, "r");
+
+ if (file_stream == NULL) return "";
+
+ config_parser.Attach(file_stream);
+ config_parser.Parse(&all_values);
+
+ for (rtc::ConfigParser::MapVector::iterator i = all_values.begin();
+ i != all_values.end(); ++i) {
+ rtc::ConfigParser::SimpleMap::iterator device_name_i =
+ i->find("name");
+
+ if (device_name_i != i->end()) {
+ return device_name_i->second;
+ }
+ }
+
+ return "";
+}
+
+static std::string GetVideoDeviceName(MetaType meta,
+ const std::string& device_file_name) {
+ std::string device_meta_path;
+ std::string device_name;
+ std::string meta_file_path;
+
+ if (meta == M2_6) {
+ meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/name";
+
+ LOG(LS_INFO) << "Trying " + meta_file_path;
+ device_name = GetVideoDeviceNameK2_6(meta_file_path);
+
+ if (device_name.empty()) {
+ meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/model";
+
+ LOG(LS_INFO) << "Trying " << meta_file_path;
+ device_name = GetVideoDeviceNameK2_6(meta_file_path);
+ }
+ } else {
+ meta_file_path = kVideoMetaPathK2_4 + device_file_name;
+ LOG(LS_INFO) << "Trying " << meta_file_path;
+ device_name = GetVideoDeviceNameK2_4(meta_file_path);
+ }
+
+ if (device_name.empty()) {
+ device_name = "/dev/" + device_file_name;
+ LOG(LS_ERROR)
+ << "Device name not found, defaulting to device path " << device_name;
+ }
+
+ LOG(LS_INFO) << "Name for " << device_file_name << " is " << device_name;
+
+ return Trim(device_name);
+}
+
+static void ScanV4L2Devices(std::vector<Device>* devices) {
+ LOG(LS_INFO) << ("Enumerating V4L2 devices");
+
+ MetaType meta;
+ std::string metadata_dir;
+
+ rtc::scoped_ptr<rtc::DirectoryIterator> directoryIterator(
+ rtc::Filesystem::IterateDirectory());
+
+ // Try and guess kernel version
+ if (directoryIterator->Iterate(kVideoMetaPathK2_6)) {
+ meta = M2_6;
+ metadata_dir = kVideoMetaPathK2_6;
+ } else if (directoryIterator->Iterate(kVideoMetaPathK2_4)) {
+ meta = M2_4;
+ metadata_dir = kVideoMetaPathK2_4;
+ } else {
+ meta = NONE;
+ }
+
+ if (meta != NONE) {
+ LOG(LS_INFO) << "V4L2 device metadata found at " << metadata_dir;
+
+ do {
+ std::string filename = directoryIterator->Name();
+
+ if (filename.find("video") == 0) {
+ std::string device_path = "/dev/" + filename;
+
+ if (V4LLookup::IsV4L2Device(device_path)) {
+ devices->push_back(
+ Device(GetVideoDeviceName(meta, filename), device_path));
+ }
+ }
+ } while (directoryIterator->Next());
+ } else {
+ LOG(LS_ERROR) << "Unable to detect v4l2 metadata directory";
+ }
+
+ if (devices->size() == 0) {
+ LOG(LS_INFO) << "Plan B. Scanning all video devices in /dev directory";
+ ScanDeviceDirectory("/dev/", devices);
+ }
+
+ LOG(LS_INFO) << "Total V4L2 devices found : " << devices->size();
+}
+
+bool LinuxDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
+ devices->clear();
+ ScanV4L2Devices(devices);
+ return FilterDevices(devices, kFilteredVideoDevicesName);
+}
+
+LinuxDeviceWatcher::LinuxDeviceWatcher(DeviceManagerInterface* dm)
+ : DeviceWatcher(dm),
+ manager_(dm),
+ udev_(NULL),
+ udev_monitor_(NULL),
+ registered_(false) {
+}
+
+LinuxDeviceWatcher::~LinuxDeviceWatcher() {
+}
+
+static rtc::PhysicalSocketServer* CurrentSocketServer() {
+ rtc::SocketServer* ss =
+ rtc::ThreadManager::Instance()->WrapCurrentThread()->socketserver();
+ return reinterpret_cast<rtc::PhysicalSocketServer*>(ss);
+}
+
+bool LinuxDeviceWatcher::Start() {
+ // We deliberately return true in the failure paths here because libudev is
+ // not a critical component of a Linux system so it may not be present/usable,
+ // and we don't want to halt LinuxDeviceManager initialization in such a case.
+ if (!libudev_.Load() || IsWrongLibUDevAbiVersion(libudev_.GetDllHandle())) {
+ LOG(LS_WARNING)
+ << "libudev not present/usable; LinuxDeviceWatcher disabled";
+ return true;
+ }
+ udev_ = libudev_.udev_new()();
+ if (!udev_) {
+ LOG_ERR(LS_ERROR) << "udev_new()";
+ return true;
+ }
+ // The second argument here is the event source. It can be either "kernel" or
+ // "udev", but "udev" is the only correct choice. Apps listen on udev and the
+ // udev daemon in turn listens on the kernel.
+ udev_monitor_ = libudev_.udev_monitor_new_from_netlink()(udev_, "udev");
+ if (!udev_monitor_) {
+ LOG_ERR(LS_ERROR) << "udev_monitor_new_from_netlink()";
+ return true;
+ }
+ // We only listen for changes in the video devices. Audio devices are more or
+ // less unimportant because receiving device change notifications really only
+ // matters for broadcasting updated send/recv capabilities based on whether
+ // there is at least one device available, and almost all computers have at
+ // least one audio device. Also, PulseAudio device notifications don't come
+ // from the udev daemon, they come from the PulseAudio daemon, so we'd only
+ // want to listen for audio device changes from udev if using ALSA. For
+ // simplicity, we don't bother with any audio stuff at all.
+ if (libudev_.udev_monitor_filter_add_match_subsystem_devtype()(
+ udev_monitor_, "video4linux", NULL) < 0) {
+ LOG_ERR(LS_ERROR) << "udev_monitor_filter_add_match_subsystem_devtype()";
+ return true;
+ }
+ if (libudev_.udev_monitor_enable_receiving()(udev_monitor_) < 0) {
+ LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()";
+ return true;
+ }
+ CurrentSocketServer()->Add(this);
+ registered_ = true;
+ return true;
+}
+
+void LinuxDeviceWatcher::Stop() {
+ if (registered_) {
+ CurrentSocketServer()->Remove(this);
+ registered_ = false;
+ }
+ if (udev_monitor_) {
+ libudev_.udev_monitor_unref()(udev_monitor_);
+ udev_monitor_ = NULL;
+ }
+ if (udev_) {
+ libudev_.udev_unref()(udev_);
+ udev_ = NULL;
+ }
+ libudev_.Unload();
+}
+
+uint32_t LinuxDeviceWatcher::GetRequestedEvents() {
+ return rtc::DE_READ;
+}
+
+void LinuxDeviceWatcher::OnPreEvent(uint32_t ff) {
+ // Nothing to do.
+}
+
+void LinuxDeviceWatcher::OnEvent(uint32_t ff, int err) {
+ udev_device* device = libudev_.udev_monitor_receive_device()(udev_monitor_);
+ if (!device) {
+ // Probably the socket connection to the udev daemon was terminated (perhaps
+ // the daemon crashed or is being restarted?).
+ LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()";
+ // Stop listening to avoid potential livelock (an fd with EOF in it is
+ // always considered readable).
+ CurrentSocketServer()->Remove(this);
+ registered_ = false;
+ return;
+ }
+ // Else we read the device successfully.
+
+ // Since we already have our own filesystem-based device enumeration code, we
+ // simply re-enumerate rather than inspecting the device event.
+ libudev_.udev_device_unref()(device);
+ manager_->SignalDevicesChange();
+}
+
+int LinuxDeviceWatcher::GetDescriptor() {
+ return libudev_.udev_monitor_get_fd()(udev_monitor_);
+}
+
+bool LinuxDeviceWatcher::IsDescriptorClosed() {
+ // If it is closed then we will just get an error in
+ // udev_monitor_receive_device and unregister, so we don't need to check for
+ // it separately.
+ return false;
+}
+
+}; // namespace cricket
diff --git a/talk/media/devices/linuxdevicemanager.h b/talk/media/devices/linuxdevicemanager.h
new file mode 100644
index 0000000000..1eb648f395
--- /dev/null
+++ b/talk/media/devices/linuxdevicemanager.h
@@ -0,0 +1,55 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_LINUXDEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_LINUXDEVICEMANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/devices/devicemanager.h"
+#include "webrtc/sound/soundsystemfactory.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/stringencode.h"
+
+namespace cricket {
+
+class LinuxDeviceManager : public DeviceManager {
+ public:
+ LinuxDeviceManager();
+ virtual ~LinuxDeviceManager();
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+
+ private:
+ virtual bool GetAudioDevices(bool input, std::vector<Device>* devs);
+ rtc::SoundSystemHandle sound_system_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_LINUXDEVICEMANAGER_H_
diff --git a/talk/media/devices/macdeviceinfo.cc b/talk/media/devices/macdeviceinfo.cc
new file mode 100644
index 0000000000..f34932d0e2
--- /dev/null
+++ b/talk/media/devices/macdeviceinfo.cc
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2012 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/deviceinfo.h"
+
+namespace cricket {
+
+bool GetUsbId(const Device& device, std::string* usb_id) {
+ // Both PID and VID are 4 characters.
+ const int id_size = 4;
+ if (device.id.size() < 2 * id_size) {
+ return false;
+ }
+
+ // The last characters of device id is a concatenation of VID and then PID.
+ const size_t vid_location = device.id.size() - 2 * id_size;
+ std::string id_vendor = device.id.substr(vid_location, id_size);
+ const size_t pid_location = device.id.size() - id_size;
+ std::string id_product = device.id.substr(pid_location, id_size);
+
+ usb_id->clear();
+ usb_id->append(id_vendor);
+ usb_id->append(":");
+ usb_id->append(id_product);
+ return true;
+}
+
+bool GetUsbVersion(const Device& device, std::string* usb_version) {
+ return false;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/macdevicemanager.cc b/talk/media/devices/macdevicemanager.cc
new file mode 100644
index 0000000000..8f777b42da
--- /dev/null
+++ b/talk/media/devices/macdevicemanager.cc
@@ -0,0 +1,196 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/macdevicemanager.h"
+
+#include <CoreAudio/CoreAudio.h>
+#include <QuickTime/QuickTime.h>
+
+#include "talk/media/base/mediacommon.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+
+class DeviceWatcherImpl;
+
+namespace cricket {
+
+DeviceManagerInterface* DeviceManagerFactory::Create() {
+ return new MacDeviceManager();
+}
+
+class MacDeviceWatcher : public DeviceWatcher {
+ public:
+ explicit MacDeviceWatcher(DeviceManagerInterface* dm);
+ virtual ~MacDeviceWatcher();
+ virtual bool Start();
+ virtual void Stop();
+
+ private:
+ DeviceManagerInterface* manager_;
+ DeviceWatcherImpl* impl_;
+};
+
+static const char* kFilteredAudioDevicesName[] = {
+ NULL,
+};
+// TODO(tommyw): Try to get hold of a copy of Final Cut to understand why we
+// crash while scanning their components on OS X.
+static const char* const kFilteredVideoDevicesName[] = {
+ "DVCPRO HD", // Final cut
+ "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent
+ NULL,
+};
+static const UInt32 kAudioDeviceNameLength = 64;
+// Obj-C functions defined in macdevicemanagermm.mm
+// TODO(ronghuawu): have a shared header for these function defines.
+extern DeviceWatcherImpl* CreateDeviceWatcherCallback(
+ DeviceManagerInterface* dm);
+extern void ReleaseDeviceWatcherCallback(DeviceWatcherImpl* impl);
+extern bool GetAVFoundationVideoDevices(std::vector<Device>* out);
+static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out);
+static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out);
+
+MacDeviceManager::MacDeviceManager() {
+ set_watcher(new MacDeviceWatcher(this));
+}
+
+MacDeviceManager::~MacDeviceManager() {
+}
+
+bool MacDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
+ devices->clear();
+ if (!GetAVFoundationVideoDevices(devices)) {
+ return false;
+ }
+ return FilterDevices(devices, kFilteredVideoDevicesName);
+}
+
+bool MacDeviceManager::GetAudioDevices(bool input,
+ std::vector<Device>* devs) {
+ devs->clear();
+ std::vector<AudioDeviceID> dev_ids;
+ bool ret = GetAudioDeviceIDs(input, &dev_ids);
+ if (!ret) {
+ return false;
+ }
+ for (size_t i = 0; i < dev_ids.size(); ++i) {
+ std::string name;
+ if (GetAudioDeviceName(dev_ids[i], input, &name)) {
+ devs->push_back(Device(name, dev_ids[i]));
+ }
+ }
+ return FilterDevices(devs, kFilteredAudioDevicesName);
+}
+
+static bool GetAudioDeviceIDs(bool input,
+ std::vector<AudioDeviceID>* out_dev_ids) {
+ UInt32 propsize;
+ OSErr err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &propsize, NULL);
+ if (0 != err) {
+ LOG(LS_ERROR) << "Couldn't get information about property, "
+ << "so no device list acquired.";
+ return false;
+ }
+
+ size_t num_devices = propsize / sizeof(AudioDeviceID);
+ rtc::scoped_ptr<AudioDeviceID[]> device_ids(
+ new AudioDeviceID[num_devices]);
+
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &propsize, device_ids.get());
+ if (0 != err) {
+ LOG(LS_ERROR) << "Failed to get device ids, "
+ << "so no device listing acquired.";
+ return false;
+ }
+
+ for (size_t i = 0; i < num_devices; ++i) {
+ AudioDeviceID an_id = device_ids[i];
+ // find out the number of channels for this direction
+ // (input/output) on this device -
+ // we'll ignore anything with no channels.
+ err = AudioDeviceGetPropertyInfo(an_id, 0, input,
+ kAudioDevicePropertyStreams,
+ &propsize, NULL);
+ if (0 == err) {
+ unsigned num_channels = propsize / sizeof(AudioStreamID);
+ if (0 < num_channels) {
+ out_dev_ids->push_back(an_id);
+ }
+ } else {
+ LOG(LS_ERROR) << "No property info for stream property for device id "
+ << an_id << "(is_input == " << input
+ << "), so not including it in the list.";
+ }
+ }
+
+ return true;
+}
+
+static bool GetAudioDeviceName(AudioDeviceID id,
+ bool input,
+ std::string* out_name) {
+ UInt32 nameLength = kAudioDeviceNameLength;
+ char name[kAudioDeviceNameLength + 1];
+ OSErr err = AudioDeviceGetProperty(id, 0, input,
+ kAudioDevicePropertyDeviceName,
+ &nameLength, name);
+ if (0 != err) {
+ LOG(LS_ERROR) << "No name acquired for device id " << id;
+ return false;
+ }
+
+ *out_name = name;
+ return true;
+}
+
+MacDeviceWatcher::MacDeviceWatcher(DeviceManagerInterface* manager)
+ : DeviceWatcher(manager),
+ manager_(manager),
+ impl_(NULL) {
+}
+
+MacDeviceWatcher::~MacDeviceWatcher() {
+}
+
+bool MacDeviceWatcher::Start() {
+ if (!impl_) {
+ impl_ = CreateDeviceWatcherCallback(manager_);
+ }
+ return impl_ != NULL;
+}
+
+void MacDeviceWatcher::Stop() {
+ if (impl_) {
+ ReleaseDeviceWatcherCallback(impl_);
+ impl_ = NULL;
+ }
+}
+
+}; // namespace cricket
diff --git a/talk/media/devices/macdevicemanager.h b/talk/media/devices/macdevicemanager.h
new file mode 100644
index 0000000000..82e62f9698
--- /dev/null
+++ b/talk/media/devices/macdevicemanager.h
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_MACDEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_MACDEVICEMANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/devices/devicemanager.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/stringencode.h"
+
+namespace cricket {
+
+class DeviceWatcher;
+
+class MacDeviceManager : public DeviceManager {
+ public:
+ MacDeviceManager();
+ virtual ~MacDeviceManager();
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+
+ private:
+ virtual bool GetAudioDevices(bool input, std::vector<Device>* devs);
+ bool FilterDevice(const Device& d);
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_MACDEVICEMANAGER_H_
diff --git a/talk/media/devices/macdevicemanagermm.mm b/talk/media/devices/macdevicemanagermm.mm
new file mode 100644
index 0000000000..ed5934d57c
--- /dev/null
+++ b/talk/media/devices/macdevicemanagermm.mm
@@ -0,0 +1,192 @@
+/*
+ * libjingle
+ * Copyright 2010 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// support GCC compiler
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#include "talk/media/devices/devicemanager.h"
+
+#import <assert.h>
+#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+ #import <AVFoundation/AVFoundation.h>
+#endif
+#endif
+#import <QTKit/QTKit.h>
+
+#include "webrtc/base/logging.h"
+
+@interface DeviceWatcherImpl : NSObject {
+ @private
+ cricket::DeviceManagerInterface* manager_;
+}
+- (id)init:(cricket::DeviceManagerInterface*)manager;
+- (void)onDevicesChanged:(NSNotification*)notification;
+@end
+
+@implementation DeviceWatcherImpl
+- (id)init:(cricket::DeviceManagerInterface*)manager {
+ if ((self = [super init])) {
+ assert(manager != NULL);
+ manager_ = manager;
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(onDevicesChanged:)
+ name:QTCaptureDeviceWasConnectedNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(onDevicesChanged:)
+ name:QTCaptureDeviceWasDisconnectedNotification
+ object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+#if !__has_feature(objc_arc)
+ [super dealloc];
+#endif
+}
+- (void)onDevicesChanged:(NSNotification*)notification {
+ manager_->SignalDevicesChange();
+}
+@end
+
+namespace cricket {
+
+DeviceWatcherImpl* CreateDeviceWatcherCallback(
+ DeviceManagerInterface* manager) {
+ DeviceWatcherImpl* impl;
+#if !__has_feature(objc_arc)
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+#else
+ @autoreleasepool
+#endif
+ { impl = [[DeviceWatcherImpl alloc] init:manager]; }
+#if !__has_feature(objc_arc)
+ [pool drain];
+#endif
+ return impl;
+}
+
+void ReleaseDeviceWatcherCallback(DeviceWatcherImpl* watcher) {
+#if !__has_feature(objc_arc)
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ [watcher release];
+ [pool drain];
+#endif
+}
+
+bool GetQTKitVideoDevices(std::vector<Device>* devices) {
+#if !__has_feature(objc_arc)
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+#else
+ @autoreleasepool
+#endif
+ {
+ NSArray* qt_capture_devices =
+ [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo];
+ NSUInteger count = [qt_capture_devices count];
+ LOG(LS_INFO) << count << " capture device(s) found:";
+ for (QTCaptureDevice* qt_capture_device in qt_capture_devices) {
+ static NSString* const kFormat = @"localizedDisplayName: \"%@\", "
+ @"modelUniqueID: \"%@\", uniqueID \"%@\", isConnected: %d, "
+ @"isOpen: %d, isInUseByAnotherApplication: %d";
+ NSString* info = [NSString
+ stringWithFormat:kFormat,
+ [qt_capture_device localizedDisplayName],
+ [qt_capture_device modelUniqueID],
+ [qt_capture_device uniqueID],
+ [qt_capture_device isConnected],
+ [qt_capture_device isOpen],
+ [qt_capture_device isInUseByAnotherApplication]];
+ LOG(LS_INFO) << [info UTF8String];
+
+ std::string name([[qt_capture_device localizedDisplayName] UTF8String]);
+ devices->push_back(
+ Device(name, [[qt_capture_device uniqueID] UTF8String]));
+ }
+ }
+#if !__has_feature(objc_arc)
+ [pool drain];
+#endif
+ return true;
+}
+
+bool GetAVFoundationVideoDevices(std::vector<Device>* devices) {
+#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >=1070
+ if (![AVCaptureDevice class]) {
+ // Fallback to using QTKit if AVFoundation is not available
+ return GetQTKitVideoDevices(devices);
+ }
+#if !__has_feature(objc_arc)
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+#else
+ @autoreleasepool
+#endif
+ {
+ NSArray* capture_devices = [AVCaptureDevice devices];
+ LOG(LS_INFO) << [capture_devices count] << " capture device(s) found:";
+ for (AVCaptureDevice* capture_device in capture_devices) {
+ if ([capture_device hasMediaType:AVMediaTypeVideo] ||
+ [capture_device hasMediaType:AVMediaTypeMuxed]) {
+ static NSString* const kFormat = @"localizedName: \"%@\", "
+ @"modelID: \"%@\", uniqueID \"%@\", isConnected: %d, "
+ @"isInUseByAnotherApplication: %d";
+ NSString* info = [NSString
+ stringWithFormat:kFormat,
+ [capture_device localizedName],
+ [capture_device modelID],
+ [capture_device uniqueID],
+ [capture_device isConnected],
+ [capture_device isInUseByAnotherApplication]];
+ LOG(LS_INFO) << [info UTF8String];
+
+ std::string name([[capture_device localizedName] UTF8String]);
+ devices->push_back(
+ Device(name, [[capture_device uniqueID] UTF8String]));
+ }
+ }
+ }
+#if !__has_feature(objc_arc)
+ [pool drain];
+#endif
+ return true;
+#else // __MAC_OS_X_VERSION_MAX_ALLOWED >=1070
+ return GetQTKitVideoDevices(devices);
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED >=1070
+#else // __MAC_OS_X_VERSION_MAX_ALLOWED
+ return GetQTKitVideoDevices(devices);
+#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/mobiledevicemanager.cc b/talk/media/devices/mobiledevicemanager.cc
new file mode 100644
index 0000000000..2a886a36d4
--- /dev/null
+++ b/talk/media/devices/mobiledevicemanager.cc
@@ -0,0 +1,77 @@
+/*
+ * libjingle
+ * Copyright 2013 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/devicemanager.h"
+#include "webrtc/base/arraysize.h"
+#include "webrtc/modules/video_capture/include/video_capture_factory.h"
+
+namespace cricket {
+
+class MobileDeviceManager : public DeviceManager {
+ public:
+ MobileDeviceManager();
+ virtual ~MobileDeviceManager();
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+};
+
+MobileDeviceManager::MobileDeviceManager() {
+ // We don't expect available devices to change on Android/iOS, so use a
+ // do-nothing watcher.
+ set_watcher(new DeviceWatcher(this));
+}
+
+MobileDeviceManager::~MobileDeviceManager() {}
+
+bool MobileDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devs) {
+ devs->clear();
+ rtc::scoped_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
+ webrtc::VideoCaptureFactory::CreateDeviceInfo(0));
+ if (!info)
+ return false;
+
+ uint32_t num_cams = info->NumberOfDevices();
+ char id[256];
+ char name[256];
+ for (uint32_t i = 0; i < num_cams; ++i) {
+ if (info->GetDeviceName(i, name, arraysize(name), id, arraysize(id)))
+ continue;
+ devs->push_back(Device(name, id));
+ }
+ return true;
+}
+
+DeviceManagerInterface* DeviceManagerFactory::Create() {
+ return new MobileDeviceManager();
+}
+
+bool GetUsbId(const Device& device, std::string* usb_id) { return false; }
+
+bool GetUsbVersion(const Device& device, std::string* usb_version) {
+ return false;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/v4llookup.cc b/talk/media/devices/v4llookup.cc
new file mode 100644
index 0000000000..20b534c82d
--- /dev/null
+++ b/talk/media/devices/v4llookup.cc
@@ -0,0 +1,92 @@
+/*
+ * libjingle
+ * Copyright 2009 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Author: lexnikitin@google.com (Alexey Nikitin)
+ *
+ * V4LLookup provides basic functionality to work with V2L2 devices in Linux
+ * The functionality is implemented as a class with virtual methods for
+ * the purpose of unit testing.
+ */
+#include "talk/media/devices/v4llookup.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "webrtc/base/logging.h"
+
+namespace cricket {
+
+V4LLookup *V4LLookup::v4l_lookup_ = NULL;
+
+bool V4LLookup::CheckIsV4L2Device(const std::string& device_path) {
+ // check device major/minor numbers are in the range for video devices.
+ struct stat s;
+
+ if (lstat(device_path.c_str(), &s) != 0 || !S_ISCHR(s.st_mode)) return false;
+
+ int video_fd = -1;
+ bool is_v4l2 = false;
+
+ // check major/minur device numbers are in range for video device
+ if (major(s.st_rdev) == 81) {
+ dev_t num = minor(s.st_rdev);
+ if (num <= 63) {
+ video_fd = ::open(device_path.c_str(), O_RDONLY | O_NONBLOCK);
+ if ((video_fd >= 0) || (errno == EBUSY)) {
+ ::v4l2_capability video_caps;
+ memset(&video_caps, 0, sizeof(video_caps));
+
+ if ((errno == EBUSY) ||
+ (::ioctl(video_fd, VIDIOC_QUERYCAP, &video_caps) >= 0 &&
+ (video_caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
+ LOG(LS_INFO) << "Found V4L2 capture device " << device_path;
+
+ is_v4l2 = true;
+ } else {
+ LOG_ERRNO(LS_ERROR) << "VIDIOC_QUERYCAP failed for " << device_path;
+ }
+ } else {
+ LOG_ERRNO(LS_ERROR) << "Failed to open " << device_path;
+ }
+ }
+ }
+
+ if (video_fd >= 0)
+ ::close(video_fd);
+
+ return is_v4l2;
+}
+
+}; // namespace cricket
diff --git a/talk/media/devices/v4llookup.h b/talk/media/devices/v4llookup.h
new file mode 100644
index 0000000000..1bed90b650
--- /dev/null
+++ b/talk/media/devices/v4llookup.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2009 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Author: lexnikitin@google.com (Alexey Nikitin)
+ *
+ * V4LLookup provides basic functionality to work with V2L2 devices in Linux
+ * The functionality is implemented as a class with virtual methods for
+ * the purpose of unit testing.
+ */
+#ifndef TALK_MEDIA_DEVICES_V4LLOOKUP_H_
+#define TALK_MEDIA_DEVICES_V4LLOOKUP_H_
+
+#include <string>
+
+#ifdef LINUX
+namespace cricket {
+class V4LLookup {
+ public:
+ virtual ~V4LLookup() {}
+
+ static bool IsV4L2Device(const std::string& device_path) {
+ return GetV4LLookup()->CheckIsV4L2Device(device_path);
+ }
+
+ static void SetV4LLookup(V4LLookup* v4l_lookup) {
+ v4l_lookup_ = v4l_lookup;
+ }
+
+ static V4LLookup* GetV4LLookup() {
+ if (!v4l_lookup_) {
+ v4l_lookup_ = new V4LLookup();
+ }
+ return v4l_lookup_;
+ }
+
+ protected:
+ static V4LLookup* v4l_lookup_;
+ // Making virtual so it is easier to mock
+ virtual bool CheckIsV4L2Device(const std::string& device_path);
+};
+
+} // namespace cricket
+
+#endif // LINUX
+#endif // TALK_MEDIA_DEVICES_V4LLOOKUP_H_
diff --git a/talk/media/devices/videorendererfactory.h b/talk/media/devices/videorendererfactory.h
new file mode 100644
index 0000000000..416f05b297
--- /dev/null
+++ b/talk/media/devices/videorendererfactory.h
@@ -0,0 +1,69 @@
+/*
+ * libjingle
+ * Copyright 2010 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// A factory to create a GUI video renderer.
+
+#ifndef TALK_MEDIA_DEVICES_VIDEORENDERERFACTORY_H_
+#define TALK_MEDIA_DEVICES_VIDEORENDERERFACTORY_H_
+
+#include "talk/media/base/videorenderer.h"
+#if defined(LINUX) && defined(HAVE_GTK)
+#include "talk/media/devices/gtkvideorenderer.h"
+#elif defined(OSX) && !defined(CARBON_DEPRECATED)
+#include "talk/media/devices/carbonvideorenderer.h"
+#elif defined(WIN32)
+#include "talk/media/devices/gdivideorenderer.h"
+#endif
+
+namespace cricket {
+
+class VideoRendererFactory {
+ public:
+ static VideoRenderer* CreateGuiVideoRenderer(int x, int y) {
+ #if defined(LINUX) && defined(HAVE_GTK)
+ return new GtkVideoRenderer(x, y);
+ #elif defined(OSX) && !defined(CARBON_DEPRECATED)
+ CarbonVideoRenderer* renderer = new CarbonVideoRenderer(x, y);
+ // Needs to be initialized on the main thread.
+ if (renderer->Initialize()) {
+ return renderer;
+ } else {
+ delete renderer;
+ return NULL;
+ }
+ #elif defined(WIN32)
+ return new GdiVideoRenderer(x, y);
+ #else
+ return NULL;
+ #endif
+ }
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_VIDEORENDERERFACTORY_H_
diff --git a/talk/media/devices/win32deviceinfo.cc b/talk/media/devices/win32deviceinfo.cc
new file mode 100644
index 0000000000..61a7759036
--- /dev/null
+++ b/talk/media/devices/win32deviceinfo.cc
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2012 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/deviceinfo.h"
+
+namespace cricket {
+
+bool GetUsbId(const Device& device, std::string* usb_id) {
+ // Both PID and VID are 4 characters.
+ const int id_size = 4;
+ const char vid[] = "vid_"; // Also contains '\0'.
+ const size_t vid_location = device.id.find(vid);
+ if (vid_location == std::string::npos ||
+ vid_location + sizeof(vid) - 1 + id_size > device.id.size()) {
+ return false;
+ }
+ const char pid[] = "pid_";
+ const size_t pid_location = device.id.find(pid);
+ if (pid_location == std::string::npos ||
+ pid_location + sizeof(pid) - 1 + id_size > device.id.size()) {
+ return false;
+ }
+ std::string id_vendor = device.id.substr(vid_location + sizeof(vid) - 1,
+ id_size);
+ std::string id_product = device.id.substr(pid_location + sizeof(pid) -1,
+ id_size);
+ usb_id->clear();
+ usb_id->append(id_vendor);
+ usb_id->append(":");
+ usb_id->append(id_product);
+ return true;
+}
+
+bool GetUsbVersion(const Device& device, std::string* usb_version) {
+ return false;
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/win32devicemanager.cc b/talk/media/devices/win32devicemanager.cc
new file mode 100644
index 0000000000..1b9e9d86f6
--- /dev/null
+++ b/talk/media/devices/win32devicemanager.cc
@@ -0,0 +1,414 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/win32devicemanager.h"
+
+#include <atlbase.h>
+#include <dbt.h>
+#include <strmif.h> // must come before ks.h
+#include <ks.h>
+#include <ksmedia.h>
+#include <mmdeviceapi.h>
+#include <mmsystem.h>
+#include <functiondiscoverykeys_devpkey.h>
+#include <uuids.h>
+
+// PKEY_AudioEndpoint_GUID isn't included in uuid.lib and we don't want
+// to define INITGUID in order to define all the uuids in this object file
+// as it will conflict with uuid.lib (multiply defined symbols).
+// So our workaround is to define this one missing symbol here manually.
+// See: https://code.google.com/p/webrtc/issues/detail?id=3996
+EXTERN_C const PROPERTYKEY PKEY_AudioEndpoint_GUID = { {
+ 0x1da5d803, 0xd492, 0x4edd, {
+ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e
+ } }, 4
+};
+
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/win32.h" // ToUtf8
+#include "webrtc/base/win32window.h"
+#include "talk/media/base/mediacommon.h"
+#ifdef HAVE_LOGITECH_HEADERS
+#include "third_party/logitech/files/logitechquickcam.h"
+#endif
+
+namespace cricket {
+
+DeviceManagerInterface* DeviceManagerFactory::Create() {
+ return new Win32DeviceManager();
+}
+
+class Win32DeviceWatcher
+ : public DeviceWatcher,
+ public rtc::Win32Window {
+ public:
+ explicit Win32DeviceWatcher(Win32DeviceManager* dm);
+ virtual ~Win32DeviceWatcher();
+ virtual bool Start();
+ virtual void Stop();
+
+ private:
+ HDEVNOTIFY Register(REFGUID guid);
+ void Unregister(HDEVNOTIFY notify);
+ virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
+
+ Win32DeviceManager* manager_;
+ HDEVNOTIFY audio_notify_;
+ HDEVNOTIFY video_notify_;
+};
+
+static const char* kFilteredAudioDevicesName[] = {
+ NULL,
+};
+static const char* const kFilteredVideoDevicesName[] = {
+ "Asus virtual Camera", // Bad Asus desktop virtual cam
+ "Bluetooth Video", // Bad Sony viao bluetooth sharing driver
+ NULL,
+};
+static const wchar_t kFriendlyName[] = L"FriendlyName";
+static const wchar_t kDevicePath[] = L"DevicePath";
+static const char kUsbDevicePathPrefix[] = "\\\\?\\usb";
+static bool GetDevices(const CLSID& catid, std::vector<Device>* out);
+static bool GetCoreAudioDevices(bool input, std::vector<Device>* devs);
+static bool GetWaveDevices(bool input, std::vector<Device>* devs);
+
+Win32DeviceManager::Win32DeviceManager()
+ : need_couninitialize_(false) {
+ set_watcher(new Win32DeviceWatcher(this));
+}
+
+Win32DeviceManager::~Win32DeviceManager() {
+ if (initialized()) {
+ Terminate();
+ }
+}
+
+bool Win32DeviceManager::Init() {
+ if (!initialized()) {
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ need_couninitialize_ = SUCCEEDED(hr);
+ if (FAILED(hr)) {
+ LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr;
+ if (hr != RPC_E_CHANGED_MODE) {
+ return false;
+ }
+ }
+ if (!watcher()->Start()) {
+ return false;
+ }
+ set_initialized(true);
+ }
+ return true;
+}
+
+void Win32DeviceManager::Terminate() {
+ if (initialized()) {
+ watcher()->Stop();
+ if (need_couninitialize_) {
+ CoUninitialize();
+ need_couninitialize_ = false;
+ }
+ set_initialized(false);
+ }
+}
+
+bool Win32DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
+ bool ret = false;
+ // If there are multiple capture devices, we want the first USB one.
+ // This avoids issues with defaulting to virtual cameras or grabber cards.
+ std::vector<Device> devices;
+ ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
+ if (ret) {
+ *device = devices[0];
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix,
+ ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) {
+ *device = devices[i];
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+bool Win32DeviceManager::GetAudioDevices(bool input,
+ std::vector<Device>* devs) {
+ devs->clear();
+
+ if (rtc::IsWindowsVistaOrLater()) {
+ if (!GetCoreAudioDevices(input, devs))
+ return false;
+ } else {
+ if (!GetWaveDevices(input, devs))
+ return false;
+ }
+ return FilterDevices(devs, kFilteredAudioDevicesName);
+}
+
+bool Win32DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
+ devices->clear();
+ if (!GetDevices(CLSID_VideoInputDeviceCategory, devices)) {
+ return false;
+ }
+ return FilterDevices(devices, kFilteredVideoDevicesName);
+}
+
+bool GetDevices(const CLSID& catid, std::vector<Device>* devices) {
+ HRESULT hr;
+
+ // CComPtr is a scoped pointer that will be auto released when going
+ // out of scope. CoUninitialize must not be called before the
+ // release.
+ CComPtr<ICreateDevEnum> sys_dev_enum;
+ CComPtr<IEnumMoniker> cam_enum;
+ if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) ||
+ FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) {
+ LOG(LS_ERROR) << "Failed to create device enumerator, hr=" << hr;
+ return false;
+ }
+
+ // Only enum devices if CreateClassEnumerator returns S_OK. If there are no
+ // devices available, S_FALSE will be returned, but enumMk will be NULL.
+ if (hr == S_OK) {
+ CComPtr<IMoniker> mk;
+ while (cam_enum->Next(1, &mk, NULL) == S_OK) {
+#ifdef HAVE_LOGITECH_HEADERS
+ // Initialize Logitech device if applicable
+ MaybeLogitechDeviceReset(mk);
+#endif
+ CComPtr<IPropertyBag> bag;
+ if (SUCCEEDED(mk->BindToStorage(NULL, NULL,
+ __uuidof(bag), reinterpret_cast<void**>(&bag)))) {
+ CComVariant name, path;
+ std::string name_str, path_str;
+ if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) &&
+ name.vt == VT_BSTR) {
+ name_str = rtc::ToUtf8(name.bstrVal);
+ // Get the device id if one exists.
+ if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) &&
+ path.vt == VT_BSTR) {
+ path_str = rtc::ToUtf8(path.bstrVal);
+ }
+
+ devices->push_back(Device(name_str, path_str));
+ }
+ }
+ mk = NULL;
+ }
+ }
+
+ return true;
+}
+
+HRESULT GetStringProp(IPropertyStore* bag, PROPERTYKEY key, std::string* out) {
+ out->clear();
+ PROPVARIANT var;
+ PropVariantInit(&var);
+
+ HRESULT hr = bag->GetValue(key, &var);
+ if (SUCCEEDED(hr)) {
+ if (var.pwszVal)
+ *out = rtc::ToUtf8(var.pwszVal);
+ else
+ hr = E_FAIL;
+ }
+
+ PropVariantClear(&var);
+ return hr;
+}
+
+// Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx
+HRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) {
+ CComPtr<IPropertyStore> props;
+
+ HRESULT hr = device->OpenPropertyStore(STGM_READ, &props);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Get the endpoint's name and id.
+ std::string name, guid;
+ hr = GetStringProp(props, PKEY_Device_FriendlyName, &name);
+ if (SUCCEEDED(hr)) {
+ hr = GetStringProp(props, PKEY_AudioEndpoint_GUID, &guid);
+
+ if (SUCCEEDED(hr)) {
+ out->name = name;
+ out->id = guid;
+ }
+ }
+ return hr;
+}
+
+bool GetCoreAudioDevices(
+ bool input, std::vector<Device>* devs) {
+ HRESULT hr = S_OK;
+ CComPtr<IMMDeviceEnumerator> enumerator;
+
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator));
+ if (SUCCEEDED(hr)) {
+ CComPtr<IMMDeviceCollection> devices;
+ hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender),
+ DEVICE_STATE_ACTIVE, &devices);
+ if (SUCCEEDED(hr)) {
+ unsigned int count;
+ hr = devices->GetCount(&count);
+
+ if (SUCCEEDED(hr)) {
+ for (unsigned int i = 0; i < count; i++) {
+ CComPtr<IMMDevice> device;
+
+ // Get pointer to endpoint number i.
+ hr = devices->Item(i, &device);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ Device dev;
+ hr = CricketDeviceFromImmDevice(device, &dev);
+ if (SUCCEEDED(hr)) {
+ devs->push_back(dev);
+ } else {
+ LOG(LS_WARNING) << "Unable to query IMM Device, skipping. HR="
+ << hr;
+ hr = S_FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr)) {
+ LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr;
+ return false;
+ }
+ return true;
+}
+
+bool GetWaveDevices(bool input, std::vector<Device>* devs) {
+ // Note, we don't use the System Device Enumerator interface here since it
+ // adds lots of pseudo-devices to the list, such as DirectSound and Wave
+ // variants of the same device.
+ if (input) {
+ int num_devs = waveInGetNumDevs();
+ for (int i = 0; i < num_devs; ++i) {
+ WAVEINCAPS caps;
+ if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
+ caps.wChannels > 0) {
+ devs->push_back(Device(rtc::ToUtf8(caps.szPname),
+ rtc::ToString(i)));
+ }
+ }
+ } else {
+ int num_devs = waveOutGetNumDevs();
+ for (int i = 0; i < num_devs; ++i) {
+ WAVEOUTCAPS caps;
+ if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
+ caps.wChannels > 0) {
+ devs->push_back(Device(rtc::ToUtf8(caps.szPname), i));
+ }
+ }
+ }
+ return true;
+}
+
+Win32DeviceWatcher::Win32DeviceWatcher(Win32DeviceManager* manager)
+ : DeviceWatcher(manager),
+ manager_(manager),
+ audio_notify_(NULL),
+ video_notify_(NULL) {
+}
+
+Win32DeviceWatcher::~Win32DeviceWatcher() {
+}
+
+bool Win32DeviceWatcher::Start() {
+ if (!Create(NULL, _T("libjingle Win32DeviceWatcher Window"),
+ 0, 0, 0, 0, 0, 0)) {
+ return false;
+ }
+
+ audio_notify_ = Register(KSCATEGORY_AUDIO);
+ if (!audio_notify_) {
+ Stop();
+ return false;
+ }
+
+ video_notify_ = Register(KSCATEGORY_VIDEO);
+ if (!video_notify_) {
+ Stop();
+ return false;
+ }
+
+ return true;
+}
+
+void Win32DeviceWatcher::Stop() {
+ UnregisterDeviceNotification(video_notify_);
+ video_notify_ = NULL;
+ UnregisterDeviceNotification(audio_notify_);
+ audio_notify_ = NULL;
+ Destroy();
+}
+
+HDEVNOTIFY Win32DeviceWatcher::Register(REFGUID guid) {
+ DEV_BROADCAST_DEVICEINTERFACE dbdi;
+ dbdi.dbcc_size = sizeof(dbdi);
+ dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ dbdi.dbcc_classguid = guid;
+ dbdi.dbcc_name[0] = '\0';
+ return RegisterDeviceNotification(handle(), &dbdi,
+ DEVICE_NOTIFY_WINDOW_HANDLE);
+}
+
+void Win32DeviceWatcher::Unregister(HDEVNOTIFY handle) {
+ UnregisterDeviceNotification(handle);
+}
+
+bool Win32DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+ LRESULT& result) {
+ if (uMsg == WM_DEVICECHANGE) {
+ if (wParam == DBT_DEVICEARRIVAL ||
+ wParam == DBT_DEVICEREMOVECOMPLETE) {
+ DEV_BROADCAST_DEVICEINTERFACE* dbdi =
+ reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam);
+ if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO ||
+ dbdi->dbcc_classguid == KSCATEGORY_VIDEO) {
+ manager_->SignalDevicesChange();
+ }
+ }
+ result = 0;
+ return true;
+ }
+
+ return false;
+}
+
+}; // namespace cricket
diff --git a/talk/media/devices/win32devicemanager.h b/talk/media/devices/win32devicemanager.h
new file mode 100644
index 0000000000..5f8ba8333a
--- /dev/null
+++ b/talk/media/devices/win32devicemanager.h
@@ -0,0 +1,60 @@
+/*
+ * libjingle
+ * Copyright 2004 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_WIN32DEVICEMANAGER_H_
+#define TALK_MEDIA_DEVICES_WIN32DEVICEMANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/devices/devicemanager.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/stringencode.h"
+
+namespace cricket {
+
+class Win32DeviceManager : public DeviceManager {
+ public:
+ Win32DeviceManager();
+ virtual ~Win32DeviceManager();
+
+ // Initialization
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual bool GetVideoCaptureDevices(std::vector<Device>* devs);
+
+ private:
+ virtual bool GetAudioDevices(bool input, std::vector<Device>* devs);
+ virtual bool GetDefaultVideoCaptureDevice(Device* device);
+
+ bool need_couninitialize_;
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_WIN32DEVICEMANAGER_H_
diff --git a/talk/media/devices/yuvframescapturer.cc b/talk/media/devices/yuvframescapturer.cc
new file mode 100644
index 0000000000..a60ad0270a
--- /dev/null
+++ b/talk/media/devices/yuvframescapturer.cc
@@ -0,0 +1,199 @@
+/*
+ * libjingle
+ * Copyright 2004--2014 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/media/devices/yuvframescapturer.h"
+
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
+
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace cricket {
+///////////////////////////////////////////////////////////////////////
+// Definition of private class YuvFramesThread that periodically generates
+// frames.
+///////////////////////////////////////////////////////////////////////
+class YuvFramesCapturer::YuvFramesThread
+ : public rtc::Thread, public rtc::MessageHandler {
+ public:
+ explicit YuvFramesThread(YuvFramesCapturer* capturer)
+ : capturer_(capturer),
+ finished_(false) {
+ }
+
+ virtual ~YuvFramesThread() {
+ Stop();
+ }
+
+ // Override virtual method of parent Thread. Context: Worker Thread.
+ virtual void Run() {
+ // Read the first frame and start the message pump. The pump runs until
+ // Stop() is called externally or Quit() is called by OnMessage().
+ int waiting_time_ms = 0;
+ if (capturer_) {
+ capturer_->ReadFrame(true);
+ PostDelayed(waiting_time_ms, this);
+ Thread::Run();
+ }
+
+ rtc::CritScope cs(&crit_);
+ finished_ = true;
+ }
+
+ // Override virtual method of parent MessageHandler. Context: Worker Thread.
+ virtual void OnMessage(rtc::Message* /*pmsg*/) {
+ int waiting_time_ms = 0;
+ if (capturer_) {
+ capturer_->ReadFrame(false);
+ PostDelayed(waiting_time_ms, this);
+ } else {
+ Quit();
+ }
+ }
+
+ // Check if Run() is finished.
+ bool Finished() const {
+ rtc::CritScope cs(&crit_);
+ return finished_;
+ }
+
+ private:
+ YuvFramesCapturer* capturer_;
+ mutable rtc::CriticalSection crit_;
+ bool finished_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(YuvFramesThread);
+};
+
+/////////////////////////////////////////////////////////////////////
+// Implementation of class YuvFramesCapturer.
+/////////////////////////////////////////////////////////////////////
+
+const char* YuvFramesCapturer::kYuvFrameDeviceName = "YuvFramesGenerator";
+
+// TODO(shaowei): allow width_ and height_ to be configurable.
+YuvFramesCapturer::YuvFramesCapturer()
+ : frames_generator_thread(NULL),
+ width_(640),
+ height_(480),
+ frame_index_(0),
+ barcode_interval_(1) {
+}
+
+YuvFramesCapturer::~YuvFramesCapturer() {
+ Stop();
+ delete[] static_cast<char*>(captured_frame_.data);
+}
+
+void YuvFramesCapturer::Init() {
+ int size = width_ * height_;
+ int qsize = size / 4;
+ frame_generator_ = new YuvFrameGenerator(width_, height_, true);
+ frame_data_size_ = size + 2 * qsize;
+ captured_frame_.data = new char[frame_data_size_];
+ captured_frame_.fourcc = FOURCC_IYUV;
+ captured_frame_.pixel_height = 1;
+ captured_frame_.pixel_width = 1;
+ captured_frame_.width = width_;
+ captured_frame_.height = height_;
+ captured_frame_.data_size = frame_data_size_;
+
+ // Enumerate the supported formats. We have only one supported format.
+ VideoFormat format(width_, height_, VideoFormat::kMinimumInterval,
+ FOURCC_IYUV);
+ std::vector<VideoFormat> supported;
+ supported.push_back(format);
+ SetSupportedFormats(supported);
+}
+
+CaptureState YuvFramesCapturer::Start(const VideoFormat& capture_format) {
+ if (IsRunning()) {
+ LOG(LS_ERROR) << "Yuv Frame Generator is already running";
+ return CS_FAILED;
+ }
+ SetCaptureFormat(&capture_format);
+
+ barcode_reference_timestamp_millis_ =
+ static_cast<int64_t>(rtc::Time()) * 1000;
+ // Create a thread to generate frames.
+ frames_generator_thread = new YuvFramesThread(this);
+ bool ret = frames_generator_thread->Start();
+ if (ret) {
+ LOG(LS_INFO) << "Yuv Frame Generator started";
+ return CS_RUNNING;
+ } else {
+ LOG(LS_ERROR) << "Yuv Frame Generator failed to start";
+ return CS_FAILED;
+ }
+}
+
+bool YuvFramesCapturer::IsRunning() {
+ return frames_generator_thread && !frames_generator_thread->Finished();
+}
+
+void YuvFramesCapturer::Stop() {
+ if (frames_generator_thread) {
+ frames_generator_thread->Stop();
+ frames_generator_thread = NULL;
+ LOG(LS_INFO) << "Yuv Frame Generator stopped";
+ }
+ SetCaptureFormat(NULL);
+}
+
+bool YuvFramesCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
+ if (!fourccs) {
+ return false;
+ }
+ fourccs->push_back(GetSupportedFormats()->at(0).fourcc);
+ return true;
+}
+
+// Executed in the context of YuvFramesThread.
+void YuvFramesCapturer::ReadFrame(bool first_frame) {
+ // 1. Signal the previously read frame to downstream.
+ if (!first_frame) {
+ SignalFrameCaptured(this, &captured_frame_);
+ }
+ uint8_t* buffer = new uint8_t[frame_data_size_];
+ frame_generator_->GenerateNextFrame(buffer, GetBarcodeValue());
+ frame_index_++;
+ memmove(captured_frame_.data, buffer, frame_data_size_);
+ delete[] buffer;
+}
+
+int32_t YuvFramesCapturer::GetBarcodeValue() {
+ if (barcode_reference_timestamp_millis_ == -1 ||
+ frame_index_ % barcode_interval_ != 0) {
+ return -1;
+ }
+ int64_t now_millis = static_cast<int64_t>(rtc::Time()) * 1000;
+ return static_cast<int32_t>(now_millis - barcode_reference_timestamp_millis_);
+}
+
+} // namespace cricket
diff --git a/talk/media/devices/yuvframescapturer.h b/talk/media/devices/yuvframescapturer.h
new file mode 100644
index 0000000000..850a8dfb1d
--- /dev/null
+++ b/talk/media/devices/yuvframescapturer.h
@@ -0,0 +1,98 @@
+/*
+ * libjingle
+ * Copyright 2010 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_
+#define TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/media/base/videocapturer.h"
+#include "talk/media/base/yuvframegenerator.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringutils.h"
+
+
+namespace rtc {
+class FileStream;
+}
+
+namespace cricket {
+
+
+// Simulated video capturer that periodically reads frames from a file.
+class YuvFramesCapturer : public VideoCapturer {
+ public:
+ YuvFramesCapturer();
+ YuvFramesCapturer(int width, int height);
+ virtual ~YuvFramesCapturer();
+
+ static const char* kYuvFrameDeviceName;
+ static Device CreateYuvFramesCapturerDevice() {
+ std::stringstream id;
+ id << kYuvFrameDeviceName;
+ return Device(id.str(), id.str());
+ }
+ static bool IsYuvFramesCapturerDevice(const Device& device) {
+ return rtc::starts_with(device.id.c_str(), kYuvFrameDeviceName);
+ }
+
+ void Init();
+ // Override virtual methods of parent class VideoCapturer.
+ virtual CaptureState Start(const VideoFormat& capture_format);
+ virtual void Stop();
+ virtual bool IsRunning();
+ virtual bool IsScreencast() const { return false; }
+
+ protected:
+ // Override virtual methods of parent class VideoCapturer.
+ virtual bool GetPreferredFourccs(std::vector<uint32_t>* fourccs);
+
+ // Read a frame and determine how long to wait for the next frame.
+ void ReadFrame(bool first_frame);
+
+ private:
+ class YuvFramesThread; // Forward declaration, defined in .cc.
+
+ YuvFrameGenerator* frame_generator_;
+ CapturedFrame captured_frame_;
+ YuvFramesThread* frames_generator_thread;
+ int width_;
+ int height_;
+ uint32_t frame_data_size_;
+ uint32_t frame_index_;
+
+ int64_t barcode_reference_timestamp_millis_;
+ int32_t barcode_interval_;
+ int32_t GetBarcodeValue();
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(YuvFramesCapturer);
+};
+
+} // namespace cricket
+
+#endif // TALK_MEDIA_DEVICES_YUVFRAMESCAPTURER_H_