Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / media / video / capture / file_video_capture_device.cc
blobbdcc66f03f76a1802662a766bb60212f4f852970
1 // Copyright 2013 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.
5 #include "media/video/capture/file_video_capture_device.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "media/base/video_capture_types.h"
15 namespace media {
16 static const int kY4MHeaderMaxSize = 200;
17 static const char kY4MSimpleFrameDelimiter[] = "FRAME";
18 static const int kY4MSimpleFrameDelimiterSize = 6;
20 int ParseY4MInt(const base::StringPiece& token) {
21 int temp_int;
22 CHECK(base::StringToInt(token, &temp_int)) << token;
23 return temp_int;
26 // Extract numerator and denominator out of a token that must have the aspect
27 // numerator:denominator, both integer numbers.
28 void ParseY4MRational(const base::StringPiece& token,
29 int* numerator,
30 int* denominator) {
31 size_t index_divider = token.find(':');
32 CHECK_NE(index_divider, token.npos);
33 *numerator = ParseY4MInt(token.substr(0, index_divider));
34 *denominator = ParseY4MInt(token.substr(index_divider + 1, token.length()));
35 CHECK(*denominator);
38 // This function parses the ASCII string in |header| as belonging to a Y4M file,
39 // returning the collected format in |video_format|. For a non authoritative
40 // explanation of the header format, check
41 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2
42 // Restrictions: Only interlaced I420 pixel format is supported, and pixel
43 // aspect ratio is ignored.
44 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace)
45 // character, however all examples mentioned in the Y4M header description end
46 // with a newline character instead. Also, some headers do _not_ specify pixel
47 // format, in this case it means I420.
48 // This code was inspired by third_party/libvpx/.../y4minput.* .
49 void ParseY4MTags(const std::string& file_header,
50 media::VideoCaptureFormat* video_format) {
51 video_format->pixel_format = media::PIXEL_FORMAT_I420;
52 video_format->frame_size.set_width(0);
53 video_format->frame_size.set_height(0);
54 size_t index = 0;
55 size_t blank_position = 0;
56 base::StringPiece token;
57 while ((blank_position = file_header.find_first_of("\n ", index)) !=
58 std::string::npos) {
59 // Every token is supposed to have an identifier letter and a bunch of
60 // information immediately after, which we extract into a |token| here.
61 token =
62 base::StringPiece(&file_header[index + 1], blank_position - index - 1);
63 CHECK(!token.empty());
64 switch (file_header[index]) {
65 case 'W':
66 video_format->frame_size.set_width(ParseY4MInt(token));
67 break;
68 case 'H':
69 video_format->frame_size.set_height(ParseY4MInt(token));
70 break;
71 case 'F': {
72 // If the token is "FRAME", it means we have finished with the header.
73 if (token[0] == 'R')
74 break;
75 int fps_numerator, fps_denominator;
76 ParseY4MRational(token, &fps_numerator, &fps_denominator);
77 video_format->frame_rate = fps_numerator / fps_denominator;
78 break;
80 case 'I':
81 // Interlacing is ignored, but we don't like mixed modes.
82 CHECK_NE(token[0], 'm');
83 break;
84 case 'A':
85 // Pixel aspect ratio ignored.
86 break;
87 case 'C':
88 CHECK(token == "420" || token == "420jpeg" || token == "420paldv")
89 << token; // Only I420 is supported, and we fudge the variants.
90 break;
91 default:
92 break;
94 // We're done if we have found a newline character right after the token.
95 if (file_header[blank_position] == '\n')
96 break;
97 index = blank_position + 1;
99 // Last video format semantic correctness check before sending it back.
100 CHECK(video_format->IsValid());
103 // Reads and parses the header of a Y4M |file|, returning the collected pixel
104 // format in |video_format|. Returns the index of the first byte of the first
105 // video frame.
106 // Restrictions: Only trivial per-frame headers are supported.
107 // static
108 int64 FileVideoCaptureDevice::ParseFileAndExtractVideoFormat(
109 base::File* file,
110 media::VideoCaptureFormat* video_format) {
111 std::string header(kY4MHeaderMaxSize, 0);
112 file->Read(0, &header[0], kY4MHeaderMaxSize - 1);
114 size_t header_end = header.find(kY4MSimpleFrameDelimiter);
115 CHECK_NE(header_end, header.npos);
117 ParseY4MTags(header, video_format);
118 return header_end + kY4MSimpleFrameDelimiterSize;
121 // Opens a given file for reading, and returns the file to the caller, who is
122 // responsible for closing it.
123 // static
124 base::File FileVideoCaptureDevice::OpenFileForRead(
125 const base::FilePath& file_path) {
126 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
127 DLOG_IF(ERROR, file.IsValid())
128 << file_path.value()
129 << ", error: " << base::File::ErrorToString(file.error_details());
130 return file.Pass();
133 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
134 : capture_thread_("CaptureThread"),
135 file_path_(file_path),
136 frame_size_(0),
137 current_byte_index_(0),
138 first_frame_byte_index_(0) {}
140 FileVideoCaptureDevice::~FileVideoCaptureDevice() {
141 DCHECK(thread_checker_.CalledOnValidThread());
142 // Check if the thread is running.
143 // This means that the device have not been DeAllocated properly.
144 CHECK(!capture_thread_.IsRunning());
147 void FileVideoCaptureDevice::AllocateAndStart(
148 const VideoCaptureParams& params,
149 scoped_ptr<VideoCaptureDevice::Client> client) {
150 DCHECK(thread_checker_.CalledOnValidThread());
151 CHECK(!capture_thread_.IsRunning());
153 capture_thread_.Start();
154 capture_thread_.message_loop()->PostTask(
155 FROM_HERE,
156 base::Bind(&FileVideoCaptureDevice::OnAllocateAndStart,
157 base::Unretained(this),
158 params,
159 base::Passed(&client)));
162 void FileVideoCaptureDevice::StopAndDeAllocate() {
163 DCHECK(thread_checker_.CalledOnValidThread());
164 CHECK(capture_thread_.IsRunning());
166 capture_thread_.message_loop()->PostTask(
167 FROM_HERE,
168 base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate,
169 base::Unretained(this)));
170 capture_thread_.Stop();
173 int FileVideoCaptureDevice::CalculateFrameSize() const {
174 DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
175 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
176 return capture_format_.ImageAllocationSize();
179 void FileVideoCaptureDevice::OnAllocateAndStart(
180 const VideoCaptureParams& params,
181 scoped_ptr<VideoCaptureDevice::Client> client) {
182 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
184 client_ = client.Pass();
186 // Open the file and parse the header. Get frame size and format.
187 DCHECK(!file_.IsValid());
188 file_ = OpenFileForRead(file_path_);
189 if (!file_.IsValid()) {
190 client_->OnError("Could not open Video file");
191 return;
193 first_frame_byte_index_ =
194 ParseFileAndExtractVideoFormat(&file_, &capture_format_);
195 current_byte_index_ = first_frame_byte_index_;
196 DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString()
197 << ", fps: " << capture_format_.frame_rate;
199 frame_size_ = CalculateFrameSize();
200 video_frame_.reset(new uint8[frame_size_]);
202 capture_thread_.message_loop()->PostTask(
203 FROM_HERE,
204 base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
205 base::Unretained(this)));
208 void FileVideoCaptureDevice::OnStopAndDeAllocate() {
209 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
210 file_.Close();
211 client_.reset();
212 current_byte_index_ = 0;
213 first_frame_byte_index_ = 0;
214 frame_size_ = 0;
215 video_frame_.reset();
218 void FileVideoCaptureDevice::OnCaptureTask() {
219 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
220 if (!client_)
221 return;
222 const base::TimeTicks timestamp_before_reading = base::TimeTicks::Now();
223 int result = file_.Read(current_byte_index_,
224 reinterpret_cast<char*>(video_frame_.get()),
225 frame_size_);
227 // If we passed EOF to base::File, it will return 0 read characters. In that
228 // case, reset the pointer and read again.
229 if (result != frame_size_) {
230 CHECK_EQ(result, 0);
231 current_byte_index_ = first_frame_byte_index_;
232 CHECK_EQ(file_.Read(current_byte_index_,
233 reinterpret_cast<char*>(video_frame_.get()),
234 frame_size_),
235 frame_size_);
236 } else {
237 current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize;
240 // Give the captured frame to the client.
241 client_->OnIncomingCapturedData(video_frame_.get(),
242 frame_size_,
243 capture_format_,
245 base::TimeTicks::Now());
246 // Reschedule next CaptureTask.
247 const base::TimeDelta frame_interval =
248 base::TimeDelta::FromMicroseconds(1E6 / capture_format_.frame_rate);
249 base::TimeDelta next_on_capture_timedelta = frame_interval -
250 (base::TimeTicks::Now() - timestamp_before_reading);
251 if (next_on_capture_timedelta.InMilliseconds() < 0) {
252 DLOG(WARNING) << "Frame reading took longer than the frame interval.";
253 next_on_capture_timedelta = frame_interval;
255 base::MessageLoop::current()->PostDelayedTask(
256 FROM_HERE,
257 base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
258 base::Unretained(this)),
259 next_on_capture_timedelta);
262 } // namespace media