Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/capture/video/fake_video_capture_device.h" | 5 #include "media/capture/video/fake_video_capture_device.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/location.h" | 12 #include "base/location.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/memory/weak_ptr.h" | 14 #include "base/memory/weak_ptr.h" |
| 15 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 17 #include "base/threading/thread_checker.h" | 17 #include "base/threading/thread_checker.h" |
| 18 #include "base/threading/thread_task_runner_handle.h" | 18 #include "base/threading/thread_task_runner_handle.h" |
| 19 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 20 #include "media/audio/fake_audio_input_stream.h" | 20 #include "media/audio/fake_audio_input_stream.h" |
| 21 #include "media/base/video_frame.h" | 21 #include "media/base/video_frame.h" |
| 22 #include "third_party/skia/include/core/SkBitmap.h" | 22 #include "third_party/skia/include/core/SkBitmap.h" |
| 23 #include "third_party/skia/include/core/SkCanvas.h" | 23 #include "third_party/skia/include/core/SkCanvas.h" |
| 24 #include "third_party/skia/include/core/SkMatrix.h" | 24 #include "third_party/skia/include/core/SkMatrix.h" |
| 25 #include "third_party/skia/include/core/SkPaint.h" | 25 #include "third_party/skia/include/core/SkPaint.h" |
| 26 #include "ui/gfx/codec/jpeg_codec.h" | |
| 26 #include "ui/gfx/codec/png_codec.h" | 27 #include "ui/gfx/codec/png_codec.h" |
| 27 | 28 |
| 28 namespace media { | 29 namespace media { |
| 29 | 30 |
| 30 namespace { | 31 namespace { |
| 31 // Sweep at 600 deg/sec. | 32 // Sweep at 600 deg/sec. |
| 32 static const float kPacmanAngularVelocity = 600; | 33 static const float kPacmanAngularVelocity = 600; |
| 33 // Beep every 500 ms. | 34 // Beep every 500 ms. |
| 34 static const int kBeepInterval = 500; | 35 static const int kBeepInterval = 500; |
| 35 // Gradient travels from bottom to top in 5 seconds. | 36 // Gradient travels from bottom to top in 5 seconds. |
| 36 static const float kGradientFrequency = 1.f / 5; | 37 static const float kGradientFrequency = 1.f / 5; |
| 37 | 38 |
| 38 static const double kMinZoom = 100.0; | 39 static const double kMinZoom = 100.0; |
| 39 static const double kMaxZoom = 400.0; | 40 static const double kMaxZoom = 400.0; |
| 40 static const double kZoomStep = 1.0; | 41 static const double kZoomStep = 1.0; |
| 41 static const double kInitialZoom = 100.0; | 42 static const double kInitialZoom = 100.0; |
| 42 | 43 |
| 43 static const gfx::Size kSupportedSizesOrderedByIncreasingWidth[] = { | 44 static const gfx::Size kSupportedSizesOrderedByIncreasingWidth[] = { |
| 44 gfx::Size(96, 96), gfx::Size(320, 240), gfx::Size(640, 480), | 45 gfx::Size(96, 96), gfx::Size(320, 240), gfx::Size(640, 480), |
| 45 gfx::Size(1280, 720), gfx::Size(1920, 1080)}; | 46 gfx::Size(1280, 720), gfx::Size(1920, 1080)}; |
| 46 static const int kSupportedSizesCount = | 47 static const int kSupportedSizesCount = |
| 47 arraysize(kSupportedSizesOrderedByIncreasingWidth); | 48 arraysize(kSupportedSizesOrderedByIncreasingWidth); |
| 48 | 49 |
| 49 static const VideoPixelFormat kSupportedPixelFormats[] = { | 50 static const VideoPixelFormat kSupportedPixelFormats[] = { |
| 50 PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_ARGB}; | 51 PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_ARGB, PIXEL_FORMAT_MJPEG}; |
| 51 | 52 |
| 52 static gfx::Size SnapToSupportedSize(const gfx::Size& requested_size) { | 53 static gfx::Size SnapToSupportedSize(const gfx::Size& requested_size) { |
| 53 for (const gfx::Size& supported_size : | 54 for (const gfx::Size& supported_size : |
| 54 kSupportedSizesOrderedByIncreasingWidth) { | 55 kSupportedSizesOrderedByIncreasingWidth) { |
| 55 if (requested_size.width() <= supported_size.width()) | 56 if (requested_size.width() <= supported_size.width()) |
| 56 return supported_size; | 57 return supported_size; |
| 57 } | 58 } |
| 58 return kSupportedSizesOrderedByIncreasingWidth[kSupportedSizesCount - 1]; | 59 return kSupportedSizesOrderedByIncreasingWidth[kSupportedSizesCount - 1]; |
| 59 } | 60 } |
| 60 | 61 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 145 ~ClientBufferFrameDeliverer() override; | 146 ~ClientBufferFrameDeliverer() override; |
| 146 | 147 |
| 147 // Implementation of FrameDeliverer | 148 // Implementation of FrameDeliverer |
| 148 void Initialize(VideoPixelFormat pixel_format, | 149 void Initialize(VideoPixelFormat pixel_format, |
| 149 std::unique_ptr<VideoCaptureDevice::Client> client, | 150 std::unique_ptr<VideoCaptureDevice::Client> client, |
| 150 const FakeDeviceState* device_state) override; | 151 const FakeDeviceState* device_state) override; |
| 151 void Uninitialize() override; | 152 void Uninitialize() override; |
| 152 void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override; | 153 void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override; |
| 153 }; | 154 }; |
| 154 | 155 |
| 156 class CAPTURE_EXPORT JpegEncodingFrameDeliverer : public FrameDeliverer { | |
| 157 public: | |
| 158 JpegEncodingFrameDeliverer(std::unique_ptr<PacmanFramePainter> frame_painter); | |
| 159 ~JpegEncodingFrameDeliverer() override; | |
| 160 | |
| 161 // Implementation of FrameDeliveryStrategy | |
| 162 void Initialize(VideoPixelFormat pixel_format, | |
| 163 std::unique_ptr<VideoCaptureDevice::Client> client, | |
| 164 const FakeDeviceState* device_state) override; | |
| 165 void Uninitialize() override; | |
| 166 void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override; | |
| 167 | |
| 168 private: | |
| 169 const FakeDeviceState* device_state_ = nullptr; | |
| 170 std::unique_ptr<uint8_t[]> argb_buffer_; | |
| 171 int argb_buffer_size_in_bytes_ = -1; | |
| 172 std::vector<unsigned char> jpeg_buffer_; | |
| 173 std::unique_ptr<VideoCaptureDevice::Client> client_; | |
| 174 // The system time when we receive the first frame. | |
| 175 base::TimeTicks first_ref_time_; | |
|
chfremer
2017/02/17 18:07:38
I forgot to remove members |device_state|, |client
chfremer
2017/02/22 17:28:41
Done.
| |
| 176 }; | |
| 177 | |
| 155 // Implements the photo functionality of a VideoCaptureDevice | 178 // Implements the photo functionality of a VideoCaptureDevice |
| 156 class FakePhotoDevice { | 179 class FakePhotoDevice { |
| 157 public: | 180 public: |
| 158 FakePhotoDevice(std::unique_ptr<PacmanFramePainter> argb_painter, | 181 FakePhotoDevice(std::unique_ptr<PacmanFramePainter> argb_painter, |
| 159 const FakeDeviceState* fake_device_state); | 182 const FakeDeviceState* fake_device_state); |
| 160 ~FakePhotoDevice(); | 183 ~FakePhotoDevice(); |
| 161 | 184 |
| 162 void GetPhotoCapabilities( | 185 void GetPhotoCapabilities( |
| 163 VideoCaptureDevice::GetPhotoCapabilitiesCallback callback); | 186 VideoCaptureDevice::GetPhotoCapabilitiesCallback callback); |
| 164 void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback, | 187 void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback, |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 235 } | 258 } |
| 236 } | 259 } |
| 237 if (!pixel_format_supported) { | 260 if (!pixel_format_supported) { |
| 238 DLOG(ERROR) << "Requested an unsupported pixel format " | 261 DLOG(ERROR) << "Requested an unsupported pixel format " |
| 239 << VideoPixelFormatToString(pixel_format); | 262 << VideoPixelFormatToString(pixel_format); |
| 240 return nullptr; | 263 return nullptr; |
| 241 } | 264 } |
| 242 | 265 |
| 243 auto device_state = | 266 auto device_state = |
| 244 base::MakeUnique<FakeDeviceState>(kInitialZoom, frame_rate, pixel_format); | 267 base::MakeUnique<FakeDeviceState>(kInitialZoom, frame_rate, pixel_format); |
| 245 auto video_frame_painter = | 268 std::unique_ptr<PacmanFramePainter> video_frame_painter; |
| 246 base::MakeUnique<PacmanFramePainter>(pixel_format, device_state.get()); | 269 if (pixel_format == PIXEL_FORMAT_MJPEG) { |
| 270 video_frame_painter = base::MakeUnique<PacmanFramePainter>( | |
| 271 PIXEL_FORMAT_ARGB, device_state.get()); | |
| 272 } else { | |
| 273 video_frame_painter = | |
| 274 base::MakeUnique<PacmanFramePainter>(pixel_format, device_state.get()); | |
| 275 } | |
|
mcasas
2017/02/17 18:35:47
I find this part hard to read, mostly because of t
chfremer
2017/02/22 17:28:42
Done.
| |
| 276 | |
| 247 std::unique_ptr<FrameDeliverer> frame_delivery_strategy; | 277 std::unique_ptr<FrameDeliverer> frame_delivery_strategy; |
| 248 switch (delivery_mode) { | 278 switch (delivery_mode) { |
| 249 case DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS: | 279 case DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS: |
| 250 frame_delivery_strategy = base::MakeUnique<OwnBufferFrameDeliverer>( | 280 if (pixel_format == PIXEL_FORMAT_MJPEG) { |
| 251 std::move(video_frame_painter)); | 281 frame_delivery_strategy = base::MakeUnique<JpegEncodingFrameDeliverer>( |
| 282 std::move(video_frame_painter)); | |
| 283 } else { | |
| 284 frame_delivery_strategy = base::MakeUnique<OwnBufferFrameDeliverer>( | |
| 285 std::move(video_frame_painter)); | |
| 286 } | |
| 252 break; | 287 break; |
| 253 case DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS: | 288 case DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS: |
| 289 if (pixel_format == PIXEL_FORMAT_MJPEG) { | |
| 290 DLOG(ERROR) << "PIXEL_FORMAT_MJPEG cannot be used in combination with " | |
| 291 << "USE_CLIENT_BUFFERS."; | |
| 292 return nullptr; | |
| 293 } | |
|
mcasas
2017/02/17 18:35:47
Maybe not in this CL, but I don't see any fundamen
chfremer
2017/02/22 17:28:41
I thought so, too, until I tried and ran into some
| |
| 254 frame_delivery_strategy = base::MakeUnique<ClientBufferFrameDeliverer>( | 294 frame_delivery_strategy = base::MakeUnique<ClientBufferFrameDeliverer>( |
| 255 std::move(video_frame_painter)); | 295 std::move(video_frame_painter)); |
| 256 break; | 296 break; |
| 257 } | 297 } |
| 258 | 298 |
| 259 auto photo_frame_painter = base::MakeUnique<PacmanFramePainter>( | 299 auto photo_frame_painter = base::MakeUnique<PacmanFramePainter>( |
| 260 PIXEL_FORMAT_ARGB, device_state.get()); | 300 PIXEL_FORMAT_ARGB, device_state.get()); |
| 261 auto photo_device = base::MakeUnique<FakePhotoDevice>( | 301 auto photo_device = base::MakeUnique<FakePhotoDevice>( |
| 262 std::move(photo_frame_painter), device_state.get()); | 302 std::move(photo_frame_painter), device_state.get()); |
| 263 | 303 |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 507 } | 547 } |
| 508 | 548 |
| 509 void FakeVideoCaptureDevice::TakePhoto(TakePhotoCallback callback) { | 549 void FakeVideoCaptureDevice::TakePhoto(TakePhotoCallback callback) { |
| 510 DCHECK(thread_checker_.CalledOnValidThread()); | 550 DCHECK(thread_checker_.CalledOnValidThread()); |
| 511 base::ThreadTaskRunnerHandle::Get()->PostTask( | 551 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 512 FROM_HERE, base::Bind(&FakePhotoDevice::TakePhoto, | 552 FROM_HERE, base::Bind(&FakePhotoDevice::TakePhoto, |
| 513 base::Unretained(photo_device_.get()), | 553 base::Unretained(photo_device_.get()), |
| 514 base::Passed(&callback), elapsed_time_)); | 554 base::Passed(&callback), elapsed_time_)); |
| 515 } | 555 } |
| 516 | 556 |
| 557 JpegEncodingFrameDeliverer::JpegEncodingFrameDeliverer( | |
| 558 std::unique_ptr<PacmanFramePainter> frame_painter) | |
| 559 : FrameDeliverer(std::move(frame_painter)) {} | |
| 560 | |
| 561 JpegEncodingFrameDeliverer::~JpegEncodingFrameDeliverer() = default; | |
| 562 | |
| 563 void JpegEncodingFrameDeliverer::Initialize( | |
| 564 VideoPixelFormat pixel_format, | |
| 565 std::unique_ptr<VideoCaptureDevice::Client> client, | |
| 566 const FakeDeviceState* device_state) { | |
| 567 client_ = std::move(client); | |
| 568 device_state_ = device_state; | |
| 569 } | |
| 570 | |
| 571 void JpegEncodingFrameDeliverer::Uninitialize() { | |
| 572 client_.reset(); | |
| 573 device_state_ = nullptr; | |
|
mcasas
2017/02/17 18:35:47
l.567-568 and l.572-573 are common among all
Frame
chfremer
2017/02/22 17:28:41
Agreed. Thanks.
I moved the common initialize and
| |
| 574 argb_buffer_.reset(); | |
| 575 argb_buffer_size_in_bytes_ = -1; | |
| 576 jpeg_buffer_.clear(); | |
| 577 } | |
| 578 | |
| 579 void JpegEncodingFrameDeliverer::PaintAndDeliverNextFrame( | |
| 580 base::TimeDelta timestamp_to_paint) { | |
| 581 if (client_ == nullptr) | |
| 582 return; | |
| 583 | |
| 584 int required_argb_buffer_size_in_bytes = VideoFrame::AllocationSize( | |
| 585 PIXEL_FORMAT_ARGB, device_state_->format.frame_size); | |
| 586 if (argb_buffer_size_in_bytes_ < required_argb_buffer_size_in_bytes) { | |
| 587 argb_buffer_ = | |
| 588 base::MakeUnique<uint8_t[]>(required_argb_buffer_size_in_bytes); | |
| 589 argb_buffer_size_in_bytes_ = required_argb_buffer_size_in_bytes; | |
| 590 } | |
|
mcasas
2017/02/17 18:35:47
Instead of having a |argb_buffer_| and
|argb_buff
chfremer
2017/02/22 17:28:41
Nice. Thanks.
Done.
| |
| 591 | |
| 592 frame_painter_->PaintFrame(timestamp_to_paint, argb_buffer_.get()); | |
| 593 | |
| 594 static const int kBytePerPixel = 4; | |
|
mcasas
2017/02/17 18:35:47
s/kBytePer/BytesPer/
But you can skip it altogethe
chfremer
2017/02/22 17:28:41
Done.
| |
| 595 static const int kQuality = 75; | |
| 596 bool success = gfx::JPEGCodec::Encode( | |
| 597 argb_buffer_.get(), gfx::JPEGCodec::FORMAT_RGBA, | |
|
mcasas
2017/02/17 18:35:47
We seem to be using ARGB format internally whereas
chfremer
2017/02/22 17:28:41
Thanks for raising this.
Yes, this probably leads
| |
| 598 device_state_->format.frame_size.width(), | |
| 599 device_state_->format.frame_size.height(), | |
| 600 device_state_->format.frame_size.width() * kBytePerPixel, kQuality, | |
|
mcasas
2017/02/17 18:35:47
Use here
VideoFrame::RowBytes(1 /* plane */, PIXE
chfremer
2017/02/22 17:28:42
Did you mean 0 instead of 1 for the plane index?
D
| |
| 601 &jpeg_buffer_); | |
| 602 if (!success) { | |
| 603 DLOG(ERROR) << "Jpeg encoding failed"; | |
| 604 return; | |
| 605 } | |
| 606 | |
| 607 const size_t frame_size = jpeg_buffer_.size(); | |
| 608 base::TimeTicks now = base::TimeTicks::Now(); | |
| 609 client_->OnIncomingCapturedData(&jpeg_buffer_[0], frame_size, | |
| 610 device_state_->format, 0 /* rotation */, now, | |
| 611 CalculateTimeSinceFirstInvocation(now)); | |
| 612 } | |
| 613 | |
| 517 OwnBufferFrameDeliverer::OwnBufferFrameDeliverer( | 614 OwnBufferFrameDeliverer::OwnBufferFrameDeliverer( |
| 518 std::unique_ptr<PacmanFramePainter> frame_painter) | 615 std::unique_ptr<PacmanFramePainter> frame_painter) |
| 519 : FrameDeliverer(std::move(frame_painter)) {} | 616 : FrameDeliverer(std::move(frame_painter)) {} |
| 520 | 617 |
| 521 OwnBufferFrameDeliverer::~OwnBufferFrameDeliverer() = default; | 618 OwnBufferFrameDeliverer::~OwnBufferFrameDeliverer() = default; |
| 522 | 619 |
| 523 void OwnBufferFrameDeliverer::Initialize( | 620 void OwnBufferFrameDeliverer::Initialize( |
| 524 VideoPixelFormat pixel_format, | 621 VideoPixelFormat pixel_format, |
| 525 std::unique_ptr<VideoCaptureDevice::Client> client, | 622 std::unique_ptr<VideoCaptureDevice::Client> client, |
| 526 const FakeDeviceState* device_state) { | 623 const FakeDeviceState* device_state) { |
| 527 client_ = std::move(client); | 624 client_ = std::move(client); |
| 528 device_state_ = device_state; | 625 device_state_ = device_state; |
| 529 buffer_.reset(new uint8_t[VideoFrame::AllocationSize( | 626 buffer_.reset(new uint8_t[VideoFrame::AllocationSize( |
| 530 pixel_format, device_state_->format.frame_size)]); | 627 pixel_format, device_state_->format.frame_size)]); |
| 531 } | 628 } |
| 532 | 629 |
| 533 void OwnBufferFrameDeliverer::Uninitialize() { | 630 void OwnBufferFrameDeliverer::Uninitialize() { |
| 534 client_.reset(); | 631 client_.reset(); |
| 535 device_state_ = nullptr; | 632 device_state_ = nullptr; |
| 536 buffer_.reset(); | 633 buffer_.reset(); |
| 537 } | 634 } |
| 538 | 635 |
| 539 void OwnBufferFrameDeliverer::PaintAndDeliverNextFrame( | 636 void OwnBufferFrameDeliverer::PaintAndDeliverNextFrame( |
| 540 base::TimeDelta timestamp_to_paint) { | 637 base::TimeDelta timestamp_to_paint) { |
| 541 if (!client_) | 638 if (!client_) |
| 542 return; | 639 return; |
| 543 const size_t frame_size = device_state_->format.ImageAllocationSize(); | 640 const size_t frame_size = device_state_->format.ImageAllocationSize(); |
| 544 memset(buffer_.get(), 0, frame_size); | |
| 545 frame_painter_->PaintFrame(timestamp_to_paint, buffer_.get()); | |
| 546 base::TimeTicks now = base::TimeTicks::Now(); | 641 base::TimeTicks now = base::TimeTicks::Now(); |
| 547 client_->OnIncomingCapturedData(buffer_.get(), frame_size, | 642 client_->OnIncomingCapturedData(buffer_.get(), frame_size, |
| 548 device_state_->format, 0 /* rotation */, now, | 643 device_state_->format, 0 /* rotation */, now, |
| 549 CalculateTimeSinceFirstInvocation(now)); | 644 CalculateTimeSinceFirstInvocation(now)); |
| 550 } | 645 } |
| 551 | 646 |
| 552 ClientBufferFrameDeliverer::ClientBufferFrameDeliverer( | 647 ClientBufferFrameDeliverer::ClientBufferFrameDeliverer( |
| 553 std::unique_ptr<PacmanFramePainter> frame_painter) | 648 std::unique_ptr<PacmanFramePainter> frame_painter) |
| 554 : FrameDeliverer(std::move(frame_painter)) {} | 649 : FrameDeliverer(std::move(frame_painter)) {} |
| 555 | 650 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 630 int session_id) { | 725 int session_id) { |
| 631 DCHECK(thread_checker_.CalledOnValidThread()); | 726 DCHECK(thread_checker_.CalledOnValidThread()); |
| 632 if (session_id != current_session_id_) | 727 if (session_id != current_session_id_) |
| 633 return; | 728 return; |
| 634 | 729 |
| 635 frame_deliverer_->PaintAndDeliverNextFrame(elapsed_time_); | 730 frame_deliverer_->PaintAndDeliverNextFrame(elapsed_time_); |
| 636 BeepAndScheduleNextCapture(expected_execution_time); | 731 BeepAndScheduleNextCapture(expected_execution_time); |
| 637 } | 732 } |
| 638 | 733 |
| 639 } // namespace media | 734 } // namespace media |
| OLD | NEW |