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/memory/scoped_ptr.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_piece.h" 13 14 namespace media { 15 static const int kY4MHeaderMaxSize = 200; 16 static const char kY4MSimpleFrameDelimiter[] = "FRAME"; 17 static const int kY4MSimpleFrameDelimiterSize = 6; 18 19 int ParseY4MInt(const base::StringPiece& token) { 20 int temp_int; 21 CHECK(base::StringToInt(token, &temp_int)) << token; 22 return temp_int; 23 } 24 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); 35 } 36 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; 78 } 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; 92 } 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; 97 } 98 // Last video format semantic correctness check before sending it back. 99 CHECK(video_format->IsValid()); 100 } 101 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); 112 113 size_t header_end = header.find(kY4MSimpleFrameDelimiter); 114 CHECK_NE(header_end, header.npos); 115 116 ParseY4MTags(header, video_format); 117 return header_end + kY4MSimpleFrameDelimiterSize; 118 } 119 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(); 128 } 129 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) {} 136 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()); 142 } 143 144 void FileVideoCaptureDevice::AllocateAndStart( 145 const VideoCaptureParams& params, 146 scoped_ptr<VideoCaptureDevice::Client> client) { 147 DCHECK(thread_checker_.CalledOnValidThread()); 148 CHECK(!capture_thread_.IsRunning()); 149 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))); 157 } 158 159 void FileVideoCaptureDevice::StopAndDeAllocate() { 160 DCHECK(thread_checker_.CalledOnValidThread()); 161 CHECK(capture_thread_.IsRunning()); 162 163 capture_thread_.message_loop()->PostTask( 164 FROM_HERE, 165 base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate, 166 base::Unretained(this))); 167 capture_thread_.Stop(); 168 } 169 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; 174 } 175 176 void FileVideoCaptureDevice::OnAllocateAndStart( 177 const VideoCaptureParams& params, 178 scoped_ptr<VideoCaptureDevice::Client> client) { 179 DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current()); 180 181 client_ = client.Pass(); 182 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; 191 192 frame_size_ = CalculateFrameSize(); 193 video_frame_.reset(new uint8[frame_size_]); 194 195 capture_thread_.message_loop()->PostTask( 196 FROM_HERE, 197 base::Bind(&FileVideoCaptureDevice::OnCaptureTask, 198 base::Unretained(this))); 199 } 200 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(); 209 } 210 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_); 218 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; 230 } 231 232 // Give the captured frame to the client. 233 client_->OnIncomingCapturedData(video_frame_.get(), 234 frame_size_, 235 capture_format_, 236 0, 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); 244 } 245 246 } // namespace media 247