Split out mojo_converters into its own target.
[chromium-blink-merge.git] / media / video / capture / file_video_capture_device.cc
blob84a2d156000fbfbcb42398af4f7bd338590d55c4
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"
14 namespace media {
15 static const int kY4MHeaderMaxSize = 200;
16 static const char kY4MSimpleFrameDelimiter[] = "FRAME";
17 static const int kY4MSimpleFrameDelimiterSize = 6;
19 int ParseY4MInt(const base::StringPiece& token) {
20 int temp_int;
21 CHECK(base::StringToInt(token, &temp_int)) << token;
22 return temp_int;
25 // Extract numerator and denominator out of a token that must have the aspect
26 // numerator:denominator, both integer numbers.
27 void ParseY4MRational(const base::StringPiece& token,
28 int* numerator,
29 int* denominator) {
30 size_t index_divider = token.find(':');
31 CHECK_NE(index_divider, token.npos);
32 *numerator = ParseY4MInt(token.substr(0, index_divider));
33 *denominator = ParseY4MInt(token.substr(index_divider + 1, token.length()));
34 CHECK(*denominator);
37 // This function parses the ASCII string in |header| as belonging to a Y4M file,
38 // returning the collected format in |video_format|. For a non authoritative
39 // explanation of the header format, check
40 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2
41 // Restrictions: Only interlaced I420 pixel format is supported, and pixel
42 // aspect ratio is ignored.
43 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace)
44 // character, however all examples mentioned in the Y4M header description end
45 // with a newline character instead. Also, some headers do _not_ specify pixel
46 // format, in this case it means I420.
47 // This code was inspired by third_party/libvpx/.../y4minput.* .
48 void ParseY4MTags(const std::string& file_header,
49 media::VideoCaptureFormat* video_format) {
50 video_format->pixel_format = media::PIXEL_FORMAT_I420;
51 video_format->frame_size.set_width(0);
52 video_format->frame_size.set_height(0);
53 size_t index = 0;
54 size_t blank_position = 0;
55 base::StringPiece token;
56 while ((blank_position = file_header.find_first_of("\n ", index)) !=
57 std::string::npos) {
58 // Every token is supposed to have an identifier letter and a bunch of
59 // information immediately after, which we extract into a |token| here.
60 token =
61 base::StringPiece(&file_header[index + 1], blank_position - index - 1);
62 CHECK(!token.empty());
63 switch (file_header[index]) {
64 case 'W':
65 video_format->frame_size.set_width(ParseY4MInt(token));
66 break;
67 case 'H':
68 video_format->frame_size.set_height(ParseY4MInt(token));
69 break;
70 case 'F': {
71 // If the token is "FRAME", it means we have finished with the header.
72 if (token[0] == 'R')
73 break;
74 int fps_numerator, fps_denominator;
75 ParseY4MRational(token, &fps_numerator, &fps_denominator);
76 video_format->frame_rate = fps_numerator / fps_denominator;
77 break;
79 case 'I':
80 // Interlacing is ignored, but we don't like mixed modes.
81 CHECK_NE(token[0], 'm');
82 break;
83 case 'A':
84 // Pixel aspect ratio ignored.
85 break;
86 case 'C':
87 CHECK(token == "420" || token == "420jpeg" || token == "420paldv")
88 << token; // Only I420 is supported, and we fudge the variants.
89 break;
90 default:
91 break;
93 // We're done if we have found a newline character right after the token.
94 if (file_header[blank_position] == '\n')
95 break;
96 index = blank_position + 1;
98 // Last video format semantic correctness check before sending it back.
99 CHECK(video_format->IsValid());
102 // Reads and parses the header of a Y4M |file|, returning the collected pixel
103 // format in |video_format|. Returns the index of the first byte of the first
104 // video frame.
105 // Restrictions: Only trivial per-frame headers are supported.
106 // static
107 int64 FileVideoCaptureDevice::ParseFileAndExtractVideoFormat(
108 base::File* file,
109 media::VideoCaptureFormat* video_format) {
110 std::string header(kY4MHeaderMaxSize, 0);
111 file->Read(0, &header[0], kY4MHeaderMaxSize - 1);
113 size_t header_end = header.find(kY4MSimpleFrameDelimiter);
114 CHECK_NE(header_end, header.npos);
116 ParseY4MTags(header, video_format);
117 return header_end + kY4MSimpleFrameDelimiterSize;
120 // Opens a given file for reading, and returns the file to the caller, who is
121 // responsible for closing it.
122 // static
123 base::File FileVideoCaptureDevice::OpenFileForRead(
124 const base::FilePath& file_path) {
125 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
126 CHECK(file.IsValid()) << file_path.value();
127 return file.Pass();
130 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
131 : capture_thread_("CaptureThread"),
132 file_path_(file_path),
133 frame_size_(0),
134 current_byte_index_(0),
135 first_frame_byte_index_(0) {}
137 FileVideoCaptureDevice::~FileVideoCaptureDevice() {
138 DCHECK(thread_checker_.CalledOnValidThread());
139 // Check if the thread is running.
140 // This means that the device have not been DeAllocated properly.
141 CHECK(!capture_thread_.IsRunning());
144 void FileVideoCaptureDevice::AllocateAndStart(
145 const VideoCaptureParams& params,
146 scoped_ptr<VideoCaptureDevice::Client> client) {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 CHECK(!capture_thread_.IsRunning());
150 capture_thread_.Start();
151 capture_thread_.message_loop()->PostTask(
152 FROM_HERE,
153 base::Bind(&FileVideoCaptureDevice::OnAllocateAndStart,
154 base::Unretained(this),
155 params,
156 base::Passed(&client)));
159 void FileVideoCaptureDevice::StopAndDeAllocate() {
160 DCHECK(thread_checker_.CalledOnValidThread());
161 CHECK(capture_thread_.IsRunning());
163 capture_thread_.message_loop()->PostTask(
164 FROM_HERE,
165 base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate,
166 base::Unretained(this)));
167 capture_thread_.Stop();
170 int FileVideoCaptureDevice::CalculateFrameSize() {
171 DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
172 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
173 return capture_format_.frame_size.GetArea() * 12 / 8;
176 void FileVideoCaptureDevice::OnAllocateAndStart(
177 const VideoCaptureParams& params,
178 scoped_ptr<VideoCaptureDevice::Client> client) {
179 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
181 client_ = client.Pass();
183 // Open the file and parse the header. Get frame size and format.
184 DCHECK(!file_.IsValid());
185 file_ = OpenFileForRead(file_path_);
186 first_frame_byte_index_ =
187 ParseFileAndExtractVideoFormat(&file_, &capture_format_);
188 current_byte_index_ = first_frame_byte_index_;
189 DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString()
190 << ", fps: " << capture_format_.frame_rate;
192 frame_size_ = CalculateFrameSize();
193 video_frame_.reset(new uint8[frame_size_]);
195 capture_thread_.message_loop()->PostTask(
196 FROM_HERE,
197 base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
198 base::Unretained(this)));
201 void FileVideoCaptureDevice::OnStopAndDeAllocate() {
202 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
203 file_.Close();
204 client_.reset();
205 current_byte_index_ = 0;
206 first_frame_byte_index_ = 0;
207 frame_size_ = 0;
208 video_frame_.reset();
211 void FileVideoCaptureDevice::OnCaptureTask() {
212 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
213 if (!client_)
214 return;
215 int result = file_.Read(current_byte_index_,
216 reinterpret_cast<char*>(video_frame_.get()),
217 frame_size_);
219 // If we passed EOF to base::File, it will return 0 read characters. In that
220 // case, reset the pointer and read again.
221 if (result != frame_size_) {
222 CHECK_EQ(result, 0);
223 current_byte_index_ = first_frame_byte_index_;
224 CHECK_EQ(file_.Read(current_byte_index_,
225 reinterpret_cast<char*>(video_frame_.get()),
226 frame_size_),
227 frame_size_);
228 } else {
229 current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize;
232 // Give the captured frame to the client.
233 client_->OnIncomingCapturedData(video_frame_.get(),
234 frame_size_,
235 capture_format_,
237 base::TimeTicks::Now());
238 // Reschedule next CaptureTask.
239 base::MessageLoop::current()->PostDelayedTask(
240 FROM_HERE,
241 base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
242 base::Unretained(this)),
243 base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate);
246 } // namespace media