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 "media/base/video_frame.h" 14 #include "third_party/skia/include/core/SkBitmap.h" 15 #include "third_party/skia/include/core/SkCanvas.h" 16 #include "third_party/skia/include/core/SkPaint.h" 17 18 namespace media { 19 20 static const int kFakeCaptureBeepCycle = 10; // Visual beep every 0.5s. 21 static const int kFakeCaptureCapabilityChangePeriod = 30; 22 23 FakeVideoCaptureDevice::FakeVideoCaptureDevice() 24 : capture_thread_("CaptureThread"), 25 frame_count_(0), 26 format_roster_index_(0) {} 27 28 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() { 29 DCHECK(thread_checker_.CalledOnValidThread()); 30 DCHECK(!capture_thread_.IsRunning()); 31 } 32 33 void FakeVideoCaptureDevice::AllocateAndStart( 34 const VideoCaptureParams& params, 35 scoped_ptr<VideoCaptureDevice::Client> client) { 36 DCHECK(thread_checker_.CalledOnValidThread()); 37 DCHECK(!capture_thread_.IsRunning()); 38 39 capture_thread_.Start(); 40 capture_thread_.message_loop()->PostTask( 41 FROM_HERE, 42 base::Bind(&FakeVideoCaptureDevice::OnAllocateAndStart, 43 base::Unretained(this), 44 params, 45 base::Passed(&client))); 46 } 47 48 void FakeVideoCaptureDevice::StopAndDeAllocate() { 49 DCHECK(thread_checker_.CalledOnValidThread()); 50 DCHECK(capture_thread_.IsRunning()); 51 capture_thread_.message_loop()->PostTask( 52 FROM_HERE, 53 base::Bind(&FakeVideoCaptureDevice::OnStopAndDeAllocate, 54 base::Unretained(this))); 55 capture_thread_.Stop(); 56 } 57 58 void FakeVideoCaptureDevice::PopulateVariableFormatsRoster( 59 const VideoCaptureFormats& formats) { 60 DCHECK(thread_checker_.CalledOnValidThread()); 61 DCHECK(!capture_thread_.IsRunning()); 62 format_roster_ = formats; 63 format_roster_index_ = 0; 64 } 65 66 void FakeVideoCaptureDevice::OnAllocateAndStart( 67 const VideoCaptureParams& params, 68 scoped_ptr<VideoCaptureDevice::Client> client) { 69 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 70 client_ = client.Pass(); 71 72 // Incoming |params| can be none of the supported formats, so we get the 73 // closest thing rounded up. TODO(mcasas): Use the |params|, if they belong to 74 // the supported ones, when http://crbug.com/309554 is verified. 75 DCHECK_EQ(params.requested_format.pixel_format, PIXEL_FORMAT_I420); 76 capture_format_.pixel_format = params.requested_format.pixel_format; 77 capture_format_.frame_rate = 30; 78 if (params.requested_format.frame_size.width() > 640) 79 capture_format_.frame_size.SetSize(1280, 720); 80 else if (params.requested_format.frame_size.width() > 320) 81 capture_format_.frame_size.SetSize(640, 480); 82 else 83 capture_format_.frame_size.SetSize(320, 240); 84 const size_t fake_frame_size = 85 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 86 fake_frame_.reset(new uint8[fake_frame_size]); 87 88 capture_thread_.message_loop()->PostTask( 89 FROM_HERE, 90 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask, 91 base::Unretained(this))); 92 } 93 94 void FakeVideoCaptureDevice::OnStopAndDeAllocate() { 95 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 96 client_.reset(); 97 } 98 99 void FakeVideoCaptureDevice::OnCaptureTask() { 100 if (!client_) 101 return; 102 103 const size_t frame_size = 104 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 105 memset(fake_frame_.get(), 0, frame_size); 106 107 SkBitmap bitmap; 108 bitmap.setConfig(SkBitmap::kA8_Config, 109 capture_format_.frame_size.width(), 110 capture_format_.frame_size.height(), 111 capture_format_.frame_size.width()), 112 bitmap.setPixels(fake_frame_.get()); 113 SkCanvas canvas(bitmap); 114 115 // Draw a sweeping circle to show an animation. 116 int radius = std::min(capture_format_.frame_size.width(), 117 capture_format_.frame_size.height()) / 4; 118 SkRect rect = 119 SkRect::MakeXYWH(capture_format_.frame_size.width() / 2 - radius, 120 capture_format_.frame_size.height() / 2 - radius, 121 2 * radius, 122 2 * radius); 123 124 SkPaint paint; 125 paint.setStyle(SkPaint::kFill_Style); 126 127 // Only Y plane is being drawn and this gives 50% grey on the Y 128 // plane. The result is a light green color in RGB space. 129 paint.setAlpha(128); 130 131 int end_angle = (frame_count_ % kFakeCaptureBeepCycle * 360) / 132 kFakeCaptureBeepCycle; 133 if (!end_angle) 134 end_angle = 360; 135 canvas.drawArc(rect, 0, end_angle, true, paint); 136 137 // Draw current time. 138 int elapsed_ms = kFakeCaptureTimeoutMs * frame_count_; 139 int milliseconds = elapsed_ms % 1000; 140 int seconds = (elapsed_ms / 1000) % 60; 141 int minutes = (elapsed_ms / 1000 / 60) % 60; 142 int hours = (elapsed_ms / 1000 / 60 / 60) % 60; 143 144 std::string time_string = 145 base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes, 146 seconds, milliseconds, frame_count_); 147 canvas.scale(3, 3); 148 canvas.drawText(time_string.data(), time_string.length(), 30, 20, 149 paint); 150 151 if (frame_count_ % kFakeCaptureBeepCycle == 0) { 152 // Generate a synchronized beep sound if there is one audio input 153 // stream created. 154 FakeAudioInputStream::BeepOnce(); 155 } 156 157 frame_count_++; 158 159 // Give the captured frame to the client. 160 client_->OnIncomingCapturedData(fake_frame_.get(), 161 frame_size, 162 capture_format_, 163 0, 164 base::TimeTicks::Now()); 165 if (!(frame_count_ % kFakeCaptureCapabilityChangePeriod) && 166 format_roster_.size() > 0U) { 167 Reallocate(); 168 } 169 // Reschedule next CaptureTask. 170 capture_thread_.message_loop()->PostDelayedTask( 171 FROM_HERE, 172 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask, 173 base::Unretained(this)), 174 base::TimeDelta::FromMilliseconds(kFakeCaptureTimeoutMs)); 175 } 176 177 void FakeVideoCaptureDevice::Reallocate() { 178 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 179 capture_format_ = 180 format_roster_.at(++format_roster_index_ % format_roster_.size()); 181 DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420); 182 DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution " 183 << capture_format_.frame_size.ToString(); 184 185 const size_t fake_frame_size = 186 VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size); 187 fake_frame_.reset(new uint8[fake_frame_size]); 188 } 189 190 } // namespace media 191