| Index: media/capture/video/file_video_capture_device.cc
|
| diff --git a/media/capture/video/file_video_capture_device.cc b/media/capture/video/file_video_capture_device.cc
|
| deleted file mode 100644
|
| index 10b3710f53fdf9775cda8682174c3ae5faf2aa84..0000000000000000000000000000000000000000
|
| --- a/media/capture/video/file_video_capture_device.cc
|
| +++ /dev/null
|
| @@ -1,394 +0,0 @@
|
| -// Copyright 2013 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "media/capture/video/file_video_capture_device.h"
|
| -
|
| -#include <stddef.h>
|
| -#include <utility>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/location.h"
|
| -#include "base/macros.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_piece.h"
|
| -#include "base/threading/thread_task_runner_handle.h"
|
| -#include "media/base/video_capture_types.h"
|
| -#include "media/filters/jpeg_parser.h"
|
| -
|
| -namespace media {
|
| -
|
| -static const int kY4MHeaderMaxSize = 200;
|
| -static const char kY4MSimpleFrameDelimiter[] = "FRAME";
|
| -static const int kY4MSimpleFrameDelimiterSize = 6;
|
| -static const float kMJpegFrameRate = 30.0f;
|
| -
|
| -int ParseY4MInt(const base::StringPiece& token) {
|
| - int temp_int;
|
| - CHECK(base::StringToInt(token, &temp_int)) << token;
|
| - return temp_int;
|
| -}
|
| -
|
| -// Extract numerator and denominator out of a token that must have the aspect
|
| -// numerator:denominator, both integer numbers.
|
| -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);
|
| -}
|
| -
|
| -// This function parses the ASCII string in |header| as belonging to a Y4M file,
|
| -// returning the collected format in |video_format|. For a non authoritative
|
| -// explanation of the header format, check
|
| -// http://wiki.multimedia.cx/index.php?title=YUV4MPEG2
|
| -// Restrictions: Only interlaced I420 pixel format is supported, and pixel
|
| -// aspect ratio is ignored.
|
| -// Implementation notes: Y4M header should end with an ASCII 0x20 (whitespace)
|
| -// character, however all examples mentioned in the Y4M header description end
|
| -// with a newline character instead. Also, some headers do _not_ specify pixel
|
| -// format, in this case it means I420.
|
| -// This code was inspired by third_party/libvpx/.../y4minput.* .
|
| -void ParseY4MTags(const std::string& file_header,
|
| - media::VideoCaptureFormat* video_format) {
|
| - media::VideoCaptureFormat format;
|
| - format.pixel_format = media::PIXEL_FORMAT_I420;
|
| - 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) {
|
| - // Every token is supposed to have an identifier letter and a bunch of
|
| - // information immediately after, which we extract into a |token| here.
|
| - token =
|
| - base::StringPiece(&file_header[index + 1], blank_position - index - 1);
|
| - CHECK(!token.empty());
|
| - switch (file_header[index]) {
|
| - case 'W':
|
| - format.frame_size.set_width(ParseY4MInt(token));
|
| - break;
|
| - case 'H':
|
| - format.frame_size.set_height(ParseY4MInt(token));
|
| - break;
|
| - case 'F': {
|
| - // If the token is "FRAME", it means we have finished with the header.
|
| - if (token[0] == 'R')
|
| - break;
|
| - int fps_numerator, fps_denominator;
|
| - ParseY4MRational(token, &fps_numerator, &fps_denominator);
|
| - format.frame_rate = fps_numerator / fps_denominator;
|
| - break;
|
| - }
|
| - case 'I':
|
| - // Interlacing is ignored, but we don't like mixed modes.
|
| - CHECK_NE(token[0], 'm');
|
| - break;
|
| - case 'A':
|
| - // Pixel aspect ratio ignored.
|
| - break;
|
| - case 'C':
|
| - CHECK(token == "420" || token == "420jpeg" || token == "420paldv")
|
| - << token; // Only I420 is supported, and we fudge the variants.
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| - // We're done if we have found a newline character right after the token.
|
| - if (file_header[blank_position] == '\n')
|
| - break;
|
| - index = blank_position + 1;
|
| - }
|
| - // Last video format semantic correctness check before sending it back.
|
| - CHECK(format.IsValid());
|
| - *video_format = format;
|
| -}
|
| -
|
| -class VideoFileParser {
|
| - public:
|
| - explicit VideoFileParser(const base::FilePath& file_path);
|
| - virtual ~VideoFileParser();
|
| -
|
| - // Parses file header and collects format information in |capture_format|.
|
| - virtual bool Initialize(media::VideoCaptureFormat* capture_format) = 0;
|
| -
|
| - // Gets the start pointer of next frame and stores current frame size in
|
| - // |frame_size|.
|
| - virtual const uint8_t* GetNextFrame(int* frame_size) = 0;
|
| -
|
| - protected:
|
| - const base::FilePath file_path_;
|
| - int frame_size_;
|
| - size_t current_byte_index_;
|
| - size_t first_frame_byte_index_;
|
| -};
|
| -
|
| -class Y4mFileParser final : public VideoFileParser {
|
| - public:
|
| - explicit Y4mFileParser(const base::FilePath& file_path);
|
| -
|
| - // VideoFileParser implementation, class methods.
|
| - ~Y4mFileParser() override;
|
| - bool Initialize(media::VideoCaptureFormat* capture_format) override;
|
| - const uint8_t* GetNextFrame(int* frame_size) override;
|
| -
|
| - private:
|
| - std::unique_ptr<base::File> file_;
|
| - std::unique_ptr<uint8_t[]> video_frame_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Y4mFileParser);
|
| -};
|
| -
|
| -class MjpegFileParser final : public VideoFileParser {
|
| - public:
|
| - explicit MjpegFileParser(const base::FilePath& file_path);
|
| -
|
| - // VideoFileParser implementation, class methods.
|
| - ~MjpegFileParser() override;
|
| - bool Initialize(media::VideoCaptureFormat* capture_format) override;
|
| - const uint8_t* GetNextFrame(int* frame_size) override;
|
| -
|
| - private:
|
| - std::unique_ptr<base::MemoryMappedFile> mapped_file_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(MjpegFileParser);
|
| -};
|
| -
|
| -VideoFileParser::VideoFileParser(const base::FilePath& file_path)
|
| - : file_path_(file_path),
|
| - frame_size_(0),
|
| - current_byte_index_(0),
|
| - first_frame_byte_index_(0) {}
|
| -
|
| -VideoFileParser::~VideoFileParser() {}
|
| -
|
| -Y4mFileParser::Y4mFileParser(const base::FilePath& file_path)
|
| - : VideoFileParser(file_path) {}
|
| -
|
| -Y4mFileParser::~Y4mFileParser() {}
|
| -
|
| -bool Y4mFileParser::Initialize(media::VideoCaptureFormat* capture_format) {
|
| - file_.reset(new base::File(file_path_,
|
| - base::File::FLAG_OPEN | base::File::FLAG_READ));
|
| - if (!file_->IsValid()) {
|
| - DLOG(ERROR) << file_path_.value() << ", error: "
|
| - << base::File::ErrorToString(file_->error_details());
|
| - return false;
|
| - }
|
| -
|
| - std::string header(kY4MHeaderMaxSize, '\0');
|
| - file_->Read(0, &header[0], header.size());
|
| - const size_t header_end = header.find(kY4MSimpleFrameDelimiter);
|
| - CHECK_NE(header_end, header.npos);
|
| -
|
| - ParseY4MTags(header, capture_format);
|
| - first_frame_byte_index_ = header_end + kY4MSimpleFrameDelimiterSize;
|
| - current_byte_index_ = first_frame_byte_index_;
|
| - frame_size_ = capture_format->ImageAllocationSize();
|
| - return true;
|
| -}
|
| -
|
| -const uint8_t* Y4mFileParser::GetNextFrame(int* frame_size) {
|
| - if (!video_frame_)
|
| - video_frame_.reset(new uint8_t[frame_size_]);
|
| - int result =
|
| - file_->Read(current_byte_index_,
|
| - reinterpret_cast<char*>(video_frame_.get()), frame_size_);
|
| -
|
| - // If we passed EOF to base::File, it will return 0 read characters. In that
|
| - // case, reset the pointer and read again.
|
| - 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;
|
| - }
|
| - *frame_size = frame_size_;
|
| - return video_frame_.get();
|
| -}
|
| -
|
| -MjpegFileParser::MjpegFileParser(const base::FilePath& file_path)
|
| - : VideoFileParser(file_path) {}
|
| -
|
| -MjpegFileParser::~MjpegFileParser() {}
|
| -
|
| -bool MjpegFileParser::Initialize(media::VideoCaptureFormat* capture_format) {
|
| - mapped_file_.reset(new base::MemoryMappedFile());
|
| -
|
| - if (!mapped_file_->Initialize(file_path_) || !mapped_file_->IsValid()) {
|
| - LOG(ERROR) << "File memory map error: " << file_path_.value();
|
| - return false;
|
| - }
|
| -
|
| - JpegParseResult result;
|
| - if (!ParseJpegStream(mapped_file_->data(), mapped_file_->length(), &result))
|
| - return false;
|
| -
|
| - frame_size_ = result.image_size;
|
| - if (frame_size_ > static_cast<int>(mapped_file_->length())) {
|
| - LOG(ERROR) << "File is incomplete";
|
| - return false;
|
| - }
|
| -
|
| - VideoCaptureFormat format;
|
| - format.pixel_format = media::PIXEL_FORMAT_MJPEG;
|
| - format.frame_size.set_width(result.frame_header.visible_width);
|
| - format.frame_size.set_height(result.frame_header.visible_height);
|
| - format.frame_rate = kMJpegFrameRate;
|
| - if (!format.IsValid())
|
| - return false;
|
| - *capture_format = format;
|
| - return true;
|
| -}
|
| -
|
| -const uint8_t* MjpegFileParser::GetNextFrame(int* frame_size) {
|
| - const uint8_t* buf_ptr = mapped_file_->data() + current_byte_index_;
|
| -
|
| - JpegParseResult result;
|
| - if (!ParseJpegStream(buf_ptr, mapped_file_->length() - current_byte_index_,
|
| - &result)) {
|
| - return nullptr;
|
| - }
|
| - *frame_size = frame_size_ = result.image_size;
|
| - current_byte_index_ += frame_size_;
|
| - // Reset the pointer to play repeatedly.
|
| - if (current_byte_index_ >= mapped_file_->length())
|
| - current_byte_index_ = first_frame_byte_index_;
|
| - return buf_ptr;
|
| -}
|
| -
|
| -// static
|
| -bool FileVideoCaptureDevice::GetVideoCaptureFormat(
|
| - const base::FilePath& file_path,
|
| - media::VideoCaptureFormat* video_format) {
|
| - std::unique_ptr<VideoFileParser> file_parser =
|
| - GetVideoFileParser(file_path, video_format);
|
| - return file_parser != nullptr;
|
| -}
|
| -
|
| -// static
|
| -std::unique_ptr<VideoFileParser> FileVideoCaptureDevice::GetVideoFileParser(
|
| - const base::FilePath& file_path,
|
| - media::VideoCaptureFormat* video_format) {
|
| - std::unique_ptr<VideoFileParser> file_parser;
|
| - std::string file_name(file_path.value().begin(), file_path.value().end());
|
| -
|
| - if (base::EndsWith(file_name, "y4m",
|
| - base::CompareCase::INSENSITIVE_ASCII)) {
|
| - file_parser.reset(new Y4mFileParser(file_path));
|
| - } else if (base::EndsWith(file_name, "mjpeg",
|
| - base::CompareCase::INSENSITIVE_ASCII)) {
|
| - file_parser.reset(new MjpegFileParser(file_path));
|
| - } else {
|
| - LOG(ERROR) << "Unsupported file format.";
|
| - return file_parser;
|
| - }
|
| -
|
| - if (!file_parser->Initialize(video_format)) {
|
| - file_parser.reset();
|
| - }
|
| - return file_parser;
|
| -}
|
| -
|
| -FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
|
| - : capture_thread_("CaptureThread"), file_path_(file_path) {}
|
| -
|
| -FileVideoCaptureDevice::~FileVideoCaptureDevice() {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - // Check if the thread is running.
|
| - // This means that the device have not been DeAllocated properly.
|
| - CHECK(!capture_thread_.IsRunning());
|
| -}
|
| -
|
| -void FileVideoCaptureDevice::AllocateAndStart(
|
| - const VideoCaptureParams& params,
|
| - std::unique_ptr<VideoCaptureDevice::Client> client) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - CHECK(!capture_thread_.IsRunning());
|
| -
|
| - capture_thread_.Start();
|
| - capture_thread_.task_runner()->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_.task_runner()->PostTask(
|
| - FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnStopAndDeAllocate,
|
| - base::Unretained(this)));
|
| - capture_thread_.Stop();
|
| -}
|
| -
|
| -void FileVideoCaptureDevice::OnAllocateAndStart(
|
| - const VideoCaptureParams& params,
|
| - std::unique_ptr<VideoCaptureDevice::Client> client) {
|
| - DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
|
| -
|
| - client_ = std::move(client);
|
| -
|
| - DCHECK(!file_parser_);
|
| - file_parser_ = GetVideoFileParser(file_path_, &capture_format_);
|
| - if (!file_parser_) {
|
| - client_->OnError(FROM_HERE, "Could not open Video file");
|
| - return;
|
| - }
|
| -
|
| - DVLOG(1) << "Opened video file " << capture_format_.frame_size.ToString()
|
| - << ", fps: " << capture_format_.frame_rate;
|
| -
|
| - capture_thread_.task_runner()->PostTask(
|
| - FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
|
| - base::Unretained(this)));
|
| -}
|
| -
|
| -void FileVideoCaptureDevice::OnStopAndDeAllocate() {
|
| - DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
|
| - file_parser_.reset();
|
| - client_.reset();
|
| - next_frame_time_ = base::TimeTicks();
|
| -}
|
| -
|
| -void FileVideoCaptureDevice::OnCaptureTask() {
|
| - DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
|
| - if (!client_)
|
| - return;
|
| -
|
| - // Give the captured frame to the client.
|
| - int frame_size = 0;
|
| - const uint8_t* frame_ptr = file_parser_->GetNextFrame(&frame_size);
|
| - DCHECK(frame_size);
|
| - CHECK(frame_ptr);
|
| - const base::TimeTicks current_time = base::TimeTicks::Now();
|
| - if (first_ref_time_.is_null())
|
| - first_ref_time_ = current_time;
|
| - client_->OnIncomingCapturedData(frame_ptr, frame_size, capture_format_, 0,
|
| - current_time, current_time - first_ref_time_);
|
| - // Reschedule next CaptureTask.
|
| - const base::TimeDelta frame_interval =
|
| - base::TimeDelta::FromMicroseconds(1E6 / capture_format_.frame_rate);
|
| - if (next_frame_time_.is_null()) {
|
| - next_frame_time_ = current_time + frame_interval;
|
| - } else {
|
| - next_frame_time_ += frame_interval;
|
| - // Don't accumulate any debt if we are lagging behind - just post next frame
|
| - // immediately and continue as normal.
|
| - if (next_frame_time_ < current_time)
|
| - next_frame_time_ = current_time;
|
| - }
|
| - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| - FROM_HERE, base::Bind(&FileVideoCaptureDevice::OnCaptureTask,
|
| - base::Unretained(this)),
|
| - next_frame_time_ - current_time);
|
| -}
|
| -
|
| -} // namespace media
|
|
|