Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Unified Diff: media/capture/video/file_video_capture_device.cc

Issue 1291933002: File video capture device supports MJPEG format (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address kcwu@ Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
index e7d9f064195f00c622f22724bd6eb995e7a0caec..5ffb59dce0977a5c7053239fda7998c32257c0ca 100644
--- a/media/capture/video/file_video_capture_device.cc
+++ b/media/capture/video/file_video_capture_device.cc
@@ -8,11 +8,13 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.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 int kMJpegFrameRate = 25;
int ParseY4MInt(const base::StringPiece& token) {
int temp_int;
@@ -43,7 +45,7 @@ void ParseY4MRational(const base::StringPiece& token,
// 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,
+bool ParseY4MTags(const std::string& file_header,
media::VideoCaptureFormat* video_format) {
video_format->pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_I420;
video_format->frame_size.set_width(0);
@@ -57,7 +59,8 @@ void ParseY4MTags(const std::string& file_header,
// information immediately after, which we extract into a |token| here.
token =
base::StringPiece(&file_header[index + 1], blank_position - index - 1);
- CHECK(!token.empty());
+ if (token.empty())
+ return false;
switch (file_header[index]) {
case 'W':
video_format->frame_size.set_width(ParseY4MInt(token));
@@ -76,15 +79,15 @@ void ParseY4MTags(const std::string& file_header,
}
case 'I':
// Interlacing is ignored, but we don't like mixed modes.
- CHECK_NE(token[0], 'm');
- break;
+ if (token[0] == 'm')
+ return false;
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;
+ // Only I420 is supported, and we fudge the variants.
+ if (token != "420" && token != "420jpeg" && token != "420paldv")
+ return false;
default:
break;
}
@@ -94,37 +97,50 @@ void ParseY4MTags(const std::string& file_header,
index = blank_position + 1;
}
// Last video format semantic correctness check before sending it back.
- CHECK(video_format->IsValid());
+ return video_format->IsValid();
}
-// Reads and parses the header of a Y4M |file|, returning the collected pixel
-// format in |video_format|. Returns the index of the first byte of the first
-// video frame.
-// Restrictions: Only trivial per-frame headers are supported.
// static
-int64 FileVideoCaptureDevice::ParseFileAndExtractVideoFormat(
- base::File* file,
- media::VideoCaptureFormat* video_format) {
- std::string header(kY4MHeaderMaxSize, 0);
- file->Read(0, &header[0], kY4MHeaderMaxSize - 1);
+size_t FileVideoCaptureDevice::ParseFileAndExtractVideoFormat(
+ const base::MemoryMappedFile* mapped_file,
+ media::VideoCaptureFormat* video_format,
+ int* frame_size) {
+ const char* buf = reinterpret_cast<const char*>(mapped_file->data());
+ // Try Y4M format first.
+ std::string header(buf, kY4MHeaderMaxSize - 1);
size_t header_end = header.find(kY4MSimpleFrameDelimiter);
- CHECK_NE(header_end, header.npos);
+ if (header_end != header.npos && ParseY4MTags(header, video_format)) {
+ *frame_size = video_format->ImageAllocationSize();
+ return header_end + kY4MSimpleFrameDelimiterSize;
+ }
- ParseY4MTags(header, video_format);
- return header_end + kY4MSimpleFrameDelimiterSize;
+ // Try MJPEG format.
+ JpegParseResult result;
+ if (!ParseJpegPicture(mapped_file->data(), mapped_file->length(), &result))
+ return -1;
+ video_format->pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG;
+ video_format->frame_size.set_width(result.frame_header.visible_width);
+ video_format->frame_size.set_height(result.frame_header.visible_height);
+ video_format->frame_rate = kMJpegFrameRate;
+ if (!video_format->IsValid())
+ return -1;
+ *frame_size = result.image_size;
+ return 0;
}
-// Opens a given file for reading, and returns the file to the caller, who is
-// responsible for closing it.
// static
-base::File FileVideoCaptureDevice::OpenFileForRead(
+scoped_ptr<base::MemoryMappedFile> FileVideoCaptureDevice::OpenFileForRead(
const base::FilePath& file_path) {
- base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
- DLOG_IF(ERROR, file.IsValid())
- << file_path.value()
- << ", error: " << base::File::ErrorToString(file.error_details());
- return file.Pass();
+ scoped_ptr<base::MemoryMappedFile> mapped_file;
+ mapped_file.reset(new base::MemoryMappedFile());
+ if (!mapped_file)
+ return nullptr;
+
+ if (!mapped_file->Initialize(file_path)) {
+ LOG(ERROR) << "File memory map error: " << file_path.value();
+ }
+ return mapped_file.Pass();
}
FileVideoCaptureDevice::FileVideoCaptureDevice(const base::FilePath& file_path)
@@ -165,12 +181,6 @@ void FileVideoCaptureDevice::StopAndDeAllocate() {
capture_thread_.Stop();
}
-int FileVideoCaptureDevice::CalculateFrameSize() const {
- DCHECK_EQ(capture_format_.pixel_format, VIDEO_CAPTURE_PIXEL_FORMAT_I420);
- DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
- return capture_format_.ImageAllocationSize();
-}
-
void FileVideoCaptureDevice::OnAllocateAndStart(
const VideoCaptureParams& params,
scoped_ptr<VideoCaptureDevice::Client> client) {
@@ -179,21 +189,24 @@ void FileVideoCaptureDevice::OnAllocateAndStart(
client_ = client.Pass();
// Open the file and parse the header. Get frame size and format.
- DCHECK(!file_.IsValid());
- file_ = OpenFileForRead(file_path_);
- if (!file_.IsValid()) {
+ DCHECK(!mapped_file_->IsValid());
+ mapped_file_ = OpenFileForRead(file_path_);
+ if (!mapped_file_->IsValid()) {
kcwu 2015/08/14 11:50:04 OpenFileForRead() may return nullptr.
henryhsu 2015/08/17 02:39:33 Done.
client_->OnError("Could not open Video file");
return;
}
- first_frame_byte_index_ =
- ParseFileAndExtractVideoFormat(&file_, &capture_format_);
+ first_frame_byte_index_ = ParseFileAndExtractVideoFormat(
+ mapped_file_.get(), &capture_format_, &frame_size_);
+
+ if (first_frame_byte_index_ < 0 ||
+ first_frame_byte_index_ + frame_size_ > mapped_file_->length()) {
+ client_->OnError("File is incomplete");
+ return;
+ }
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)));
@@ -201,40 +214,51 @@ void FileVideoCaptureDevice::OnAllocateAndStart(
void FileVideoCaptureDevice::OnStopAndDeAllocate() {
DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
- file_.Close();
+ mapped_file_.reset();
client_.reset();
current_byte_index_ = 0;
first_frame_byte_index_ = 0;
frame_size_ = 0;
next_frame_time_ = base::TimeTicks();
- video_frame_.reset();
+}
+
+const uint8_t* FileVideoCaptureDevice::GetNextFrame() {
+ DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
+ const uint8_t* buf_ptr = mapped_file_->data() + current_byte_index_;
+ int image_size = 0;
+
+ if (capture_format_.pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG) {
+ JpegParseResult result;
+ if (!ParseJpegPicture(mapped_file_->data() + current_byte_index_,
+ mapped_file_->length() - current_byte_index_,
+ &result)) {
+ return nullptr;
+ }
+ // TODO(henryhsu): Update |capture_format_| if video has resolution change.
+ frame_size_ = result.image_size;
+ image_size = frame_size_;
+ } else if (capture_format_.pixel_format ==
+ media::VIDEO_CAPTURE_PIXEL_FORMAT_I420) {
+ image_size = frame_size_ + kY4MSimpleFrameDelimiterSize;
+ }
kcwu 2015/08/14 11:50:04 else UNREACHABLE
henryhsu 2015/08/17 02:39:33 Done.
+ current_byte_index_ += image_size;
+ // Play the video repeatly.
+ if (current_byte_index_ >= mapped_file_->length())
+ current_byte_index_ = first_frame_byte_index_;
+ return buf_ptr;
}
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 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;
- }
// Give the captured frame to the client.
+ const uint8_t* frame_ptr = GetNextFrame();
+ CHECK(frame_ptr);
const base::TimeTicks current_time = base::TimeTicks::Now();
- client_->OnIncomingCapturedData(video_frame_.get(), frame_size_,
- capture_format_, 0, current_time);
+ client_->OnIncomingCapturedData(frame_ptr, frame_size_, capture_format_, 0,
+ current_time);
// Reschedule next CaptureTask.
const base::TimeDelta frame_interval =
base::TimeDelta::FromMicroseconds(1E6 / capture_format_.frame_rate);

Powered by Google App Engine
This is Rietveld 408576698