summaryrefslogtreecommitdiff
path: root/chromium/media/video/capture/linux/video_capture_device_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/video/capture/linux/video_capture_device_linux.cc')
-rw-r--r--chromium/media/video/capture/linux/video_capture_device_linux.cc206
1 files changed, 116 insertions, 90 deletions
diff --git a/chromium/media/video/capture/linux/video_capture_device_linux.cc b/chromium/media/video/capture/linux/video_capture_device_linux.cc
index fdd52772cb1..21f57ee132a 100644
--- a/chromium/media/video/capture/linux/video_capture_device_linux.cc
+++ b/chromium/media/video/capture/linux/video_capture_device_linux.cc
@@ -45,7 +45,7 @@ static const int32 kV4l2RawFmts[] = {
V4L2_PIX_FMT_YUYV
};
-// USB VID and PID are both 4 bytes long
+// USB VID and PID are both 4 bytes long.
static const size_t kVidPidSize = 4;
// /sys/class/video4linux/video{N}/device is a symlink to the corresponding
@@ -55,6 +55,8 @@ static const char kVidPathTemplate[] =
static const char kPidPathTemplate[] =
"/sys/class/video4linux/%s/device/../idProduct";
+// This function translates Video4Linux pixel formats to Chromium pixel formats,
+// should only support those listed in GetListOfUsableFourCCs.
static VideoPixelFormat V4l2ColorToVideoCaptureColorFormat(
int32 v4l2_fourcc) {
VideoPixelFormat result = PIXEL_FORMAT_UNKNOWN;
@@ -69,8 +71,9 @@ static VideoPixelFormat V4l2ColorToVideoCaptureColorFormat(
case V4L2_PIX_FMT_JPEG:
result = PIXEL_FORMAT_MJPEG;
break;
+ default:
+ DVLOG(1) << "Unsupported pixel format " << std::hex << v4l2_fourcc;
}
- DCHECK_NE(result, PIXEL_FORMAT_UNKNOWN);
return result;
}
@@ -141,6 +144,76 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
}
}
+void VideoCaptureDevice::GetDeviceSupportedFormats(
+ const Name& device,
+ VideoCaptureFormats* supported_formats) {
+ if (device.id().empty())
+ return;
+ int fd;
+ if ((fd = open(device.id().c_str(), O_RDONLY)) < 0)
+ return;
+
+ supported_formats->clear();
+ // Retrieve the caps one by one, first get pixel format, then sizes, then
+ // frame rates. See http://linuxtv.org/downloads/v4l-dvb-apis for reference.
+ v4l2_fmtdesc pixel_format = {};
+ pixel_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ while (ioctl(fd, VIDIOC_ENUM_FMT, &pixel_format) == 0) {
+ VideoCaptureFormat supported_format;
+ supported_format.pixel_format =
+ V4l2ColorToVideoCaptureColorFormat((int32)pixel_format.pixelformat);
+ if (supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) {
+ ++pixel_format.index;
+ continue;
+ }
+
+ v4l2_frmsizeenum frame_size = {};
+ frame_size.pixel_format = pixel_format.pixelformat;
+ while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0) {
+ if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ supported_format.frame_size.SetSize(
+ frame_size.discrete.width, frame_size.discrete.height);
+ } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
+ // TODO(mcasas): see http://crbug.com/249953, support these devices.
+ NOTIMPLEMENTED();
+ } else if (frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
+ // TODO(mcasas): see http://crbug.com/249953, support these devices.
+ NOTIMPLEMENTED();
+ }
+ v4l2_frmivalenum frame_interval = {};
+ frame_interval.pixel_format = pixel_format.pixelformat;
+ frame_interval.width = frame_size.discrete.width;
+ frame_interval.height = frame_size.discrete.height;
+ while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frame_interval) == 0) {
+ if (frame_interval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
+ if (frame_interval.discrete.numerator != 0) {
+ supported_format.frame_rate =
+ static_cast<float>(frame_interval.discrete.denominator) /
+ static_cast<float>(frame_interval.discrete.numerator);
+ } else {
+ supported_format.frame_rate = 0;
+ }
+ } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
+ // TODO(mcasas): see http://crbug.com/249953, support these devices.
+ NOTIMPLEMENTED();
+ break;
+ } else if (frame_interval.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
+ // TODO(mcasas): see http://crbug.com/249953, support these devices.
+ NOTIMPLEMENTED();
+ break;
+ }
+ supported_formats->push_back(supported_format);
+ ++frame_interval.index;
+ }
+ ++frame_size.index;
+ }
+ ++pixel_format.index;
+ }
+
+ close(fd);
+ return;
+}
+
static bool ReadIdFile(const std::string path, std::string* id) {
char id_buf[kVidPidSize];
FILE* file = fopen(path.c_str(), "rb");
@@ -196,14 +269,12 @@ VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
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),
- timeout_count_(0) {
-}
+ timeout_count_(0) {}
VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
state_ = kIdle;
@@ -217,68 +288,45 @@ VideoCaptureDeviceLinux::~VideoCaptureDeviceLinux() {
}
}
-void VideoCaptureDeviceLinux::Allocate(
- const VideoCaptureCapability& capture_format,
- EventHandler* observer) {
+void VideoCaptureDeviceLinux::AllocateAndStart(
+ const VideoCaptureParams& params,
+ scoped_ptr<VideoCaptureDevice::Client> client) {
if (v4l2_thread_.IsRunning()) {
return; // Wrong state.
}
v4l2_thread_.Start();
- v4l2_thread_.message_loop()
- ->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnAllocate,
- base::Unretained(this),
- capture_format.width,
- capture_format.height,
- capture_format.frame_rate,
- observer));
-}
-
-void VideoCaptureDeviceLinux::Start() {
- if (!v4l2_thread_.IsRunning()) {
- return; // Wrong state.
- }
- v4l2_thread_.message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnStart, base::Unretained(this)));
-}
-
-void VideoCaptureDeviceLinux::Stop() {
- if (!v4l2_thread_.IsRunning()) {
- return; // Wrong state.
- }
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnStop, base::Unretained(this)));
+ base::Bind(&VideoCaptureDeviceLinux::OnAllocateAndStart,
+ base::Unretained(this),
+ params.requested_format.frame_size.width(),
+ params.requested_format.frame_size.height(),
+ params.requested_format.frame_rate,
+ base::Passed(&client)));
}
-void VideoCaptureDeviceLinux::DeAllocate() {
+void VideoCaptureDeviceLinux::StopAndDeAllocate() {
if (!v4l2_thread_.IsRunning()) {
return; // Wrong state.
}
v4l2_thread_.message_loop()->PostTask(
FROM_HERE,
- base::Bind(&VideoCaptureDeviceLinux::OnDeAllocate,
+ base::Bind(&VideoCaptureDeviceLinux::OnStopAndDeAllocate,
base::Unretained(this)));
v4l2_thread_.Stop();
-
// Make sure no buffers are still allocated.
// This can happen (theoretically) if an error occurs when trying to stop
// the camera.
DeAllocateVideoBuffers();
}
-const VideoCaptureDevice::Name& VideoCaptureDeviceLinux::device_name() {
- return device_name_;
-}
-
-void VideoCaptureDeviceLinux::OnAllocate(int width,
- int height,
- int frame_rate,
- EventHandler* observer) {
+void VideoCaptureDeviceLinux::OnAllocateAndStart(int width,
+ int height,
+ int frame_rate,
+ scoped_ptr<Client> client) {
DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
- observer_ = observer;
+ client_ = client.Pass();
// Need to open camera with O_RDWR after Linux kernel 3.3.
if ((device_fd_ = open(device_name_.id().c_str(), O_RDWR)) < 0) {
@@ -359,46 +407,13 @@ void VideoCaptureDeviceLinux::OnAllocate(int width,
// framerate configuration, or the actual one is different from the desired?
// Store our current width and height.
- VideoCaptureCapability 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;
- current_settings.expected_capture_delay = 0;
- current_settings.interlaced = false;
-
- state_ = kAllocated;
- // Report the resulting frame size to the observer.
- observer_->OnFrameInfo(current_settings);
-}
-
-void VideoCaptureDeviceLinux::OnDeAllocate() {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
-
- // If we are in error state or capturing
- // try to stop the camera.
- if (state_ == kCapturing) {
- OnStop();
- }
- if (state_ == kAllocated) {
- state_ = kIdle;
- }
-
- // 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.
- close(device_fd_);
- device_fd_ = -1;
-}
-
-void VideoCaptureDeviceLinux::OnStart() {
- DCHECK_EQ(v4l2_thread_.message_loop(), base::MessageLoop::current());
-
- if (state_ != kAllocated) {
- return;
- }
+ capture_format_.frame_size.SetSize(video_fmt.fmt.pix.width,
+ video_fmt.fmt.pix.height);
+ capture_format_.frame_rate = frame_rate;
+ capture_format_.pixel_format =
+ V4l2ColorToVideoCaptureColorFormat(video_fmt.fmt.pix.pixelformat);
+ // Start capturing.
if (!AllocateVideoBuffers()) {
// Error, We can not recover.
SetErrorState("Allocate buffer failed");
@@ -420,11 +435,9 @@ void VideoCaptureDeviceLinux::OnStart() {
base::Unretained(this)));
}
-void VideoCaptureDeviceLinux::OnStop() {
+void VideoCaptureDeviceLinux::OnStopAndDeAllocate() {
DCHECK_EQ(v4l2_thread_.message_loop(), base::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");
@@ -433,6 +446,14 @@ void VideoCaptureDeviceLinux::OnStop() {
// We don't dare to deallocate the buffers if we can't stop
// the capture device.
DeAllocateVideoBuffers();
+
+ // 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.
+ close(device_fd_);
+ device_fd_ = -1;
+ state_ = kIdle;
+ client_.reset();
}
void VideoCaptureDeviceLinux::OnCaptureTask() {
@@ -488,9 +509,12 @@ void VideoCaptureDeviceLinux::OnCaptureTask() {
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(), 0, false, false);
+ client_->OnIncomingCapturedFrame(
+ static_cast<uint8*>(buffer_pool_[buffer.index].start),
+ buffer.bytesused,
+ base::Time::Now(),
+ 0,
+ capture_format_);
// Enqueue the buffer again.
if (ioctl(device_fd_, VIDIOC_QBUF, &buffer) == -1) {
@@ -581,9 +605,11 @@ void VideoCaptureDeviceLinux::DeAllocateVideoBuffers() {
}
void VideoCaptureDeviceLinux::SetErrorState(const std::string& reason) {
+ DCHECK(!v4l2_thread_.IsRunning() ||
+ v4l2_thread_.message_loop() == base::MessageLoop::current());
DVLOG(1) << reason;
state_ = kError;
- observer_->OnError();
+ client_->OnError();
}
} // namespace media