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

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 wucheng@ 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..a7d649e59dc882f971c876d8bb1275db389fa2fe 100644
--- a/media/capture/video/file_video_capture_device.cc
+++ b/media/capture/video/file_video_capture_device.cc
@@ -8,13 +8,15 @@
#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 = 30;
-int ParseY4MInt(const base::StringPiece& token) {
+static int ParseY4MInt(const base::StringPiece& token) {
mcasas 2015/08/18 18:28:42 IIRC, media/ folder has this strange thing of _not
henryhsu 2015/08/19 01:57:24 Hi xhwang, do you know this?
int temp_int;
CHECK(base::StringToInt(token, &temp_int)) << token;
return temp_int;
@@ -22,7 +24,7 @@ int ParseY4MInt(const base::StringPiece& token) {
// Extract numerator and denominator out of a token that must have the aspect
// numerator:denominator, both integer numbers.
-void ParseY4MRational(const base::StringPiece& token,
+static void ParseY4MRational(const base::StringPiece& token,
int* numerator,
int* denominator) {
size_t index_divider = token.find(':');
@@ -43,11 +45,10 @@ 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,
+static 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);
- video_format->frame_size.set_height(0);
+ media::VideoCaptureFormat format;
+ format.pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_I420;
size_t index = 0;
size_t blank_position = 0;
base::StringPiece token;
@@ -57,13 +58,14 @@ 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));
+ format.frame_size.set_width(ParseY4MInt(token));
break;
case 'H':
- video_format->frame_size.set_height(ParseY4MInt(token));
+ format.frame_size.set_height(ParseY4MInt(token));
break;
case 'F': {
// If the token is "FRAME", it means we have finished with the header.
@@ -71,20 +73,20 @@ void ParseY4MTags(const std::string& file_header,
break;
int fps_numerator, fps_denominator;
ParseY4MRational(token, &fps_numerator, &fps_denominator);
- video_format->frame_rate = 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;
+ 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 +96,51 @@ 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());
+ if (!format.IsValid())
mcasas 2015/08/18 18:28:42 The original CHECK() was correct (and hammered by
henryhsu 2015/08/19 01:57:24 Current implementation is to parse Y4M and MJPEG.
+ return false;
+ *video_format = format;
+ return true;
}
-// 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);
mcasas 2015/08/18 18:28:42 const (maybe)
size_t header_end = header.find(kY4MSimpleFrameDelimiter);
mcasas 2015/08/18 18:28:41 const
henryhsu 2015/08/19 10:05:11 Done.
- 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(new base::MemoryMappedFile());
+
+ if (!mapped_file->Initialize(file_path) || !mapped_file->IsValid()) {
+ LOG(ERROR) << "File memory map error: " << file_path.value();
+ return nullptr;
+ }
+ 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_);
+ mapped_file_ = OpenFileForRead(file_path_);
+ if (!mapped_file_) {
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,50 @@ 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();
}
-void FileVideoCaptureDevice::OnCaptureTask() {
+const uint8_t* FileVideoCaptureDevice::GetNextFrame() {
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_);
+ const uint8_t* buf_ptr = mapped_file_->data() + current_byte_index_;
- // 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 {
+ if (capture_format_.pixel_format == media::VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG) {
+ JpegParseResult result;
+ if (!ParseJpegPicture(buf_ptr, mapped_file_->length() - current_byte_index_,
+ &result)) {
+ return nullptr;
+ }
+ // TODO(henryhsu): Update |capture_format_| if video has resolution change.
+ frame_size_ = result.image_size;
+ current_byte_index_ += frame_size_;
+ } else if (capture_format_.pixel_format ==
+ media::VIDEO_CAPTURE_PIXEL_FORMAT_I420) {
current_byte_index_ += frame_size_ + kY4MSimpleFrameDelimiterSize;
+ } else {
+ NOTREACHED() << "Not supported format: " << capture_format_.pixel_format;
}
+ // Play the video repeatly.
mcasas 2015/08/18 18:28:42 s/repeatly/repeatedly/ but I'd just remove this co
henryhsu 2015/08/19 10:05:11 Done.
+ 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;
// 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