This source file includes following definitions.
- ParseY4MInt
- ParseY4MRational
- ParseY4MTags
- ParseFileAndExtractVideoFormat
- OpenFileForRead
- GetFilePathFromCommandLine
- GetDeviceNames
- GetDeviceSupportedFormats
- Create
- first_frame_byte_index_
- AllocateAndStart
- StopAndDeAllocate
- CalculateFrameSize
- OnAllocateAndStart
- OnStopAndDeAllocate
- OnCaptureTask
#include "media/video/capture/file_video_capture_device.h"
#include <string>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/sys_string_conversions.h"
#include "media/base/media_switches.h"
namespace media {
static const char kFileVideoCaptureDeviceName[] =
"/dev/placeholder-for-file-backed-fake-capture-device";
static const int kY4MHeaderMaxSize = 200;
static const char kY4MSimpleFrameDelimiter[] = "FRAME";
static const int kY4MSimpleFrameDelimiterSize = 6;
int ParseY4MInt(const base::StringPiece& token) {
int temp_int;
CHECK(base::StringToInt(token, &temp_int)) << token;
return temp_int;
}
void ParseY4MRational(const base::StringPiece& token,
int* numerator,
int* denominator) {
size_t index_divider = token.find(':');
CHECK_NE(index_divider, token.npos);
*numerator = ParseY4MInt(token.substr(0, index_divider));
*denominator = ParseY4MInt(token.substr(index_divider + 1, token.length()));
CHECK(*denominator);
}
void ParseY4MTags(const std::string& file_header,
media::VideoCaptureFormat* video_format) {
video_format->pixel_format = media::PIXEL_FORMAT_I420;
video_format->frame_size.set_width(0);
video_format->frame_size.set_height(0);
size_t index = 0;
size_t blank_position = 0;
base::StringPiece token;
while ((blank_position = file_header.find_first_of("\n ", index)) !=
std::string::npos) {
token =
base::StringPiece(&file_header[index + 1], blank_position - index - 1);
CHECK(!token.empty());
switch (file_header[index]) {
case 'W':
video_format->frame_size.set_width(ParseY4MInt(token));
break;
case 'H':
video_format->frame_size.set_height(ParseY4MInt(token));
break;
case 'F': {
if (token[0] == 'R')
break;
int fps_numerator, fps_denominator;
ParseY4MRational(token, &fps_numerator, &fps_denominator);
video_format->frame_rate = fps_numerator / fps_denominator;
break;
}
case 'I':
CHECK_NE(token[0], 'm');
break;
case 'A':
break;
case 'C':
CHECK(token == "420" || token == "420jpeg" || token == "420paldv")
<< token;
break;
default:
break;
}
if (file_header[blank_position] == '\n')
break;
index = blank_position + 1;
}
CHECK(video_format->IsValid());
}
int64 ParseFileAndExtractVideoFormat(
base::File* file,
media::VideoCaptureFormat* video_format) {
std::string header(kY4MHeaderMaxSize, 0);
file->Read(0, &header[0], kY4MHeaderMaxSize - 1);
size_t header_end = header.find(kY4MSimpleFrameDelimiter);
CHECK_NE(header_end, header.npos);
ParseY4MTags(header, video_format);
return header_end + kY4MSimpleFrameDelimiterSize;
}
base::File OpenFileForRead(const base::FilePath& file_path) {
base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
CHECK(file.IsValid()) << file_path.value();
return file.Pass();
}
base::FilePath GetFilePathFromCommandLine() {
base::FilePath command_line_file_path =
CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kUseFileForFakeVideoCapture);
CHECK(!command_line_file_path.empty());
return command_line_file_path;
}
void FileVideoCaptureDevice::GetDeviceNames(Names* const device_names) {
DCHECK(device_names->empty());
base::FilePath command_line_file_path = GetFilePathFromCommandLine();
#if defined(OS_WIN)
device_names->push_back(
Name(base::SysWideToUTF8(command_line_file_path.value()),
kFileVideoCaptureDeviceName));
#else
device_names->push_back(Name(command_line_file_path.value(),
kFileVideoCaptureDeviceName));
#endif
}
void FileVideoCaptureDevice::GetDeviceSupportedFormats(
const Name& device,
VideoCaptureFormats* supported_formats) {
base::File file = OpenFileForRead(GetFilePathFromCommandLine());
VideoCaptureFormat capture_format;
ParseFileAndExtractVideoFormat(&file, &capture_format);
supported_formats->push_back(capture_format);
}
VideoCaptureDevice* FileVideoCaptureDevice::Create(const Name& device_name) {
#if defined(OS_WIN)
return new FileVideoCaptureDevice(
base::FilePath(base::SysUTF8ToWide(device_name.name())));
#else
return new FileVideoCaptureDevice(base::FilePath(device_name.name()));
#endif
}
FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
: capture_thread_("CaptureThread"),
file_path_(file_path),
frame_size_(0),
current_byte_index_(0),
first_frame_byte_index_(0) {}
FileVideoCaptureDevice::~FileVideoCaptureDevice() {
DCHECK(thread_checker_.CalledOnValidThread());
CHECK(!capture_thread_.IsRunning());
}
void FileVideoCaptureDevice::AllocateAndStart(
const VideoCaptureParams& params,
scoped_ptr<VideoCaptureDevice::Client> client) {
DCHECK(thread_checker_.CalledOnValidThread());
CHECK(!capture_thread_.IsRunning());
capture_thread_.Start();
capture_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&FileVideoCaptureDevice::OnAllocateAndStart,
base::Unretained(this),
params,
base::Passed(&client)));
}
void FileVideoCaptureDevice::StopAndDeAllocate() {
DCHECK(thread_checker_.CalledOnValidThread());
CHECK(capture_thread_.IsRunning());
capture_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate,
base::Unretained(this)));
capture_thread_.Stop();
}
int FileVideoCaptureDevice::CalculateFrameSize() {
DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
return capture_format_.frame_size.GetArea() * 12 / 8;
}
void FileVideoCaptureDevice::OnAllocateAndStart(
const VideoCaptureParams& params,
scoped_ptr<VideoCaptureDevice::Client> client) {
DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
client_ = client.Pass();
DCHECK(!file_.IsValid());
file_ = OpenFileForRead(file_path_);
first_frame_byte_index_ =
ParseFileAndExtractVideoFormat(&file_, &capture_format_);
current_byte_index_ = first_frame_byte_index_;
DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString()
<< ", fps: " << capture_format_.frame_rate;
frame_size_ = CalculateFrameSize();
video_frame_.reset(new uint8[frame_size_]);
capture_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
base::Unretained(this)));
}
void FileVideoCaptureDevice::OnStopAndDeAllocate() {
DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
file_.Close();
client_.reset();
current_byte_index_ = 0;
first_frame_byte_index_ = 0;
frame_size_ = 0;
video_frame_.reset();
}
void FileVideoCaptureDevice::OnCaptureTask() {
DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
if (!client_)
return;
int result = file_.Read(current_byte_index_,
reinterpret_cast<char*>(video_frame_.get()),
frame_size_);
if (result != frame_size_) {
CHECK_EQ(result, 0);
current_byte_index_ = first_frame_byte_index_;
CHECK_EQ(file_.Read(current_byte_index_,
reinterpret_cast<char*>(video_frame_.get()),
frame_size_),
frame_size_);
} else {
current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize;
}
client_->OnIncomingCapturedData(video_frame_.get(),
frame_size_,
capture_format_,
0,
base::TimeTicks::Now());
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
base::Unretained(this)),
base::TimeDelta::FromSeconds(1) / capture_format_.frame_rate);
}
}