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 3 // found in the LICENSE file. 4 5 #include "media/video/capture/fake_video_capture_device.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/strings/stringprintf.h" 12 #include "media/audio/fake_audio_input_stream.h" 13 #include "third_party/skia/include/core/SkBitmap.h" 14 #include "third_party/skia/include/core/SkCanvas.h" 15 #include "third_party/skia/include/core/SkPaint.h" 16 17 namespace media { 18 19 static const int kFakeCaptureTimeoutMs = 50; 20 static const int kFakeCaptureBeepCycle = 20; // Visual beep every 1s. 21 static const int kFakeCaptureCapabilityChangePeriod = 30; 22 enum { kNumberOfFakeDevices = 2 }; 23 24 bool FakeVideoCaptureDevice::fail_next_create_ = false; 25 base::subtle::Atomic32 FakeVideoCaptureDevice::number_of_devices_ = 26 kNumberOfFakeDevices; 27 28 // static 29 size_t FakeVideoCaptureDevice::NumberOfFakeDevices(void) { 30 return number_of_devices_; 31 } 32 33 // static 34 void FakeVideoCaptureDevice::GetDeviceNames(Names* const device_names) { 35 // Empty the name list. 36 device_names->erase(device_names->begin(), device_names->end()); 37 38 int number_of_devices = base::subtle::NoBarrier_Load(&number_of_devices_); 39 for (int32 n = 0; n < number_of_devices; n++) { 40 Name name(base::StringPrintf("fake_device_%d", n), 41 base::StringPrintf("/dev/video%d", n)); 42 device_names->push_back(name); 43 } 44 } 45 46 // static 47 void FakeVideoCaptureDevice::GetDeviceSupportedFormats( 48 const Name& device, 49 VideoCaptureFormats* supported_formats) { 50 51 supported_formats->clear(); 52 VideoCaptureFormat capture_format_640x480; 53 capture_format_640x480.pixel_format = media::PIXEL_FORMAT_I420; 54 capture_format_640x480.frame_size.SetSize(640, 480); 55 capture_format_640x480.frame_rate = 1000 / kFakeCaptureTimeoutMs; 56 supported_formats->push_back(capture_format_640x480); 57 VideoCaptureFormat capture_format_320x240; 58 capture_format_320x240.pixel_format = media::PIXEL_FORMAT_I420; 59 capture_format_320x240.frame_size.SetSize(320, 240); 60 capture_format_320x240.frame_rate = 1000 / kFakeCaptureTimeoutMs; 61 supported_formats->push_back(capture_format_320x240); 62 } 63 64 // static 65 VideoCaptureDevice* FakeVideoCaptureDevice::Create(const Name& device_name) { 66 if (fail_next_create_) { 67 fail_next_create_ = false; 68 return NULL; 69 } 70 int number_of_devices = base::subtle::NoBarrier_Load(&number_of_devices_); 71 for (int32 n = 0; n < number_of_devices; ++n) { 72 std::string possible_id = base::StringPrintf("/dev/video%d", n); 73 if (device_name.id().compare(possible_id) == 0) { 74 return new FakeVideoCaptureDevice(); 75 } 76 } 77 return NULL; 78 } 79 80 // static 81 void FakeVideoCaptureDevice::SetFailNextCreate() { 82 fail_next_create_ = true; 83 } 84 85 // static 86 void FakeVideoCaptureDevice::SetNumberOfFakeDevices(size_t number_of_devices) { 87 base::subtle::NoBarrier_AtomicExchange(&number_of_devices_, 88 number_of_devices); 89 } 90 91 FakeVideoCaptureDevice::FakeVideoCaptureDevice() 92 : capture_thread_("CaptureThread"), 93 frame_count_(0), 94 format_roster_index_(0) {} 95 96 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() { 97 DCHECK(thread_checker_.CalledOnValidThread()); 98 DCHECK(!capture_thread_.IsRunning()); 99 } 100 101 void FakeVideoCaptureDevice::AllocateAndStart( 102 const VideoCaptureParams& params, 103 scoped_ptr<VideoCaptureDevice::Client> client) { 104 DCHECK(thread_checker_.CalledOnValidThread()); 105 DCHECK(!capture_thread_.IsRunning()); 106 107 capture_thread_.Start(); 108 capture_thread_.message_loop()->PostTask( 109 FROM_HERE, 110 base::Bind(&FakeVideoCaptureDevice::OnAllocateAndStart, 111 base::Unretained(this), 112 params, 113 base::Passed(&client))); 114 } 115 116 void FakeVideoCaptureDevice::StopAndDeAllocate() { 117 DCHECK(thread_checker_.CalledOnValidThread()); 118 DCHECK(capture_thread_.IsRunning()); 119 capture_thread_.message_loop()->PostTask( 120 FROM_HERE, 121 base::Bind(&FakeVideoCaptureDevice::OnStopAndDeAllocate, 122 base::Unretained(this))); 123 capture_thread_.Stop(); 124 } 125 126 void FakeVideoCaptureDevice::OnAllocateAndStart( 127 const VideoCaptureParams& params, 128 scoped_ptr<VideoCaptureDevice::Client> client) { 129 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 130 client_ = client.Pass(); 131 capture_format_.pixel_format = PIXEL_FORMAT_I420; 132 capture_format_.frame_rate = 30; 133 if (params.requested_format.frame_size.width() > 320) 134 capture_format_.frame_size.SetSize(640, 480); 135 else 136 capture_format_.frame_size.SetSize(320, 240); 137 if (params.allow_resolution_change) 138 PopulateFormatRoster(); 139 const size_t fake_frame_size = 140 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 141 fake_frame_.reset(new uint8[fake_frame_size]); 142 143 capture_thread_.message_loop()->PostTask( 144 FROM_HERE, 145 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask, 146 base::Unretained(this))); 147 } 148 149 void FakeVideoCaptureDevice::OnStopAndDeAllocate() { 150 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 151 client_.reset(); 152 } 153 154 void FakeVideoCaptureDevice::OnCaptureTask() { 155 if (!client_) 156 return; 157 158 const size_t frame_size = 159 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 160 memset(fake_frame_.get(), 0, frame_size); 161 162 SkBitmap bitmap; 163 bitmap.setConfig(SkBitmap::kA8_Config, 164 capture_format_.frame_size.width(), 165 capture_format_.frame_size.height(), 166 capture_format_.frame_size.width()), 167 bitmap.setPixels(fake_frame_.get()); 168 169 SkCanvas canvas(bitmap); 170 171 // Draw a sweeping circle to show an animation. 172 int radius = std::min(capture_format_.frame_size.width(), 173 capture_format_.frame_size.height()) / 174 4; 175 SkRect rect = 176 SkRect::MakeXYWH(capture_format_.frame_size.width() / 2 - radius, 177 capture_format_.frame_size.height() / 2 - radius, 178 2 * radius, 179 2 * radius); 180 181 SkPaint paint; 182 paint.setStyle(SkPaint::kFill_Style); 183 184 // Only Y plane is being drawn and this gives 50% grey on the Y 185 // plane. The result is a light green color in RGB space. 186 paint.setAlpha(128); 187 188 int end_angle = (frame_count_ % kFakeCaptureBeepCycle * 360) / 189 kFakeCaptureBeepCycle; 190 if (!end_angle) 191 end_angle = 360; 192 canvas.drawArc(rect, 0, end_angle, true, paint); 193 194 // Draw current time. 195 int elapsed_ms = kFakeCaptureTimeoutMs * frame_count_; 196 int milliseconds = elapsed_ms % 1000; 197 int seconds = (elapsed_ms / 1000) % 60; 198 int minutes = (elapsed_ms / 1000 / 60) % 60; 199 int hours = (elapsed_ms / 1000 / 60 / 60) % 60; 200 201 std::string time_string = 202 base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes, 203 seconds, milliseconds, frame_count_); 204 canvas.scale(3, 3); 205 canvas.drawText(time_string.data(), time_string.length(), 30, 20, 206 paint); 207 208 if (frame_count_ % kFakeCaptureBeepCycle == 0) { 209 // Generate a synchronized beep sound if there is one audio input 210 // stream created. 211 FakeAudioInputStream::BeepOnce(); 212 } 213 214 frame_count_++; 215 216 // Give the captured frame to the client. 217 client_->OnIncomingCapturedFrame(fake_frame_.get(), 218 frame_size, 219 base::Time::Now(), 220 0, 221 capture_format_); 222 if (!(frame_count_ % kFakeCaptureCapabilityChangePeriod) && 223 format_roster_.size() > 0U) { 224 Reallocate(); 225 } 226 // Reschedule next CaptureTask. 227 capture_thread_.message_loop()->PostDelayedTask( 228 FROM_HERE, 229 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask, 230 base::Unretained(this)), 231 base::TimeDelta::FromMilliseconds(kFakeCaptureTimeoutMs)); 232 } 233 234 void FakeVideoCaptureDevice::Reallocate() { 235 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 236 capture_format_ = 237 format_roster_.at(++format_roster_index_ % format_roster_.size()); 238 DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420); 239 DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution " 240 << capture_format_.frame_size.ToString(); 241 242 const size_t fake_frame_size = 243 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 244 fake_frame_.reset(new uint8[fake_frame_size]); 245 } 246 247 void FakeVideoCaptureDevice::PopulateFormatRoster() { 248 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 249 format_roster_.push_back( 250 media::VideoCaptureFormat(gfx::Size(320, 240), 30, PIXEL_FORMAT_I420)); 251 format_roster_.push_back( 252 media::VideoCaptureFormat(gfx::Size(640, 480), 30, PIXEL_FORMAT_I420)); 253 format_roster_.push_back( 254 media::VideoCaptureFormat(gfx::Size(800, 600), 30, PIXEL_FORMAT_I420)); 255 256 format_roster_index_ = 0; 257 } 258 259 } // namespace media 260