OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/capture/video/file_video_capture_device.h" | 5 #include "media/capture/video/file_video_capture_device.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
9 #include "base/strings/string_piece.h" | 9 #include "base/strings/string_piece.h" |
10 #include "media/base/video_capture_types.h" | 10 #include "media/base/video_capture_types.h" |
11 #include "media/filters/jpeg_parser.h" | |
11 | 12 |
12 namespace media { | 13 namespace media { |
14 | |
13 static const int kY4MHeaderMaxSize = 200; | 15 static const int kY4MHeaderMaxSize = 200; |
14 static const char kY4MSimpleFrameDelimiter[] = "FRAME"; | 16 static const char kY4MSimpleFrameDelimiter[] = "FRAME"; |
15 static const int kY4MSimpleFrameDelimiterSize = 6; | 17 static const int kY4MSimpleFrameDelimiterSize = 6; |
18 static const int kMJpegFrameRate = 30; | |
mcasas
2015/08/19 22:17:35
float, 30.0f
henryhsu
2015/08/20 03:18:56
Done.
| |
16 | 19 |
17 int ParseY4MInt(const base::StringPiece& token) { | 20 FileVideoCaptureDevice::VideoFileParser::VideoFileParser( |
21 const base::FilePath& file_path) | |
22 : file_path_(file_path), | |
23 frame_size_(0), | |
24 current_byte_index_(0), | |
25 first_frame_byte_index_(0) {} | |
26 | |
27 FileVideoCaptureDevice::VideoFileParser::~VideoFileParser() {} | |
28 | |
29 FileVideoCaptureDevice::Y4mFileParser::Y4mFileParser( | |
30 const base::FilePath& file_path) | |
31 : VideoFileParser(file_path) {} | |
32 | |
33 FileVideoCaptureDevice::Y4mFileParser::~Y4mFileParser() {} | |
34 | |
35 bool FileVideoCaptureDevice::Y4mFileParser::Initialize( | |
36 media::VideoCaptureFormat* capture_format) { | |
37 file_.reset(new base::File(file_path_, | |
38 base::File::FLAG_OPEN | base::File::FLAG_READ)); | |
39 if (!file_->IsValid()) { | |
40 DLOG(ERROR) << file_path_.value() << ", error: " | |
41 << base::File::ErrorToString(file_->error_details()); | |
42 return false; | |
43 } | |
44 | |
45 std::string header(kY4MHeaderMaxSize, 0); | |
kcwu
2015/08/19 11:29:38
s/0/'\0'/
henryhsu
2015/08/20 03:18:57
Done.
| |
46 file_->Read(0, &header[0], kY4MHeaderMaxSize - 1); | |
kcwu
2015/08/19 11:29:38
s/kY4MHeaderMaxSize - 1/header.size()/
henryhsu
2015/08/20 03:18:57
Done.
| |
47 const size_t header_end = header.find(kY4MSimpleFrameDelimiter); | |
48 CHECK_NE(header_end, header.npos); | |
49 | |
50 ParseY4MTags(header, capture_format); | |
51 first_frame_byte_index_ = header_end + kY4MSimpleFrameDelimiterSize; | |
52 current_byte_index_ = first_frame_byte_index_; | |
53 frame_size_ = capture_format->ImageAllocationSize(); | |
54 video_frame_.reset(new uint8[frame_size_]); | |
kcwu
2015/08/19 11:29:38
uint8_t
henryhsu
2015/08/20 03:18:57
Done.
| |
55 return true; | |
56 } | |
57 | |
58 const uint8_t* FileVideoCaptureDevice::Y4mFileParser::GetNextFrame( | |
59 int* frame_size) { | |
60 int result = | |
61 file_->Read(current_byte_index_, | |
62 reinterpret_cast<char*>(video_frame_.get()), frame_size_); | |
63 | |
64 // If we passed EOF to base::File, it will return 0 read characters. In that | |
65 // case, reset the pointer andd read again. | |
kcwu
2015/08/19 11:29:38
s/andd/and/
henryhsu
2015/08/20 03:18:57
Done.
| |
66 if (result != frame_size_) { | |
67 CHECK_EQ(result, 0); | |
68 current_byte_index_ = first_frame_byte_index_; | |
69 CHECK_EQ( | |
70 file_->Read(current_byte_index_, | |
71 reinterpret_cast<char*>(video_frame_.get()), frame_size_), | |
72 frame_size_); | |
73 } else { | |
74 current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize; | |
75 } | |
76 *frame_size = frame_size_; | |
77 return video_frame_.get(); | |
78 } | |
79 | |
80 int FileVideoCaptureDevice::Y4mFileParser::ParseY4MInt( | |
mcasas
2015/08/19 22:17:34
This method should be a file function as before.
henryhsu
2015/08/20 03:18:56
Done.
| |
81 const base::StringPiece& token) { | |
18 int temp_int; | 82 int temp_int; |
19 CHECK(base::StringToInt(token, &temp_int)) << token; | 83 CHECK(base::StringToInt(token, &temp_int)) << token; |
20 return temp_int; | 84 return temp_int; |
21 } | 85 } |
22 | 86 |
23 // Extract numerator and denominator out of a token that must have the aspect | 87 // Extract numerator and denominator out of a token that must have the aspect |
24 // numerator:denominator, both integer numbers. | 88 // numerator:denominator, both integer numbers. |
25 void ParseY4MRational(const base::StringPiece& token, | 89 void FileVideoCaptureDevice::Y4mFileParser::ParseY4MRational( |
mcasas
2015/08/19 22:17:34
Same: This method should be a file function as bef
henryhsu
2015/08/20 03:18:57
Done.
| |
26 int* numerator, | 90 const base::StringPiece& token, |
27 int* denominator) { | 91 int* numerator, |
92 int* denominator) { | |
28 size_t index_divider = token.find(':'); | 93 size_t index_divider = token.find(':'); |
29 CHECK_NE(index_divider, token.npos); | 94 CHECK_NE(index_divider, token.npos); |
30 *numerator = ParseY4MInt(token.substr(0, index_divider)); | 95 *numerator = ParseY4MInt(token.substr(0, index_divider)); |
31 *denominator = ParseY4MInt(token.substr(index_divider + 1, token.length())); | 96 *denominator = ParseY4MInt(token.substr(index_divider + 1, token.length())); |
32 CHECK(*denominator); | 97 CHECK(*denominator); |
33 } | 98 } |
34 | 99 |
35 // This function parses the ASCII string in |header| as belonging to a Y4M file, | 100 // 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 | 101 // returning the collected format in |video_format|. For a non authoritative |
37 // explanation of the header format, check | 102 // explanation of the header format, check |
38 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 | 103 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 |
39 // Restrictions: Only interlaced I420 pixel format is supported, and pixel | 104 // Restrictions: Only interlaced I420 pixel format is supported, and pixel |
40 // aspect ratio is ignored. | 105 // aspect ratio is ignored. |
41 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace) | 106 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace) |
42 // character, however all examples mentioned in the Y4M header description end | 107 // character, however all examples mentioned in the Y4M header description end |
43 // with a newline character instead. Also, some headers do _not_ specify pixel | 108 // with a newline character instead. Also, some headers do _not_ specify pixel |
44 // format, in this case it means I420. | 109 // format, in this case it means I420. |
45 // This code was inspired by third_party/libvpx/.../y4minput.* . | 110 // This code was inspired by third_party/libvpx/.../y4minput.* . |
46 void ParseY4MTags(const std::string& file_header, | 111 void FileVideoCaptureDevice::Y4mFileParser::ParseY4MTags( |
mcasas
2015/08/19 22:17:34
Same: This method should be a file function as bef
henryhsu
2015/08/20 03:18:57
Done.
| |
47 media::VideoCaptureFormat* video_format) { | 112 const std::string& file_header, |
48 video_format->pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_I420; | 113 media::VideoCaptureFormat* video_format) { |
49 video_format->frame_size.set_width(0); | 114 media::VideoCaptureFormat format; |
50 video_format->frame_size.set_height(0); | 115 format.pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_I420; |
51 size_t index = 0; | 116 size_t index = 0; |
52 size_t blank_position = 0; | 117 size_t blank_position = 0; |
53 base::StringPiece token; | 118 base::StringPiece token; |
54 while ((blank_position = file_header.find_first_of("\n ", index)) != | 119 while ((blank_position = file_header.find_first_of("\n ", index)) != |
55 std::string::npos) { | 120 std::string::npos) { |
56 // Every token is supposed to have an identifier letter and a bunch of | 121 // Every token is supposed to have an identifier letter and a bunch of |
57 // information immediately after, which we extract into a |token| here. | 122 // information immediately after, which we extract into a |token| here. |
58 token = | 123 token = |
59 base::StringPiece(&file_header[index + 1], blank_position - index - 1); | 124 base::StringPiece(&file_header[index + 1], blank_position - index - 1); |
60 CHECK(!token.empty()); | 125 CHECK(!token.empty()); |
61 switch (file_header[index]) { | 126 switch (file_header[index]) { |
62 case 'W': | 127 case 'W': |
63 video_format->frame_size.set_width(ParseY4MInt(token)); | 128 format.frame_size.set_width(ParseY4MInt(token)); |
64 break; | 129 break; |
65 case 'H': | 130 case 'H': |
66 video_format->frame_size.set_height(ParseY4MInt(token)); | 131 format.frame_size.set_height(ParseY4MInt(token)); |
67 break; | 132 break; |
68 case 'F': { | 133 case 'F': { |
69 // If the token is "FRAME", it means we have finished with the header. | 134 // If the token is "FRAME", it means we have finished with the header. |
70 if (token[0] == 'R') | 135 if (token[0] == 'R') |
71 break; | 136 break; |
72 int fps_numerator, fps_denominator; | 137 int fps_numerator, fps_denominator; |
73 ParseY4MRational(token, &fps_numerator, &fps_denominator); | 138 ParseY4MRational(token, &fps_numerator, &fps_denominator); |
74 video_format->frame_rate = fps_numerator / fps_denominator; | 139 format.frame_rate = fps_numerator / fps_denominator; |
75 break; | 140 break; |
76 } | 141 } |
77 case 'I': | 142 case 'I': |
78 // Interlacing is ignored, but we don't like mixed modes. | 143 // Interlacing is ignored, but we don't like mixed modes. |
79 CHECK_NE(token[0], 'm'); | 144 CHECK_NE(token[0], 'm'); |
80 break; | 145 break; |
81 case 'A': | 146 case 'A': |
82 // Pixel aspect ratio ignored. | 147 // Pixel aspect ratio ignored. |
83 break; | 148 break; |
84 case 'C': | 149 case 'C': |
85 CHECK(token == "420" || token == "420jpeg" || token == "420paldv") | 150 CHECK(token == "420" || token == "420jpeg" || token == "420paldv") |
86 << token; // Only I420 is supported, and we fudge the variants. | 151 << token; // Only I420 is supported, and we fudge the variants. |
87 break; | |
88 default: | 152 default: |
kcwu
2015/08/19 11:29:38
why removed break; above this line?
henryhsu
2015/08/20 03:18:56
Thanks.
| |
89 break; | 153 break; |
90 } | 154 } |
91 // We're done if we have found a newline character right after the token. | 155 // We're done if we have found a newline character right after the token. |
92 if (file_header[blank_position] == '\n') | 156 if (file_header[blank_position] == '\n') |
93 break; | 157 break; |
94 index = blank_position + 1; | 158 index = blank_position + 1; |
95 } | 159 } |
96 // Last video format semantic correctness check before sending it back. | 160 // Last video format semantic correctness check before sending it back. |
97 CHECK(video_format->IsValid()); | 161 CHECK(format.IsValid()); |
162 *video_format = format; | |
98 } | 163 } |
99 | 164 |
100 // Reads and parses the header of a Y4M |file|, returning the collected pixel | 165 FileVideoCaptureDevice::MjpegFileParser::MjpegFileParser( |
101 // format in |video_format|. Returns the index of the first byte of the first | 166 const base::FilePath& file_path) |
102 // video frame. | 167 : VideoFileParser(file_path) {} |
103 // Restrictions: Only trivial per-frame headers are supported. | |
104 // static | |
105 int64 FileVideoCaptureDevice::ParseFileAndExtractVideoFormat( | |
106 base::File* file, | |
107 media::VideoCaptureFormat* video_format) { | |
108 std::string header(kY4MHeaderMaxSize, 0); | |
109 file->Read(0, &header[0], kY4MHeaderMaxSize - 1); | |
110 | 168 |
111 size_t header_end = header.find(kY4MSimpleFrameDelimiter); | 169 FileVideoCaptureDevice::MjpegFileParser::~MjpegFileParser() {} |
112 CHECK_NE(header_end, header.npos); | |
113 | 170 |
114 ParseY4MTags(header, video_format); | 171 bool FileVideoCaptureDevice::MjpegFileParser::Initialize( |
115 return header_end + kY4MSimpleFrameDelimiterSize; | 172 media::VideoCaptureFormat* capture_format) { |
173 mapped_file_.reset(new base::MemoryMappedFile()); | |
174 | |
175 if (!mapped_file_->Initialize(file_path_) || !mapped_file_->IsValid()) { | |
176 LOG(ERROR) << "File memory map error: " << file_path_.value(); | |
177 return false; | |
178 } | |
179 | |
180 JpegParseResult result; | |
181 if (!ParseJpegPicture(mapped_file_->data(), mapped_file_->length(), &result)) | |
182 return false; | |
183 | |
184 frame_size_ = result.image_size; | |
185 if (frame_size_ > static_cast<int>(mapped_file_->length())) { | |
186 LOG(ERROR) << "File is incomplete"; | |
187 return false; | |
188 } | |
189 | |
190 VideoCaptureFormat format; | |
191 format.pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG; | |
192 format.frame_size.set_width(result.frame_header.visible_width); | |
193 format.frame_size.set_height(result.frame_header.visible_height); | |
194 format.frame_rate = kMJpegFrameRate; | |
195 if (!format.IsValid()) | |
196 return false; | |
197 *capture_format = format; | |
198 return true; | |
116 } | 199 } |
117 | 200 |
118 // Opens a given file for reading, and returns the file to the caller, who is | 201 const uint8_t* FileVideoCaptureDevice::MjpegFileParser::GetNextFrame( |
119 // responsible for closing it. | 202 int* frame_size) { |
203 const uint8_t* buf_ptr = mapped_file_->data() + current_byte_index_; | |
204 | |
205 JpegParseResult result; | |
206 if (!ParseJpegPicture(buf_ptr, mapped_file_->length() - current_byte_index_, | |
207 &result)) { | |
208 return nullptr; | |
209 } | |
210 *frame_size = frame_size_ = result.image_size; | |
mcasas
2015/08/19 22:17:35
Should we DCHECK something of |result|?
DCHECK_GT(
henryhsu
2015/08/20 03:18:57
We don't use result.data_size. I think if ParseJpe
| |
211 current_byte_index_ += frame_size_; | |
212 // reset the pointer to play repeatedly. | |
mcasas
2015/08/19 22:17:34
s/reset/Reset/
henryhsu
2015/08/20 03:18:57
Done.
| |
213 if (current_byte_index_ >= mapped_file_->length()) | |
214 current_byte_index_ = first_frame_byte_index_; | |
215 return buf_ptr; | |
216 } | |
217 | |
120 // static | 218 // static |
121 base::File FileVideoCaptureDevice::OpenFileForRead( | 219 bool FileVideoCaptureDevice::GetVideoCaptureFormat( |
122 const base::FilePath& file_path) { | 220 const base::FilePath& file_path, |
123 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); | 221 media::VideoCaptureFormat* video_format) { |
124 DLOG_IF(ERROR, file.IsValid()) | 222 scoped_ptr<VideoFileParser> file_parser = |
125 << file_path.value() | 223 GetVideoFileParser(file_path, video_format); |
mcasas
2015/08/19 22:17:34
Creating a VideoFileParser just for checking
if th
henryhsu
2015/08/20 03:18:57
I move buffer allocation to GetNextFrame.
Only all
| |
126 << ", error: " << base::File::ErrorToString(file.error_details()); | 224 if (!file_parser) |
kcwu
2015/08/19 11:29:38
return file_parser != nullptr;
henryhsu
2015/08/20 03:18:57
Done.
| |
127 return file.Pass(); | 225 return false; |
226 return true; | |
227 } | |
228 | |
229 // static | |
230 scoped_ptr<FileVideoCaptureDevice::VideoFileParser> | |
231 FileVideoCaptureDevice::GetVideoFileParser( | |
232 const base::FilePath& file_path, | |
233 media::VideoCaptureFormat* video_format) { | |
234 scoped_ptr<VideoFileParser> file_parser; | |
235 | |
236 if (base::EndsWith(file_path.value(), "y4m", | |
237 base::CompareCase::INSENSITIVE_ASCII)) { | |
238 file_parser.reset(new Y4mFileParser(file_path)); | |
239 } else if (base::EndsWith(file_path.value(), "mjpeg", | |
240 base::CompareCase::INSENSITIVE_ASCII)) { | |
241 file_parser.reset(new MjpegFileParser(file_path)); | |
242 } else { | |
243 LOG(ERROR) << "Unsupported file format."; | |
244 return file_parser.Pass(); | |
245 } | |
246 | |
247 if (!file_parser || !file_parser->Initialize(video_format)) { | |
248 file_parser.reset(); | |
249 } | |
250 return file_parser.Pass(); | |
128 } | 251 } |
129 | 252 |
130 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path) | 253 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path) |
131 : capture_thread_("CaptureThread"), | 254 : capture_thread_("CaptureThread"), file_path_(file_path) {} |
132 file_path_(file_path), | |
133 frame_size_(0), | |
134 current_byte_index_(0), | |
135 first_frame_byte_index_(0) { | |
136 } | |
137 | 255 |
138 FileVideoCaptureDevice::~FileVideoCaptureDevice() { | 256 FileVideoCaptureDevice::~FileVideoCaptureDevice() { |
139 DCHECK(thread_checker_.CalledOnValidThread()); | 257 DCHECK(thread_checker_.CalledOnValidThread()); |
140 // Check if the thread is running. | 258 // Check if the thread is running. |
141 // This means that the device have not been DeAllocated properly. | 259 // This means that the device have not been DeAllocated properly. |
142 CHECK(!capture_thread_.IsRunning()); | 260 CHECK(!capture_thread_.IsRunning()); |
143 } | 261 } |
144 | 262 |
145 void FileVideoCaptureDevice::AllocateAndStart( | 263 void FileVideoCaptureDevice::AllocateAndStart( |
146 const VideoCaptureParams& params, | 264 const VideoCaptureParams& params, |
(...skipping 11 matching lines...) Expand all Loading... | |
158 void FileVideoCaptureDevice::StopAndDeAllocate() { | 276 void FileVideoCaptureDevice::StopAndDeAllocate() { |
159 DCHECK(thread_checker_.CalledOnValidThread()); | 277 DCHECK(thread_checker_.CalledOnValidThread()); |
160 CHECK(capture_thread_.IsRunning()); | 278 CHECK(capture_thread_.IsRunning()); |
161 | 279 |
162 capture_thread_.message_loop()->PostTask( | 280 capture_thread_.message_loop()->PostTask( |
163 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate, | 281 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate, |
164 base::Unretained(this))); | 282 base::Unretained(this))); |
165 capture_thread_.Stop(); | 283 capture_thread_.Stop(); |
166 } | 284 } |
167 | 285 |
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(); | |
172 } | |
173 | |
174 void FileVideoCaptureDevice::OnAllocateAndStart( | 286 void FileVideoCaptureDevice::OnAllocateAndStart( |
175 const VideoCaptureParams& params, | 287 const VideoCaptureParams& params, |
176 scoped_ptr<VideoCaptureDevice::Client> client) { | 288 scoped_ptr<VideoCaptureDevice::Client> client) { |
177 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); | 289 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); |
178 | 290 |
179 client_ = client.Pass(); | 291 client_ = client.Pass(); |
180 | 292 |
181 // Open the file and parse the header. Get frame size and format. | 293 DCHECK(!file_parser_); |
182 DCHECK(!file_.IsValid()); | 294 file_parser_ = GetVideoFileParser(file_path_, &capture_format_); |
183 file_ = OpenFileForRead(file_path_); | 295 if (!file_parser_) { |
184 if (!file_.IsValid()) { | |
185 client_->OnError("Could not open Video file"); | 296 client_->OnError("Could not open Video file"); |
186 return; | 297 return; |
187 } | 298 } |
188 first_frame_byte_index_ = | 299 |
189 ParseFileAndExtractVideoFormat(&file_, &capture_format_); | |
190 current_byte_index_ = first_frame_byte_index_; | |
191 DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString() | 300 DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString() |
192 << ", fps: " << capture_format_.frame_rate; | 301 << ", fps: " << capture_format_.frame_rate; |
193 | 302 |
194 frame_size_ = CalculateFrameSize(); | |
195 video_frame_.reset(new uint8[frame_size_]); | |
196 | |
197 capture_thread_.message_loop()->PostTask( | 303 capture_thread_.message_loop()->PostTask( |
198 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask, | 304 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask, |
199 base::Unretained(this))); | 305 base::Unretained(this))); |
200 } | 306 } |
201 | 307 |
202 void FileVideoCaptureDevice::OnStopAndDeAllocate() { | 308 void FileVideoCaptureDevice::OnStopAndDeAllocate() { |
203 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); | 309 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); |
204 file_.Close(); | 310 file_parser_.reset(); |
205 client_.reset(); | 311 client_.reset(); |
206 current_byte_index_ = 0; | |
207 first_frame_byte_index_ = 0; | |
208 frame_size_ = 0; | |
209 next_frame_time_ = base::TimeTicks(); | 312 next_frame_time_ = base::TimeTicks(); |
210 video_frame_.reset(); | |
211 } | 313 } |
212 | 314 |
213 void FileVideoCaptureDevice::OnCaptureTask() { | 315 void FileVideoCaptureDevice::OnCaptureTask() { |
214 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); | 316 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); |
215 if (!client_) | 317 if (!client_) |
216 return; | 318 return; |
217 int result = | |
218 file_.Read(current_byte_index_, | |
219 reinterpret_cast<char*>(video_frame_.get()), frame_size_); | |
220 | |
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_) { | |
224 CHECK_EQ(result, 0); | |
225 current_byte_index_ = first_frame_byte_index_; | |
226 CHECK_EQ( | |
227 file_.Read(current_byte_index_, | |
228 reinterpret_cast<char*>(video_frame_.get()), frame_size_), | |
229 frame_size_); | |
230 } else { | |
231 current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize; | |
232 } | |
233 | 319 |
234 // Give the captured frame to the client. | 320 // Give the captured frame to the client. |
321 int frame_size = 0; | |
322 const uint8_t* frame_ptr = file_parser_->GetNextFrame(&frame_size); | |
323 CHECK(frame_ptr); | |
mcasas
2015/08/19 22:17:35
and DCHECK(frame_size); ?
It might look like over
henryhsu
2015/08/20 03:18:57
Done.
| |
235 const base::TimeTicks current_time = base::TimeTicks::Now(); | 324 const base::TimeTicks current_time = base::TimeTicks::Now(); |
236 client_->OnIncomingCapturedData(video_frame_.get(), frame_size_, | 325 client_->OnIncomingCapturedData(frame_ptr, frame_size, capture_format_, 0, |
237 capture_format_, 0, current_time); | 326 current_time); |
238 // Reschedule next CaptureTask. | 327 // Reschedule next CaptureTask. |
239 const base::TimeDelta frame_interval = | 328 const base::TimeDelta frame_interval = |
240 base::TimeDelta::FromMicroseconds(1E6 / capture_format_.frame_rate); | 329 base::TimeDelta::FromMicroseconds(1E6 / capture_format_.frame_rate); |
241 if (next_frame_time_.is_null()) { | 330 if (next_frame_time_.is_null()) { |
242 next_frame_time_ = current_time + frame_interval; | 331 next_frame_time_ = current_time + frame_interval; |
243 } else { | 332 } else { |
244 next_frame_time_ += frame_interval; | 333 next_frame_time_ += frame_interval; |
245 // Don't accumulate any debt if we are lagging behind - just post next frame | 334 // Don't accumulate any debt if we are lagging behind - just post next frame |
246 // immediately and continue as normal. | 335 // immediately and continue as normal. |
247 if (next_frame_time_ < current_time) | 336 if (next_frame_time_ < current_time) |
248 next_frame_time_ = current_time; | 337 next_frame_time_ = current_time; |
249 } | 338 } |
250 base::MessageLoop::current()->PostDelayedTask( | 339 base::MessageLoop::current()->PostDelayedTask( |
251 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask, | 340 FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask, |
252 base::Unretained(this)), | 341 base::Unretained(this)), |
253 next_frame_time_ - current_time); | 342 next_frame_time_ - current_time); |
254 } | 343 } |
255 | 344 |
256 } // namespace media | 345 } // namespace media |
OLD | NEW |