Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / file_video_capture_device.cc
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.
4
5 #include "media/video/capture/file_video_capture_device.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "media/base/media_switches.h"
16
17
18 namespace media {
19 static const char kFileVideoCaptureDeviceName[] =
20     "/dev/placeholder-for-file-backed-fake-capture-device";
21
22 static const int kY4MHeaderMaxSize = 200;
23 static const char kY4MSimpleFrameDelimiter[] = "FRAME";
24 static const int kY4MSimpleFrameDelimiterSize = 6;
25
26 int ParseY4MInt(const base::StringPiece& token) {
27   int temp_int;
28   CHECK(base::StringToInt(token, &temp_int)) << token;
29   return temp_int;
30 }
31
32 // Extract numerator and denominator out of a token that must have the aspect
33 // numerator:denominator, both integer numbers.
34 void ParseY4MRational(const base::StringPiece& token,
35                       int* numerator,
36                       int* denominator) {
37   size_t index_divider = token.find(':');
38   CHECK_NE(index_divider, token.npos);
39   *numerator = ParseY4MInt(token.substr(0, index_divider));
40   *denominator = ParseY4MInt(token.substr(index_divider + 1, token.length()));
41   CHECK(*denominator);
42 }
43
44 // This function parses the ASCII string in |header| as belonging to a Y4M file,
45 // returning the collected format in |video_format|. For a non authoritative
46 // explanation of the header format, check
47 // http://wiki.multimedia.cx/index.php?title=YUV4MPEG2
48 // Restrictions: Only interlaced I420 pixel format is supported, and pixel
49 // aspect ratio is ignored.
50 // Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace)
51 // character, however all examples mentioned in the Y4M header description end
52 // with a newline character instead. Also, some headers do _not_ specify pixel
53 // format, in this case it means I420.
54 // This code was inspired by third_party/libvpx/.../y4minput.* .
55 void ParseY4MTags(const std::string& file_header,
56                   media::VideoCaptureFormat* video_format) {
57   video_format->pixel_format = media::PIXEL_FORMAT_I420;
58   video_format->frame_size.set_width(0);
59   video_format->frame_size.set_height(0);
60   size_t index = 0;
61   size_t blank_position = 0;
62   base::StringPiece token;
63   while ((blank_position = file_header.find_first_of("\n ", index)) !=
64          std::string::npos) {
65     // Every token is supposed to have an identifier letter and a bunch of
66     // information immediately after, which we extract into a |token| here.
67     token =
68         base::StringPiece(&file_header[index + 1], blank_position - index - 1);
69     CHECK(!token.empty());
70     switch (file_header[index]) {
71       case 'W':
72         video_format->frame_size.set_width(ParseY4MInt(token));
73         break;
74       case 'H':
75         video_format->frame_size.set_height(ParseY4MInt(token));
76         break;
77       case 'F': {
78         // If the token is "FRAME", it means we have finished with the header.
79         if (token[0] == 'R')
80           break;
81         int fps_numerator, fps_denominator;
82         ParseY4MRational(token, &fps_numerator, &fps_denominator);
83         video_format->frame_rate = fps_numerator / fps_denominator;
84         break;
85       }
86       case 'I':
87         // Interlacing is ignored, but we don't like mixed modes.
88         CHECK_NE(token[0], 'm');
89         break;
90       case 'A':
91         // Pixel aspect ratio ignored.
92         break;
93       case 'C':
94         CHECK(token == "420" || token == "420jpeg" || token == "420paldv")
95             << token;  // Only I420 is supported, and we fudge the variants.
96         break;
97       default:
98         break;
99     }
100     // We're done if we have found a newline character right after the token.
101     if (file_header[blank_position] == '\n')
102       break;
103     index = blank_position + 1;
104   }
105   // Last video format semantic correctness check before sending it back.
106   CHECK(video_format->IsValid());
107 }
108
109 // Reads and parses the header of a Y4M |file|, returning the collected pixel
110 // format in |video_format|. Returns the index of the first byte of the first
111 // video frame.
112 // Restrictions: Only trivial per-frame headers are supported.
113 int64 ParseFileAndExtractVideoFormat(
114     base::File* file,
115     media::VideoCaptureFormat* video_format) {
116   std::string header(kY4MHeaderMaxSize, 0);
117   file->Read(0, &header[0], kY4MHeaderMaxSize - 1);
118
119   size_t header_end = header.find(kY4MSimpleFrameDelimiter);
120   CHECK_NE(header_end, header.npos);
121
122   ParseY4MTags(header, video_format);
123   return header_end + kY4MSimpleFrameDelimiterSize;
124 }
125
126 // Opens a given file for reading, and returns the file to the caller, who is
127 // responsible for closing it.
128 base::File OpenFileForRead(const base::FilePath& file_path) {
129   base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
130   CHECK(file.IsValid()) << file_path.value();
131   return file.Pass();
132 }
133
134 // Inspects the command line and retrieves the file path parameter.
135 base::FilePath GetFilePathFromCommandLine() {
136   base::FilePath command_line_file_path =
137       CommandLine::ForCurrentProcess()->GetSwitchValuePath(
138           switches::kUseFileForFakeVideoCapture);
139   CHECK(!command_line_file_path.empty());
140   return command_line_file_path;
141 }
142
143 void FileVideoCaptureDevice::GetDeviceNames(Names* const device_names) {
144   DCHECK(device_names->empty());
145   base::FilePath command_line_file_path = GetFilePathFromCommandLine();
146 #if defined(OS_WIN)
147   device_names->push_back(
148       Name(base::SysWideToUTF8(command_line_file_path.value()),
149            kFileVideoCaptureDeviceName));
150 #else
151   device_names->push_back(Name(command_line_file_path.value(),
152                                kFileVideoCaptureDeviceName));
153 #endif  // OS_WIN
154 }
155
156 void FileVideoCaptureDevice::GetDeviceSupportedFormats(
157     const Name& device,
158     VideoCaptureFormats* supported_formats) {
159   base::File file = OpenFileForRead(GetFilePathFromCommandLine());
160   VideoCaptureFormat capture_format;
161   ParseFileAndExtractVideoFormat(&file, &capture_format);
162   supported_formats->push_back(capture_format);
163 }
164
165 VideoCaptureDevice* FileVideoCaptureDevice::Create(const Name& device_name) {
166 #if defined(OS_WIN)
167   return new FileVideoCaptureDevice(
168       base::FilePath(base::SysUTF8ToWide(device_name.name())));
169 #else
170   return new FileVideoCaptureDevice(base::FilePath(device_name.name()));
171 #endif  // OS_WIN
172 }
173
174 FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
175     : capture_thread_("CaptureThread"),
176       file_path_(file_path),
177       frame_size_(0),
178       current_byte_index_(0),
179       first_frame_byte_index_(0) {}
180
181 FileVideoCaptureDevice::~FileVideoCaptureDevice() {
182   DCHECK(thread_checker_.CalledOnValidThread());
183   // Check if the thread is running.
184   // This means that the device have not been DeAllocated properly.
185   CHECK(!capture_thread_.IsRunning());
186 }
187
188 void FileVideoCaptureDevice::AllocateAndStart(
189     const VideoCaptureParams& params,
190     scoped_ptr<VideoCaptureDevice::Client> client) {
191   DCHECK(thread_checker_.CalledOnValidThread());
192   CHECK(!capture_thread_.IsRunning());
193
194   capture_thread_.Start();
195   capture_thread_.message_loop()->PostTask(
196       FROM_HERE,
197       base::Bind(&FileVideoCaptureDevice::OnAllocateAndStart,
198                  base::Unretained(this),
199                  params,
200                  base::Passed(&client)));
201 }
202
203 void FileVideoCaptureDevice::StopAndDeAllocate() {
204   DCHECK(thread_checker_.CalledOnValidThread());
205   CHECK(capture_thread_.IsRunning());
206
207   capture_thread_.message_loop()->PostTask(
208       FROM_HERE,
209       base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate,
210                  base::Unretained(this)));
211   capture_thread_.Stop();
212 }
213
214 int FileVideoCaptureDevice::CalculateFrameSize() {
215   DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
216   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
217   return capture_format_.frame_size.GetArea() * 12 / 8;
218 }
219
220 void FileVideoCaptureDevice::OnAllocateAndStart(
221     const VideoCaptureParams& params,
222     scoped_ptr<VideoCaptureDevice::Client> client) {
223   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
224
225   client_ = client.Pass();
226
227   // Open the file and parse the header. Get frame size and format.
228   DCHECK(!file_.IsValid());
229   file_ = OpenFileForRead(file_path_);
230   first_frame_byte_index_ =
231       ParseFileAndExtractVideoFormat(&file_, &capture_format_);
232   current_byte_index_ = first_frame_byte_index_;
233   DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString()
234            << ", fps: " << capture_format_.frame_rate;
235
236   frame_size_ = CalculateFrameSize();
237   video_frame_.reset(new uint8[frame_size_]);
238
239   capture_thread_.message_loop()->PostTask(
240       FROM_HERE,
241       base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
242                  base::Unretained(this)));
243 }
244
245 void FileVideoCaptureDevice::OnStopAndDeAllocate() {
246   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
247   file_.Close();
248   client_.reset();
249   current_byte_index_ = 0;
250   first_frame_byte_index_ = 0;
251   frame_size_ = 0;
252   video_frame_.reset();
253 }
254
255 void FileVideoCaptureDevice::OnCaptureTask() {
256   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
257   if (!client_)
258     return;
259   int result = file_.Read(current_byte_index_,
260                           reinterpret_cast<char*>(video_frame_.get()),
261                           frame_size_);
262
263   // If we passed EOF to base::File, it will return 0 read characters. In that
264   // case, reset the pointer and read again.
265   if (result != frame_size_) {
266     CHECK_EQ(result, 0);
267     current_byte_index_ = first_frame_byte_index_;
268     CHECK_EQ(file_.Read(current_byte_index_,
269                         reinterpret_cast<char*>(video_frame_.get()),
270                         frame_size_),
271              frame_size_);
272   } else {
273     current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize;
274   }
275
276   // Give the captured frame to the client.
277   client_->OnIncomingCapturedData(video_frame_.get(),
278                                   frame_size_,
279                                   capture_format_,
280                                   0,
281                                   base::TimeTicks::Now());
282   // Reschedule next CaptureTask.
283   base::MessageLoop::current()->PostDelayedTask(
284       FROM_HERE,
285       base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
286                  base::Unretained(this)),
287       base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate);
288 }
289
290 }  // namespace media