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/capture/video/file_video_capture_device.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_piece.h"
10 #include "media/base/video_capture_types.h"
13 static const int kY4MHeaderMaxSize
= 200;
14 static const char kY4MSimpleFrameDelimiter
[] = "FRAME";
15 static const int kY4MSimpleFrameDelimiterSize
= 6;
17 int ParseY4MInt(const base::StringPiece
& token
) {
19 CHECK(base::StringToInt(token
, &temp_int
)) << token
;
23 // Extract numerator and denominator out of a token that must have the aspect
24 // numerator:denominator, both integer numbers.
25 void ParseY4MRational(const base::StringPiece
& token
,
28 size_t index_divider
= token
.find(':');
29 CHECK_NE(index_divider
, token
.npos
);
30 *numerator
= ParseY4MInt(token
.substr(0, index_divider
));
31 *denominator
= ParseY4MInt(token
.substr(index_divider
+ 1, token
.length()));
35 // This function parses the ASCII string in |header| as belonging to a Y4M file,
36 // returning the collected format in |video_format|. For a non authoritative
37 // explanation of the header format, check
38 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2
39 // Restrictions: Only interlaced I420 pixel format is supported, and pixel
40 // aspect ratio is ignored.
41 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace)
42 // character, however all examples mentioned in the Y4M header description end
43 // with a newline character instead. Also, some headers do _not_ specify pixel
44 // format, in this case it means I420.
45 // This code was inspired by third_party/libvpx/.../y4minput.* .
46 void ParseY4MTags(const std::string
& file_header
,
47 media::VideoCaptureFormat
* video_format
) {
48 video_format
->pixel_format
= media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
49 video_format
->frame_size
.set_width(0);
50 video_format
->frame_size
.set_height(0);
52 size_t blank_position
= 0;
53 base::StringPiece token
;
54 while ((blank_position
= file_header
.find_first_of("\n ", index
)) !=
56 // Every token is supposed to have an identifier letter and a bunch of
57 // information immediately after, which we extract into a |token| here.
59 base::StringPiece(&file_header
[index
+ 1], blank_position
- index
- 1);
60 CHECK(!token
.empty());
61 switch (file_header
[index
]) {
63 video_format
->frame_size
.set_width(ParseY4MInt(token
));
66 video_format
->frame_size
.set_height(ParseY4MInt(token
));
69 // If the token is "FRAME", it means we have finished with the header.
72 int fps_numerator
, fps_denominator
;
73 ParseY4MRational(token
, &fps_numerator
, &fps_denominator
);
74 video_format
->frame_rate
= fps_numerator
/ fps_denominator
;
78 // Interlacing is ignored, but we don't like mixed modes.
79 CHECK_NE(token
[0], 'm');
82 // Pixel aspect ratio ignored.
85 CHECK(token
== "420" || token
== "420jpeg" || token
== "420paldv")
86 << token
; // Only I420 is supported, and we fudge the variants.
91 // We're done if we have found a newline character right after the token.
92 if (file_header
[blank_position
] == '\n')
94 index
= blank_position
+ 1;
96 // Last video format semantic correctness check before sending it back.
97 CHECK(video_format
->IsValid());
100 // Reads and parses the header of a Y4M |file|, returning the collected pixel
101 // format in |video_format|. Returns the index of the first byte of the first
103 // Restrictions: Only trivial per-frame headers are supported.
105 int64
FileVideoCaptureDevice::ParseFileAndExtractVideoFormat(
107 media::VideoCaptureFormat
* video_format
) {
108 std::string
header(kY4MHeaderMaxSize
, 0);
109 file
->Read(0, &header
[0], kY4MHeaderMaxSize
- 1);
111 size_t header_end
= header
.find(kY4MSimpleFrameDelimiter
);
112 CHECK_NE(header_end
, header
.npos
);
114 ParseY4MTags(header
, video_format
);
115 return header_end
+ kY4MSimpleFrameDelimiterSize
;
118 // Opens a given file for reading, and returns the file to the caller, who is
119 // responsible for closing it.
121 base::File
FileVideoCaptureDevice::OpenFileForRead(
122 const base::FilePath
& file_path
) {
123 base::File
file(file_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
124 DLOG_IF(ERROR
, file
.IsValid())
126 << ", error: " << base::File::ErrorToString(file
.error_details());
130 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath
& file_path
)
131 : capture_thread_("CaptureThread"),
132 file_path_(file_path
),
134 current_byte_index_(0),
135 first_frame_byte_index_(0) {
138 FileVideoCaptureDevice::~FileVideoCaptureDevice() {
139 DCHECK(thread_checker_
.CalledOnValidThread());
140 // Check if the thread is running.
141 // This means that the device have not been DeAllocated properly.
142 CHECK(!capture_thread_
.IsRunning());
145 void FileVideoCaptureDevice::AllocateAndStart(
146 const VideoCaptureParams
& params
,
147 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
148 DCHECK(thread_checker_
.CalledOnValidThread());
149 CHECK(!capture_thread_
.IsRunning());
151 capture_thread_
.Start();
152 capture_thread_
.message_loop()->PostTask(
154 base::Bind(&FileVideoCaptureDevice::OnAllocateAndStart
,
155 base::Unretained(this), params
, base::Passed(&client
)));
158 void FileVideoCaptureDevice::StopAndDeAllocate() {
159 DCHECK(thread_checker_
.CalledOnValidThread());
160 CHECK(capture_thread_
.IsRunning());
162 capture_thread_
.message_loop()->PostTask(
163 FROM_HERE
, base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate
,
164 base::Unretained(this)));
165 capture_thread_
.Stop();
168 int FileVideoCaptureDevice::CalculateFrameSize() const {
169 DCHECK_EQ(capture_format_
.pixel_format
, VIDEO_CAPTURE_PIXEL_FORMAT_I420
);
170 DCHECK_EQ(capture_thread_
.message_loop(), base::MessageLoop::current());
171 return capture_format_
.ImageAllocationSize();
174 void FileVideoCaptureDevice::OnAllocateAndStart(
175 const VideoCaptureParams
& params
,
176 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
177 DCHECK_EQ(capture_thread_
.message_loop(), base::MessageLoop::current());
179 client_
= client
.Pass();
181 // Open the file and parse the header. Get frame size and format.
182 DCHECK(!file_
.IsValid());
183 file_
= OpenFileForRead(file_path_
);
184 if (!file_
.IsValid()) {
185 client_
->OnError("Could not open Video file");
188 first_frame_byte_index_
=
189 ParseFileAndExtractVideoFormat(&file_
, &capture_format_
);
190 current_byte_index_
= first_frame_byte_index_
;
191 DVLOG(1) << "Opened video file " << capture_format_
.frame_size
.ToString()
192 << ", fps: " << capture_format_
.frame_rate
;
194 frame_size_
= CalculateFrameSize();
195 video_frame_
.reset(new uint8
[frame_size_
]);
197 capture_thread_
.message_loop()->PostTask(
198 FROM_HERE
, base::Bind(&FileVideoCaptureDevice::OnCaptureTask
,
199 base::Unretained(this)));
202 void FileVideoCaptureDevice::OnStopAndDeAllocate() {
203 DCHECK_EQ(capture_thread_
.message_loop(), base::MessageLoop::current());
206 current_byte_index_
= 0;
207 first_frame_byte_index_
= 0;
209 next_frame_time_
= base::TimeTicks();
210 video_frame_
.reset();
213 void FileVideoCaptureDevice::OnCaptureTask() {
214 DCHECK_EQ(capture_thread_
.message_loop(), base::MessageLoop::current());
218 file_
.Read(current_byte_index_
,
219 reinterpret_cast<char*>(video_frame_
.get()), frame_size_
);
221 // If we passed EOF to base::File, it will return 0 read characters. In that
222 // case, reset the pointer and read again.
223 if (result
!= frame_size_
) {
225 current_byte_index_
= first_frame_byte_index_
;
227 file_
.Read(current_byte_index_
,
228 reinterpret_cast<char*>(video_frame_
.get()), frame_size_
),
231 current_byte_index_
+= frame_size_
+ kY4MSimpleFrameDelimiterSize
;
234 // Give the captured frame to the client.
235 const base::TimeTicks current_time
= base::TimeTicks::Now();
236 client_
->OnIncomingCapturedData(video_frame_
.get(), frame_size_
,
237 capture_format_
, 0, current_time
);
238 // Reschedule next CaptureTask.
239 const base::TimeDelta frame_interval
=
240 base::TimeDelta::FromMicroseconds(1E6
/ capture_format_
.frame_rate
);
241 if (next_frame_time_
.is_null()) {
242 next_frame_time_
= current_time
+ frame_interval
;
244 next_frame_time_
+= frame_interval
;
245 // Don't accumulate any debt if we are lagging behind - just post next frame
246 // immediately and continue as normal.
247 if (next_frame_time_
< current_time
)
248 next_frame_time_
= current_time
;
250 base::MessageLoop::current()->PostDelayedTask(
251 FROM_HERE
, base::Bind(&FileVideoCaptureDevice::OnCaptureTask
,
252 base::Unretained(this)),
253 next_frame_time_
- current_time
);