summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-04 23:01:48 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-04 23:01:48 +0000
commit8ea2a8155f7fe2bdd4d6611264bce1b764adae5a (patch)
tree111ca4b6ba6c132667fd3e54faa76ec526838263
parentb68129ac05dfde5133feccdb138f92a0adb8d392 (diff)
downloadchromium_src-8ea2a8155f7fe2bdd4d6611264bce1b764adae5a.zip
chromium_src-8ea2a8155f7fe2bdd4d6611264bce1b764adae5a.tar.gz
chromium_src-8ea2a8155f7fe2bdd4d6611264bce1b764adae5a.tar.bz2
Linux hardware dependent code for Video Capture.
This includes header files for the abstract base classes necessary for lower level parts necessary for video capturing in Chromium as well as an implementation of these classes on Linux. This patch is the first in a series necessary for implementing video capture in the browser process. Tested on Linux. This patch also includes a fake implementation of VideoCaptureDevice to be used with unittests for testing Video Capture classes. Patch by perkl@google.com: http://codereview.chromium.org/6878013/ BUG=none TEST=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84148 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/media.gyp5
-rw-r--r--media/video/capture/fake_video_capture_device.cc126
-rw-r--r--media/video/capture/fake_video_capture_device.h62
-rw-r--r--media/video/capture/linux/video_capture_device_linux.cc412
-rw-r--r--media/video/capture/linux/video_capture_device_linux.h84
-rw-r--r--media/video/capture/video_capture_device.h110
6 files changed, 799 insertions, 0 deletions
diff --git a/media/media.gyp b/media/media.gyp
index 177ea52..13e2b51 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -173,7 +173,12 @@
'filters/rtc_video_decoder.h',
'filters/video_renderer_base.cc',
'filters/video_renderer_base.h',
+ 'video/capture/fake_video_capture_device.cc',
+ 'video/capture/fake_video_capture_device.h',
+ 'video/capture/linux/video_capture_device_linux.cc',
+ 'video/capture/linux/video_capture_device_linux.h',
'video/capture/video_capture.h',
+ 'video/capture/video_capture_device.h',
'video/ffmpeg_video_allocator.cc',
'video/ffmpeg_video_allocator.h',
'video/ffmpeg_video_decode_engine.cc',
diff --git a/media/video/capture/fake_video_capture_device.cc b/media/video/capture/fake_video_capture_device.cc
new file mode 100644
index 0000000..8fce8e4
--- /dev/null
+++ b/media/video/capture/fake_video_capture_device.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/video/capture/fake_video_capture_device.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/stringprintf.h"
+
+namespace media {
+
+static const int kFakeCaptureTimeoutMs = 100;
+enum { kNumberOfFakeDevices = 2 };
+
+void FakeVideoCaptureDevice::GetDeviceNames(Names* const device_names) {
+ // Empty the name list.
+ device_names->erase(device_names->begin(), device_names->end());
+
+ for (int n = 0; n < kNumberOfFakeDevices; n++) {
+ Name name;
+ name.unique_id = StringPrintf("/dev/video%d", n);
+ name.device_name = StringPrintf("fake_device_%d", n);
+ device_names->push_back(name);
+ }
+}
+
+VideoCaptureDevice* FakeVideoCaptureDevice::Create(const Name& device_name) {
+ for (int n = 0; n < kNumberOfFakeDevices; ++n) {
+ std::string possible_id = StringPrintf("/dev/video%d", n);
+ if (device_name.unique_id.compare(possible_id) == 0) {
+ return new FakeVideoCaptureDevice(device_name);
+ }
+ }
+ return NULL;
+}
+
+FakeVideoCaptureDevice::FakeVideoCaptureDevice(const Name& device_name)
+ : device_name_(device_name),
+ state_(kIdle),
+ capture_thread_("CaptureThread") {
+}
+
+FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
+ // Check if the thread is running.
+ // This means that the device have not been DeAllocated properly.
+ DCHECK(!capture_thread_.IsRunning());
+}
+
+void FakeVideoCaptureDevice::Allocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer) {
+ if (state_ != kIdle) {
+ return; // Wrong state.
+ }
+
+ observer_ = observer;
+ Capability current_settings;
+ current_settings.color = kI420;
+ if (width > 320) { // VGA
+ current_settings.width = 640;
+ current_settings.height = 480;
+ current_settings.frame_rate = 30;
+ } else { // QVGA
+ current_settings.width = 320;
+ current_settings.height = 240;
+ current_settings.frame_rate = 30;
+ }
+
+ fake_frame_.reset(new uint8[current_settings.width *
+ current_settings.height * 3 / 2]);
+ memset(fake_frame_.get(), 0, sizeof(fake_frame_.get()));
+
+ state_ = kAllocated;
+ observer_->OnFrameInfo(current_settings);
+}
+
+void FakeVideoCaptureDevice::Start() {
+ if (state_ != kAllocated) {
+ return; // Wrong state.
+ }
+ state_ = kCapturing;
+ capture_thread_.Start();
+ capture_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask));
+}
+
+void FakeVideoCaptureDevice::Stop() {
+ if (state_ != kCapturing) {
+ return; // Wrong state.
+ }
+ capture_thread_.Stop();
+ state_ = kAllocated;
+}
+
+void FakeVideoCaptureDevice::DeAllocate() {
+ if (state_ != kAllocated && state_ != kCapturing) {
+ return; // Wrong state.
+ }
+ capture_thread_.Stop();
+ state_ = kIdle;
+}
+
+const VideoCaptureDevice::Name& FakeVideoCaptureDevice::device_name() {
+ return device_name_;
+}
+
+void FakeVideoCaptureDevice::OnCaptureTask() {
+ if (state_ != kCapturing) {
+ return;
+ }
+ // Give the captured frame to the observer.
+ observer_->OnIncomingCapturedFrame(fake_frame_.get(),
+ sizeof(fake_frame_.get()),
+ base::Time::Now());
+ // Reschedule next CaptureTask.
+ capture_thread_.message_loop()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask),
+ kFakeCaptureTimeoutMs);
+}
+
+} // namespace media
diff --git a/media/video/capture/fake_video_capture_device.h b/media/video/capture/fake_video_capture_device.h
new file mode 100644
index 0000000..089efb0
--- /dev/null
+++ b/media/video/capture/fake_video_capture_device.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation of a fake VideoCaptureDevice class. Used for testing other
+// video capture classes when no real hardware is available.
+
+#ifndef MEDIA_VIDEO_CAPTURE_FAKE_VIDEO_CAPTURE_DEVICE_H_
+#define MEDIA_VIDEO_CAPTURE_FAKE_VIDEO_CAPTURE_DEVICE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "media/video/capture/video_capture_device.h"
+
+namespace media {
+
+class FakeVideoCaptureDevice : public VideoCaptureDevice {
+ public:
+ static VideoCaptureDevice* Create(const Name& device_name);
+ virtual ~FakeVideoCaptureDevice();
+
+ static void GetDeviceNames(Names* device_names);
+
+ // VideoCaptureDevice implementation.
+ virtual void Allocate(int width,
+ int height,
+ int frame_rate,
+ VideoCaptureDevice::EventHandler* observer);
+ virtual void Start();
+ virtual void Stop();
+ virtual void DeAllocate();
+ virtual const Name& device_name();
+
+ private:
+ // Flag indicating the internal state.
+ enum InternalState {
+ kIdle,
+ kAllocated,
+ kCapturing,
+ kError
+ };
+ explicit FakeVideoCaptureDevice(const Name& device_name);
+
+ // Called on the capture_thread_.
+ void OnCaptureTask();
+
+ Name device_name_;
+ VideoCaptureDevice::EventHandler* observer_;
+ InternalState state_;
+ base::Thread capture_thread_;
+ scoped_ptr<uint8> fake_frame_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FakeVideoCaptureDevice);
+};
+
+} // namespace media
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(media::FakeVideoCaptureDevice);
+
+#endif // MEDIA_VIDEO_CAPTURE_FAKE_VIDEO_CAPTURE_DEVICE_H_
diff --git a/media/video/capture/linux/video_capture_device_linux.cc b/media/video/capture/linux/video_capture_device_linux.cc
new file mode 100644
index 0000000..52b46d2
--- /dev/null
+++ b/media/video/capture/linux/video_capture_device_linux.cc
@@ -0,0 +1,412 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/video/capture/linux/video_capture_device_linux.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/stringprintf.h"
+
+namespace media {
+
+// Number of supported v4l2 color formats.
+enum { kColorFormats = 2 };
+// Max number of video buffers VideoCaptureDeviceLinux can allocate.
+enum { kMaxVideoBuffers = 2 };
+// Timeout in microseconds v4l2_thread_ blocks waiting for a frame from the hw.
+enum { kCaptureTimeoutUs = 200000 };
+// Time to wait in milliseconds before v4l2_thread_ reschedules OnCaptureTask
+// if an event is triggered (select) but no video frame is read.
+enum { kCaptureSelectWaitMs = 10 };
+
+// V4L2 color formats VideoCaptureDeviceLinux support.
+static const int32 kV4l2Fmts[kColorFormats] = {
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YUYV
+};
+
+static VideoCaptureDevice::Format V4l2ColorToVideoCaptureColorFormat(
+ int32 v4l2_fourcc) {
+ VideoCaptureDevice::Format result = VideoCaptureDevice::kColorUnknown;
+ switch (v4l2_fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ result = VideoCaptureDevice::kI420;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ result = VideoCaptureDevice::kYUY2;
+ break;
+ }
+ DCHECK_NE(result, VideoCaptureDevice::kColorUnknown);
+ return result;
+}
+
+void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
+ int fd = -1;
+
+ // Empty the name list.
+ device_names->clear();
+
+ FilePath path("/dev/");
+ file_util::FileEnumerator enumerator(
+ path, false, file_util::FileEnumerator::FILES, "video*");
+
+ while (!enumerator.Next().empty()) {
+ file_util::FileEnumerator::FindInfo info;
+ enumerator.GetFindInfo(&info);
+
+ Name name;
+ name.unique_id = path.value() + info.filename;
+ if ((fd = open(name.unique_id.c_str() , O_RDONLY)) <= 0) {
+ // Failed to open this device.
+ continue;
+ }
+ // Test if this is a V4L2 device.
+ v4l2_capability cap;
+ if ((ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) &&
+ (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ // This is a V4L2 video capture device
+ name.device_name = StringPrintf("%s", cap.card);
+ device_names->push_back(name);
+ }
+ close(fd);
+ }
+}
+
+VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
+ VideoCaptureDeviceLinux* self = new VideoCaptureDeviceLinux(device_name);
+ if (!self || self->Init() != true) {
+ return NULL;
+ }
+ return self;
+}
+
+VideoCaptureDeviceLinux::VideoCaptureDeviceLinux(const Name& device_name)
+ : state_(kIdle),
+ observer_(NULL),
+ device_name_(device_name),
+ device_fd_(-1),
+ v4l2_thread_("V4L2Thread"),
+ buffer_pool_(NULL),
+ buffer_pool_size_(0) {
+}
+
+VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
+ state_ = kIdle;
+ // Check if the thread is running.
+ // This means that the device have not been DeAllocated properly.
+ DCHECK(!v4l2_thread_.IsRunning());
+
+ v4l2_thread_.Stop();
+ if (device_fd_ >= 0) {
+ close(device_fd_);
+ }
+}
+
+bool VideoCaptureDeviceLinux::Init() {
+ if ((device_fd_ = open(device_name_.unique_id.c_str(), O_RDONLY)) < 0) {
+ return false;
+ }
+ return true;
+}
+
+void VideoCaptureDeviceLinux::Allocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer) {
+ if (v4l2_thread_.IsRunning()) {
+ return; // Wrong state.
+ }
+ v4l2_thread_.Start();
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnAllocate,
+ width, height, frame_rate, observer));
+}
+
+void VideoCaptureDeviceLinux::Start() {
+ if (!v4l2_thread_.IsRunning()) {
+ return; // Wrong state.
+ }
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnStart));
+}
+
+void VideoCaptureDeviceLinux::Stop() {
+ if (!v4l2_thread_.IsRunning()) {
+ return; // Wrong state.
+ }
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnStop));
+}
+
+void VideoCaptureDeviceLinux::DeAllocate() {
+ if (!v4l2_thread_.IsRunning()) {
+ return; // Wrong state.
+ }
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnDeAllocate));
+ v4l2_thread_.Stop();
+
+ // We need to close and open the device if we want to change the settings
+ // Otherwise VIDIOC_S_FMT will return error
+ // Sad but true. We still open the device in the Init function.
+ close(device_fd_);
+ device_fd_ = -1;
+
+ // Make sure no buffers are still allocated.
+ // This can happen (theoretically) if an error occurs when trying to stop
+ // the camera.
+ DeAllocateVideoBuffers();
+
+ if (!Init() && state_ != kError) {
+ SetErrorState("Failed to reinitialize camera");
+ return;
+ }
+}
+
+const VideoCaptureDevice::Name& VideoCaptureDeviceLinux::device_name() {
+ return device_name_;
+}
+
+void VideoCaptureDeviceLinux::OnAllocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer) {
+ DCHECK_EQ(v4l2_thread_.message_loop(), MessageLoop::current());
+
+ observer_ = observer;
+
+ // Test if this is a V4L2 device.
+ v4l2_capability cap;
+ if (!((ioctl(device_fd_, VIDIOC_QUERYCAP, &cap) == 0) &&
+ (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
+ // This is not a V4L2 video capture device.
+ close(device_fd_);
+ device_fd_ = -1;
+ SetErrorState("This is not a V4L2 video capture device");
+ return;
+ }
+
+ v4l2_format video_fmt;
+ memset(&video_fmt, 0, sizeof(video_fmt));
+ video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ video_fmt.fmt.pix.sizeimage = 0;
+ video_fmt.fmt.pix.width = width;
+ video_fmt.fmt.pix.height = height;
+
+ bool format_match = false;
+ for (int i = 0; i < kColorFormats; i++) {
+ video_fmt.fmt.pix.pixelformat = kV4l2Fmts[i];
+ if (ioctl(device_fd_, VIDIOC_TRY_FMT, &video_fmt) < 0) {
+ continue;
+ }
+ format_match = true;
+ }
+
+ if (!format_match) {
+ SetErrorState("Failed to find supported camera format.");
+ return;
+ }
+ // Set format and frame size now.
+ if (ioctl(device_fd_, VIDIOC_S_FMT, &video_fmt) < 0) {
+ SetErrorState("Failed to set camera format");
+ return;
+ }
+
+ // Store our current width and height.
+ Capability current_settings;
+ current_settings.color = V4l2ColorToVideoCaptureColorFormat(
+ video_fmt.fmt.pix.pixelformat);
+ current_settings.width = video_fmt.fmt.pix.width;
+ current_settings.height = video_fmt.fmt.pix.height;
+ current_settings.frame_rate = frame_rate;
+
+ state_ = kAllocated;
+ // Report the resulting frame size to the observer.
+ observer_->OnFrameInfo(current_settings);
+}
+
+void VideoCaptureDeviceLinux::OnDeAllocate() {
+ DCHECK_EQ(v4l2_thread_.message_loop(), MessageLoop::current());
+
+ // If we are in error state or capturing
+ // try to stop the camera.
+ if (state_ == kCapturing) {
+ OnStop();
+ }
+ if (state_ == kAllocated) {
+ state_ = kIdle;
+ }
+}
+
+void VideoCaptureDeviceLinux::OnStart() {
+ DCHECK_EQ(v4l2_thread_.message_loop(), MessageLoop::current());
+
+ if (state_ != kAllocated) {
+ return;
+ }
+
+ if (!AllocateVideoBuffers()) {
+ // Error, We can not recover.
+ SetErrorState("Allocate buffer failed");
+ return;
+ }
+
+ // Start UVC camera.
+ v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl(device_fd_, VIDIOC_STREAMON, &type) == -1) {
+ SetErrorState("VIDIOC_STREAMON failed");
+ return;
+ }
+
+ state_ = kCapturing;
+ // Post task to start fetching frames from v4l2.
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnCaptureTask));
+}
+
+void VideoCaptureDeviceLinux::OnStop() {
+ DCHECK_EQ(v4l2_thread_.message_loop(), MessageLoop::current());
+
+ state_ = kAllocated;
+
+ v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (ioctl(device_fd_, VIDIOC_STREAMOFF, &type) < 0) {
+ SetErrorState("VIDIOC_STREAMOFF failed");
+ return;
+ }
+ // We don't dare to deallocate the buffers if we can't stop
+ // the capture device.
+ DeAllocateVideoBuffers();
+}
+
+void VideoCaptureDeviceLinux::OnCaptureTask() {
+ DCHECK_EQ(v4l2_thread_.message_loop(), MessageLoop::current());
+
+ if (state_ != kCapturing) {
+ return;
+ }
+
+ fd_set r_set;
+ FD_ZERO(&r_set);
+ FD_SET(device_fd_, &r_set);
+ timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = kCaptureTimeoutUs;
+
+ // First argument to select is the highest numbered file descriptor +1.
+ // Refer to http://linux.die.net/man/2/select for more information.
+ int result = select(device_fd_ + 1, &r_set, NULL, NULL, &timeout);
+ // Check if select have failed.
+ if (result < 0) {
+ // EINTR is a signal. This is not really an error.
+ if (errno != EINTR) {
+ SetErrorState("Select failed");
+ return;
+ }
+ v4l2_thread_.message_loop()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnCaptureTask),
+ kCaptureSelectWaitMs);
+ }
+
+ // Check if the driver have filled a buffer.
+ if (FD_ISSET(device_fd_, &r_set)) {
+ v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ // Dequeue a buffer.
+ if (ioctl(device_fd_, VIDIOC_DQBUF, &buffer) == 0) {
+ observer_->OnIncomingCapturedFrame(
+ static_cast<uint8*> (buffer_pool_[buffer.index].start),
+ buffer.bytesused, base::Time::Now());
+
+ // Enqueue the buffer again.
+ if (ioctl(device_fd_, VIDIOC_QBUF, &buffer) == -1) {
+ SetErrorState(
+ StringPrintf("Failed to enqueue capture buffer errno %d", errno));
+ }
+ } // Else wait for next event.
+ }
+
+ v4l2_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureDeviceLinux::OnCaptureTask));
+}
+
+bool VideoCaptureDeviceLinux::AllocateVideoBuffers() {
+ v4l2_requestbuffers r_buffer;
+ memset(&r_buffer, 0, sizeof(r_buffer));
+
+ r_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ r_buffer.memory = V4L2_MEMORY_MMAP;
+ r_buffer.count = kMaxVideoBuffers;
+
+ if (ioctl(device_fd_, VIDIOC_REQBUFS, &r_buffer) < 0) {
+ return false;
+ }
+
+ if (r_buffer.count > kMaxVideoBuffers) {
+ r_buffer.count = kMaxVideoBuffers;
+ }
+
+ buffer_pool_size_ = r_buffer.count;
+
+ // Map the buffers.
+ buffer_pool_ = new Buffer[r_buffer.count];
+ for (unsigned int i = 0; i < r_buffer.count; i++) {
+ v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buffer.memory = V4L2_MEMORY_MMAP;
+ buffer.index = i;
+
+ if (ioctl(device_fd_, VIDIOC_QUERYBUF, &buffer) < 0) {
+ return false;
+ }
+
+ buffer_pool_[i].start = mmap(NULL, buffer.length, PROT_READ,
+ MAP_SHARED, device_fd_, buffer.m.offset);
+ if (buffer_pool_[i].start == MAP_FAILED) {
+ return false;
+ }
+ buffer_pool_[i].length = buffer.length;
+ // Enqueue the buffer in the drivers incoming queue.
+ if (ioctl(device_fd_, VIDIOC_QBUF, &buffer) < 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() {
+ // Unmaps buffers.
+ for (int i = 0; i < buffer_pool_size_; i++) {
+ munmap(buffer_pool_[i].start, buffer_pool_[i].length);
+ }
+ delete [] buffer_pool_;
+ buffer_pool_ = NULL;
+ buffer_pool_size_ = 0;
+}
+
+void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) {
+ DLOG(ERROR) << reason;
+ state_ = kError;
+ observer_->OnError();
+}
+
+} // namespace media
diff --git a/media/video/capture/linux/video_capture_device_linux.h b/media/video/capture/linux/video_capture_device_linux.h
new file mode 100644
index 0000000..adcb592
--- /dev/null
+++ b/media/video/capture/linux/video_capture_device_linux.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Linux specific implementation of VideoCaptureDevice.
+// V4L2 is used for capturing. V4L2 does not provide it's own thread for
+// capturing so this implementation uses a Chromium thread for fetching frames
+// from V4L2.
+
+#ifndef MEDIA_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_DEVICE_LINUX_H_
+#define MEDIA_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_DEVICE_LINUX_H_
+
+#include <string>
+
+#include "base/threading/thread.h"
+#include "media/video/capture/video_capture_device.h"
+
+namespace media {
+
+class VideoCaptureDeviceLinux : public VideoCaptureDevice {
+ public:
+ explicit VideoCaptureDeviceLinux(const Name& device_name);
+ virtual ~VideoCaptureDeviceLinux();
+
+ // Opens the device driver for this device.
+ // This function is used by the static VideoCaptureDevice::Create function.
+ bool Init();
+
+ // VideoCaptureDevice implementation.
+ virtual void Allocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer);
+ virtual void Start();
+ virtual void Stop();
+ virtual void DeAllocate();
+ virtual const Name& device_name();
+
+ private:
+ enum InternalState {
+ kIdle, // The device driver is opened but camera is not in use.
+ kAllocated, // The camera has been allocated and can be started.
+ kCapturing, // Video is being captured.
+ kError // Error accessing HW functions.
+ // User needs to recover by destroying the object.
+ };
+
+ // Buffers used to receive video frames from with v4l2.
+ struct Buffer {
+ Buffer() : start(0), length(0) {}
+ void* start;
+ size_t length;
+ };
+
+ // Called on the v4l2_thread_.
+ void OnAllocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer);
+ void OnStart();
+ void OnStop();
+ void OnDeAllocate();
+ void OnCaptureTask();
+
+ bool AllocateVideoBuffers();
+ void DeAllocateVideoBuffers();
+ void SetErrorState(const std::string& reason);
+
+ InternalState state_;
+ VideoCaptureDevice::EventHandler* observer_;
+ Name device_name_;
+ int device_fd_; // File descriptor for the opened camera device.
+ base::Thread v4l2_thread_; // Thread used for reading data from the device.
+ Buffer* buffer_pool_;
+ int buffer_pool_size_; // Number of allocated buffers.
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceLinux);
+};
+
+} // namespace media
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(media::VideoCaptureDeviceLinux);
+
+#endif // MEDIA_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_DEVICE_LINUX_H_
diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h
new file mode 100644
index 0000000..a2d4ddd
--- /dev/null
+++ b/media/video/capture/video_capture_device.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// VideoCaptureDevice is the abstract base class for realizing video capture
+// device support in Chromium. It provides the interface for OS dependent
+// implementations.
+// The class is created and functions are invoked on a thread owned by
+// VideoCaptureManager. Capturing is done on other threads depended on the OS
+// specific implementation.
+
+#ifndef MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_H_
+#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_H_
+
+#include <list>
+#include <string>
+
+#include "base/time.h"
+
+namespace media {
+
+class VideoCaptureDevice {
+ public:
+
+ struct Name {
+ // Friendly name of a device
+ std::string device_name;
+
+ // Unique name of a device. Even if there are multiple devices with the same
+ // friendly name connected to the computer this will be unique.
+ std::string unique_id;
+ };
+ typedef std::list<Name> Names;
+
+ // Color formats from camera.
+ enum Format {
+ kColorUnknown, // Color format not set.
+ kI420,
+ kYUY2,
+ kUYVY,
+ kRGB24,
+ kARGB,
+ kMJPEG, // Currently only supported on Windows.
+ };
+
+ // Describes the format a camera capture video in.
+ struct Capability {
+ Capability()
+ : width(0),
+ height(0),
+ frame_rate(0),
+ color(kColorUnknown) {}
+ int width;
+ int height;
+ int frame_rate;
+ Format color;
+ };
+
+ class EventHandler {
+ public:
+ // Captured a new video frame.
+ virtual void OnIncomingCapturedFrame(const uint8* data,
+ int length,
+ base::Time timestamp) = 0;
+ // An error has occurred that can not be handled
+ // and VideoCaptureDevice must be DeAllocated.
+ virtual void OnError() = 0;
+ // Called when VideoCaptureDevice::Allocate has been called
+ // to inform of the resulting frame size and color format.
+ virtual void OnFrameInfo(const Capability& info) = 0;
+
+ protected:
+ virtual ~EventHandler() {}
+ };
+ // Creates a VideoCaptureDevice object.
+ // Return NULL if the hardware is not available.
+ static VideoCaptureDevice* Create(const Name& device_name);
+ virtual ~VideoCaptureDevice() {}
+
+ // Gets the names of all video capture devices connected to this computer.
+ static void GetDeviceNames(Names* device_names);
+
+ // Prepare the camera for use. After this function has been called no other
+ // applications can use the camera. On completion EventHandler::OnFrameInfo is
+ // called informing of the resulting resolution and frame rate.
+ // DeAllocate must be called before this function can be called again and
+ // before the object is deleted.
+ virtual void Allocate(int width,
+ int height,
+ int frame_rate,
+ EventHandler* observer) = 0;
+
+ // Start capturing video frames. Allocate must be called before this function.
+ virtual void Start() = 0;
+
+ // Stop capturing video frames.
+ virtual void Stop() = 0;
+
+ // DeAllocates the camera. This means other applications can use it.
+ // After this function has been called the Capture device is reset to the
+ // state it was when created.
+ virtual void DeAllocate() = 0;
+
+ // Get the name of the capture device.
+ virtual const Name& device_name() = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_DEVICE_H_