Index: media/capture/video/fake_video_capture_device.cc |
diff --git a/media/capture/video/fake_video_capture_device.cc b/media/capture/video/fake_video_capture_device.cc |
index 7e46f68cab708272d74618b7b1b964b765df1556..e899464164485b20a98e97a941570a9b4a6e3210 100644 |
--- a/media/capture/video/fake_video_capture_device.cc |
+++ b/media/capture/video/fake_video_capture_device.cc |
@@ -29,6 +29,7 @@ |
namespace media { |
namespace { |
+ |
// Sweep at 600 deg/sec. |
static const float kPacmanAngularVelocity = 600; |
// Beep every 500 ms. |
@@ -39,54 +40,76 @@ static const float kGradientFrequency = 1.f / 5; |
static const double kMinZoom = 100.0; |
static const double kMaxZoom = 400.0; |
static const double kZoomStep = 1.0; |
-static const double kInitialZoom = 100.0; |
- |
-static const gfx::Size kSupportedSizesOrderedByIncreasingWidth[] = { |
- gfx::Size(96, 96), gfx::Size(320, 240), gfx::Size(640, 480), |
- gfx::Size(1280, 720), gfx::Size(1920, 1080)}; |
-static const int kSupportedSizesCount = |
- arraysize(kSupportedSizesOrderedByIncreasingWidth); |
- |
-static gfx::Size SnapToSupportedSize(const gfx::Size& requested_size) { |
- for (const gfx::Size& supported_size : |
- kSupportedSizesOrderedByIncreasingWidth) { |
- if (requested_size.width() <= supported_size.width()) |
- return supported_size; |
- } |
- return kSupportedSizesOrderedByIncreasingWidth[kSupportedSizesCount - 1]; |
-} |
- |
-// Represents the current state of a FakeVideoCaptureDevice. |
-// This is a separate struct because read-access to it is shared with several |
-// collaborating classes. |
-struct FakeDeviceState { |
- FakeDeviceState(float zoom, float frame_rate, VideoPixelFormat pixel_format) |
- : zoom(zoom), |
- format(gfx::Size(), frame_rate, pixel_format, PIXEL_STORAGE_CPU) {} |
- uint32_t zoom; |
- VideoCaptureFormat format; |
+enum class PixelFormatMatchType { |
+ EXACT, |
+ SUPPORTED_THROUGH_CONVERSION, |
+ INCOMPATIBLE |
}; |
-// Paints a "pacman-like" animated circle including textual information such |
-// as a frame count and timer. |
-class PacmanFramePainter { |
- public: |
- enum class Format { I420, SK_N32, Y16 }; |
- PacmanFramePainter(Format pixel_format, |
- const FakeDeviceState* fake_device_state); |
- |
- void PaintFrame(base::TimeDelta elapsed_time, uint8_t* target_buffer); |
+PixelFormatMatchType DetermineFormatMatchType( |
+ media::VideoPixelFormat supported_format, |
+ media::VideoPixelFormat requested_format) { |
+ if (requested_format == media::PIXEL_FORMAT_I420) { |
emircan
2017/03/01 18:58:58
Merge to a single if statement.
chfremer
2017/03/01 21:55:48
Done.
|
+ if (supported_format == media::PIXEL_FORMAT_MJPEG) |
+ return PixelFormatMatchType::SUPPORTED_THROUGH_CONVERSION; |
+ } |
+ return (requested_format == supported_format) |
+ ? PixelFormatMatchType::EXACT |
+ : PixelFormatMatchType::INCOMPATIBLE; |
+} |
- private: |
- void DrawGradientSquares(base::TimeDelta elapsed_time, |
- uint8_t* target_buffer); |
+bool CurrentFormatMatchIsWorseThanBest(PixelFormatMatchType current_match, |
+ PixelFormatMatchType best_match) { |
+ if (current_match == PixelFormatMatchType::INCOMPATIBLE) { |
emircan
2017/03/01 18:58:58
I don't think comparing like this is scalable. Can
chfremer
2017/03/01 21:55:48
Done.
|
+ return best_match == PixelFormatMatchType::EXACT || |
+ best_match == PixelFormatMatchType::SUPPORTED_THROUGH_CONVERSION; |
+ } |
+ if (current_match == PixelFormatMatchType::SUPPORTED_THROUGH_CONVERSION) |
+ return best_match == PixelFormatMatchType::EXACT; |
+ return false; |
+} |
- void DrawPacman(base::TimeDelta elapsed_time, uint8_t* target_buffer); |
+int FindIndexOfClosestSupportedFormat( |
+ const VideoCaptureFormat& requested_format, |
+ const VideoCaptureFormats& supported_formats) { |
+ int best_index = 0; |
+ PixelFormatMatchType best_format_match = PixelFormatMatchType::INCOMPATIBLE; |
+ int best_width_mismatch = std::numeric_limits<int>::max(); |
+ float best_frame_rate_mismatch = std::numeric_limits<float>::max(); |
+ for (int i = 0; i < static_cast<int>(supported_formats.size()); i++) { |
+ const auto& supported_format = supported_formats[i]; |
+ PixelFormatMatchType current_format_match = DetermineFormatMatchType( |
+ supported_format.pixel_format, requested_format.pixel_format); |
+ if (CurrentFormatMatchIsWorseThanBest(current_format_match, |
+ best_format_match)) { |
+ continue; |
+ } |
+ if (supported_format.frame_size.width() < |
+ requested_format.frame_size.width()) |
+ continue; |
+ const int current_width_mismatch = supported_format.frame_size.width() - |
+ requested_format.frame_size.width(); |
+ if (current_width_mismatch > best_width_mismatch) |
+ continue; |
+ const float current_frame_rate_mismatch = |
+ std::abs(supported_format.frame_rate - requested_format.frame_rate); |
+ if (current_width_mismatch < best_width_mismatch) { |
+ best_width_mismatch = current_width_mismatch; |
+ best_frame_rate_mismatch = current_frame_rate_mismatch; |
+ best_index = i; |
+ continue; |
+ } |
+ DCHECK_EQ(best_frame_rate_mismatch, current_frame_rate_mismatch); |
+ if (current_frame_rate_mismatch < best_frame_rate_mismatch) { |
+ best_frame_rate_mismatch = current_frame_rate_mismatch; |
+ best_index = i; |
+ } |
+ } |
+ return best_index; |
+} |
- const Format pixel_format_; |
- const FakeDeviceState* fake_device_state_ = nullptr; |
-}; |
+} // anonymous namespace |
// Paints and delivers frames to a client, which is set via Initialize(). |
class FrameDeliverer { |
@@ -100,10 +123,6 @@ class FrameDeliverer { |
client_ = std::move(client); |
device_state_ = device_state; |
} |
- virtual void Uninitialize() { |
- client_.reset(); |
- device_state_ = nullptr; |
- } |
virtual void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) = 0; |
protected: |
@@ -134,7 +153,6 @@ class OwnBufferFrameDeliverer : public FrameDeliverer { |
void Initialize(VideoPixelFormat pixel_format, |
std::unique_ptr<VideoCaptureDevice::Client> client, |
const FakeDeviceState* device_state) override; |
- void Uninitialize() override; |
void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override; |
private: |
@@ -158,7 +176,6 @@ class JpegEncodingFrameDeliverer : public FrameDeliverer { |
~JpegEncodingFrameDeliverer() override; |
// Implementation of FrameDeliveryStrategy |
- void Uninitialize() override; |
void PaintAndDeliverNextFrame(base::TimeDelta timestamp_to_paint) override; |
private: |
@@ -166,129 +183,57 @@ class JpegEncodingFrameDeliverer : public FrameDeliverer { |
std::vector<unsigned char> jpeg_buffer_; |
}; |
-// Implements the photo functionality of a VideoCaptureDevice |
-class FakePhotoDevice { |
- public: |
- FakePhotoDevice(std::unique_ptr<PacmanFramePainter> painter, |
- const FakeDeviceState* fake_device_state); |
- ~FakePhotoDevice(); |
- |
- void GetPhotoCapabilities( |
- VideoCaptureDevice::GetPhotoCapabilitiesCallback callback); |
- void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback, |
- base::TimeDelta elapsed_time); |
- |
- private: |
- const std::unique_ptr<PacmanFramePainter> painter_; |
- const FakeDeviceState* const fake_device_state_; |
-}; |
- |
-// Implementation of VideoCaptureDevice that generates test frames. This is |
-// useful for testing the video capture components without having to use real |
-// devices. The implementation schedules delayed tasks to itself to generate and |
-// deliver frames at the requested rate. |
-class FakeVideoCaptureDevice : public VideoCaptureDevice { |
- public: |
- FakeVideoCaptureDevice( |
- std::unique_ptr<FrameDeliverer> frame_delivery_strategy, |
- std::unique_ptr<FakePhotoDevice> photo_device, |
- std::unique_ptr<FakeDeviceState> device_state); |
- ~FakeVideoCaptureDevice() override; |
- |
- // VideoCaptureDevice implementation. |
- void AllocateAndStart(const VideoCaptureParams& params, |
- std::unique_ptr<Client> client) override; |
- void StopAndDeAllocate() override; |
- void GetPhotoCapabilities(GetPhotoCapabilitiesCallback callback) override; |
- void SetPhotoOptions(mojom::PhotoSettingsPtr settings, |
- SetPhotoOptionsCallback callback) override; |
- void TakePhoto(TakePhotoCallback callback) override; |
- |
- private: |
- void BeepAndScheduleNextCapture(base::TimeTicks expected_execution_time); |
- void OnNextFrameDue(base::TimeTicks expected_execution_time, int session_id); |
- |
- const std::unique_ptr<FrameDeliverer> frame_deliverer_; |
- const std::unique_ptr<FakePhotoDevice> photo_device_; |
- const std::unique_ptr<FakeDeviceState> device_state_; |
- int current_session_id_ = 0; |
- |
- // Time when the next beep occurs. |
- base::TimeDelta beep_time_; |
- // Time since the fake video started rendering frames. |
- base::TimeDelta elapsed_time_; |
- |
- base::ThreadChecker thread_checker_; |
- |
- // FakeVideoCaptureDevice post tasks to itself for frame construction and |
- // needs to deal with asynchronous StopAndDeallocate(). |
- base::WeakPtrFactory<FakeVideoCaptureDevice> weak_factory_; |
- |
- DISALLOW_COPY_AND_ASSIGN(FakeVideoCaptureDevice); |
-}; |
- |
-} // anonymous namespace |
- |
-// static |
-void FakeVideoCaptureDeviceMaker::GetSupportedSizes( |
- std::vector<gfx::Size>* supported_sizes) { |
- for (int i = 0; i < kSupportedSizesCount; i++) |
- supported_sizes->push_back(kSupportedSizesOrderedByIncreasingWidth[i]); |
-} |
+FrameDelivererFactory::FrameDelivererFactory( |
+ FakeVideoCaptureDevice::DeliveryMode delivery_mode, |
+ const FakeDeviceState* device_state) |
+ : delivery_mode_(delivery_mode), device_state_(device_state) {} |
-// static |
-std::unique_ptr<VideoCaptureDevice> FakeVideoCaptureDeviceMaker::MakeInstance( |
- PixelFormat pixel_format, |
- DeliveryMode delivery_mode, |
- float frame_rate) { |
- auto device_state = base::MakeUnique<FakeDeviceState>( |
- kInitialZoom, frame_rate, |
- static_cast<media::VideoPixelFormat>(pixel_format)); |
+std::unique_ptr<FrameDeliverer> FrameDelivererFactory::CreateFrameDeliverer( |
+ const VideoCaptureFormat& format) { |
PacmanFramePainter::Format painter_format; |
- switch (pixel_format) { |
- case PixelFormat::I420: |
+ switch (format.pixel_format) { |
+ case PIXEL_FORMAT_I420: |
painter_format = PacmanFramePainter::Format::I420; |
break; |
- case PixelFormat::Y16: |
+ case PIXEL_FORMAT_Y16: |
painter_format = PacmanFramePainter::Format::Y16; |
break; |
- case PixelFormat::MJPEG: |
+ case PIXEL_FORMAT_MJPEG: |
painter_format = PacmanFramePainter::Format::SK_N32; |
break; |
+ default: |
+ NOTREACHED(); |
+ painter_format = PacmanFramePainter::Format::I420; |
+ } |
+ auto frame_painter = |
+ base::MakeUnique<PacmanFramePainter>(painter_format, device_state_); |
+ |
+ FakeVideoCaptureDevice::DeliveryMode delivery_mode = delivery_mode_; |
+ if (format.pixel_format == PIXEL_FORMAT_MJPEG && |
+ delivery_mode_ == |
+ FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS) { |
+ DLOG(WARNING) << "PIXEL_FORMAT_MJPEG cannot be used in combination with " |
+ << "USE_CLIENT_PROVIDED_BUFFERS. Switching to " |
+ "USE_DEVICE_INTERNAL_BUFFERS."; |
emircan
2017/03/01 18:58:58
We should still return an error here to propagate
chfremer
2017/03/01 21:55:48
I tried out your suggestions but then ended up rev
emircan
2017/03/02 20:28:18
Sounds good.
|
+ delivery_mode = |
+ FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS; |
} |
- auto video_frame_painter = |
- base::MakeUnique<PacmanFramePainter>(painter_format, device_state.get()); |
- std::unique_ptr<FrameDeliverer> frame_delivery_strategy; |
switch (delivery_mode) { |
- case DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS: |
- if (pixel_format == PixelFormat::MJPEG) { |
- frame_delivery_strategy = base::MakeUnique<JpegEncodingFrameDeliverer>( |
- std::move(video_frame_painter)); |
+ case FakeVideoCaptureDevice::DeliveryMode::USE_DEVICE_INTERNAL_BUFFERS: |
+ if (format.pixel_format == PIXEL_FORMAT_MJPEG) { |
+ return base::MakeUnique<JpegEncodingFrameDeliverer>( |
+ std::move(frame_painter)); |
} else { |
- frame_delivery_strategy = base::MakeUnique<OwnBufferFrameDeliverer>( |
- std::move(video_frame_painter)); |
- } |
- break; |
- case DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS: |
- if (pixel_format == PixelFormat::MJPEG) { |
- DLOG(ERROR) << "PixelFormat::MJPEG cannot be used in combination with " |
- << "USE_CLIENT_PROVIDED_BUFFERS."; |
- return nullptr; |
+ return base::MakeUnique<OwnBufferFrameDeliverer>( |
+ std::move(frame_painter)); |
} |
- frame_delivery_strategy = base::MakeUnique<ClientBufferFrameDeliverer>( |
- std::move(video_frame_painter)); |
- break; |
+ case FakeVideoCaptureDevice::DeliveryMode::USE_CLIENT_PROVIDED_BUFFERS: |
+ return base::MakeUnique<ClientBufferFrameDeliverer>( |
+ std::move(frame_painter)); |
} |
- |
- auto photo_frame_painter = base::MakeUnique<PacmanFramePainter>( |
- PacmanFramePainter::Format::SK_N32, device_state.get()); |
- auto photo_device = base::MakeUnique<FakePhotoDevice>( |
- std::move(photo_frame_painter), device_state.get()); |
- |
- return base::MakeUnique<FakeVideoCaptureDevice>( |
- std::move(frame_delivery_strategy), std::move(photo_device), |
- std::move(device_state)); |
+ NOTREACHED(); |
+ return nullptr; |
} |
PacmanFramePainter::PacmanFramePainter(Format pixel_format, |
@@ -427,9 +372,11 @@ void PacmanFramePainter::DrawPacman(base::TimeDelta elapsed_time, |
} |
} |
-FakePhotoDevice::FakePhotoDevice(std::unique_ptr<PacmanFramePainter> painter, |
- const FakeDeviceState* fake_device_state) |
- : painter_(std::move(painter)), fake_device_state_(fake_device_state) {} |
+FakePhotoDevice::FakePhotoDevice( |
+ std::unique_ptr<PacmanFramePainter> sk_n32_painter, |
+ const FakeDeviceState* fake_device_state) |
+ : sk_n32_painter_(std::move(sk_n32_painter)), |
+ fake_device_state_(fake_device_state) {} |
FakePhotoDevice::~FakePhotoDevice() = default; |
@@ -440,7 +387,7 @@ void FakePhotoDevice::TakePhoto(VideoCaptureDevice::TakePhotoCallback callback, |
PIXEL_FORMAT_ARGB, fake_device_state_->format.frame_size); |
std::unique_ptr<uint8_t[]> buffer(new uint8_t[required_sk_n32_buffer_size]); |
memset(buffer.get(), 0, required_sk_n32_buffer_size); |
- painter_->PaintFrame(elapsed_time, buffer.get()); |
+ sk_n32_painter_->PaintFrame(elapsed_time, buffer.get()); |
mojom::BlobPtr blob = mojom::Blob::New(); |
const gfx::PNGCodec::ColorFormat encoding_source_format = |
(kN32_SkColorType == kRGBA_8888_SkColorType) ? gfx::PNGCodec::FORMAT_RGBA |
@@ -459,10 +406,12 @@ void FakePhotoDevice::TakePhoto(VideoCaptureDevice::TakePhotoCallback callback, |
} |
FakeVideoCaptureDevice::FakeVideoCaptureDevice( |
- std::unique_ptr<FrameDeliverer> frame_delivery_strategy, |
+ const VideoCaptureFormats& supported_formats, |
+ std::unique_ptr<FrameDelivererFactory> frame_deliverer_factory, |
std::unique_ptr<FakePhotoDevice> photo_device, |
std::unique_ptr<FakeDeviceState> device_state) |
- : frame_deliverer_(std::move(frame_delivery_strategy)), |
+ : supported_formats_(supported_formats), |
+ frame_deliverer_factory_(std::move(frame_deliverer_factory)), |
photo_device_(std::move(photo_device)), |
device_state_(std::move(device_state)), |
weak_factory_(this) {} |
@@ -476,10 +425,16 @@ void FakeVideoCaptureDevice::AllocateAndStart( |
std::unique_ptr<VideoCaptureDevice::Client> client) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
+ const int closest_supported_format_index = FindIndexOfClosestSupportedFormat( |
+ params.requested_format, supported_formats_); |
+ const VideoCaptureFormat& selected_format = |
+ supported_formats_[closest_supported_format_index]; |
emircan
2017/03/01 18:58:58
Why don't we make the function FindClosestSupporte
chfremer
2017/03/01 21:55:48
Done.
|
+ |
beep_time_ = base::TimeDelta(); |
elapsed_time_ = base::TimeDelta(); |
- device_state_->format.frame_size = |
- SnapToSupportedSize(params.requested_format.frame_size); |
+ frame_deliverer_ = |
+ frame_deliverer_factory_->CreateFrameDeliverer(selected_format); |
+ device_state_->format.frame_size = selected_format.frame_size; |
frame_deliverer_->Initialize(device_state_->format.pixel_format, |
std::move(client), device_state_.get()); |
current_session_id_++; |
@@ -491,7 +446,7 @@ void FakeVideoCaptureDevice::StopAndDeAllocate() { |
// Invalidate WeakPtr to stop the perpetual scheduling of tasks. |
weak_factory_.InvalidateWeakPtrs(); |
- frame_deliverer_->Uninitialize(); |
+ frame_deliverer_.reset(); |
} |
void FakeVideoCaptureDevice::GetPhotoCapabilities( |
@@ -574,11 +529,6 @@ void OwnBufferFrameDeliverer::Initialize( |
pixel_format, device_state->format.frame_size)]); |
} |
-void OwnBufferFrameDeliverer::Uninitialize() { |
- FrameDeliverer::Uninitialize(); |
- buffer_.reset(); |
-} |
- |
void OwnBufferFrameDeliverer::PaintAndDeliverNextFrame( |
base::TimeDelta timestamp_to_paint) { |
if (!client()) |
@@ -631,12 +581,6 @@ JpegEncodingFrameDeliverer::JpegEncodingFrameDeliverer( |
JpegEncodingFrameDeliverer::~JpegEncodingFrameDeliverer() = default; |
-void JpegEncodingFrameDeliverer::Uninitialize() { |
- FrameDeliverer::Uninitialize(); |
- sk_n32_buffer_.clear(); |
- jpeg_buffer_.clear(); |
-} |
- |
void JpegEncodingFrameDeliverer::PaintAndDeliverNextFrame( |
base::TimeDelta timestamp_to_paint) { |
if (!client()) |