summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-30 00:57:51 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-30 00:57:51 +0000
commit599c04f92812016bb927fc1844a4d8fb532a32d4 (patch)
treea8322ac510a8eab9a4c256eb18e800cf0b57b967 /media
parent7106f7918a629ec51411ed5dc8d3a01372b3443d (diff)
downloadchromium_src-599c04f92812016bb927fc1844a4d8fb532a32d4.zip
chromium_src-599c04f92812016bb927fc1844a4d8fb532a32d4.tar.gz
chromium_src-599c04f92812016bb927fc1844a4d8fb532a32d4.tar.bz2
Use FFmpeg demuxer and bitstream converters in omx_test
omx_test now uses FFmpeg demuxer and bistream converters. It can now parse mp4 files and convert them to H264 Annex B stream for input to OpenMAX decoder. Also refactored the file to move different reader methods to separate classes. BUG=32753 TEST=this is a test by itself Review URL: http://codereview.chromium.org/556032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37592 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/media.gyp9
-rw-r--r--media/tools/omx_test/color_space_util.cc61
-rw-r--r--media/tools/omx_test/color_space_util.h24
-rw-r--r--media/tools/omx_test/file_reader_util.cc268
-rw-r--r--media/tools/omx_test/file_reader_util.h119
-rw-r--r--media/tools/omx_test/omx_test.cc341
6 files changed, 579 insertions, 243 deletions
diff --git a/media/media.gyp b/media/media.gyp
index 2c57239d..90db049 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -269,10 +269,15 @@
'target_name': 'omx_test',
'type': 'executable',
'dependencies': [
- 'omx_wrapper',
- '../base/base.gyp:base',
+ 'media',
+ '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
+ '../third_party/openmax/openmax.gyp:il',
],
'sources': [
+ 'tools/omx_test/color_space_util.cc',
+ 'tools/omx_test/color_space_util.h',
+ 'tools/omx_test/file_reader_util.cc',
+ 'tools/omx_test/file_reader_util.h',
'tools/omx_test/omx_test.cc',
],
},
diff --git a/media/tools/omx_test/color_space_util.cc b/media/tools/omx_test/color_space_util.cc
new file mode 100644
index 0000000..87a7bba
--- /dev/null
+++ b/media/tools/omx_test/color_space_util.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 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/tools/omx_test/color_space_util.h"
+
+namespace media {
+
+void NV21toIYUV(const uint8* nv21, uint8* iyuv, int width, int height) {
+ memcpy(iyuv, nv21, width * height);
+ iyuv += width * height;
+ nv21 += width * height;
+ uint8* u = iyuv;
+ uint8* v = iyuv + width * height / 4;
+
+ for (int i = 0; i < width * height / 4; ++i) {
+ *v++ = *nv21++;
+ *u++ = *nv21++;
+ }
+}
+
+void NV21toYV12(const uint8* nv21, uint8* yv12, int width, int height) {
+ memcpy(yv12, nv21, width * height);
+ yv12 += width * height;
+ nv21 += width * height;
+ uint8* v = yv12;
+ uint8* u = yv12 + width * height / 4;
+
+ for (int i = 0; i < width * height / 4; ++i) {
+ *v++ = *nv21++;
+ *u++ = *nv21++;
+ }
+}
+
+void IYUVtoNV21(const uint8* iyuv, uint8* nv21, int width, int height) {
+ memcpy(nv21, iyuv, width * height);
+ iyuv += width * height;
+ nv21 += width * height;
+ const uint8* u = iyuv;
+ const uint8* v = iyuv + width * height / 4;
+
+ for (int i = 0; i < width * height / 4; ++i) {
+ *nv21++ = *v++;
+ *nv21++ = *u++;
+ }
+}
+
+void YV12toNV21(const uint8* yv12, uint8* nv21, int width, int height) {
+ memcpy(nv21, yv12, width * height);
+ yv12 += width * height;
+ nv21 += width * height;
+ const uint8* v = yv12;
+ const uint8* u = yv12 + width * height / 4;
+
+ for (int i = 0; i < width * height / 4; ++i) {
+ *nv21++ = *v++;
+ *nv21++ = *u++;
+ }
+}
+
+} // namespace media
diff --git a/media/tools/omx_test/color_space_util.h b/media/tools/omx_test/color_space_util.h
new file mode 100644
index 0000000..1969485d
--- /dev/null
+++ b/media/tools/omx_test/color_space_util.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 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.
+
+// Color space conversion methods, they are for testing purpose and are
+// not optimized for production use.
+
+#ifndef MEDIA_TOOLS_OMX_TEST_COLOR_SPACE_UTIL_H_
+#define MEDIA_TOOLS_OMX_TEST_COLOR_SPACE_UTIL_H_
+
+#include "base/basictypes.h"
+
+namespace media {
+
+// First parameter is the input buffer, second parameter is the output
+// buffer.
+void NV21toIYUV(const uint8* nv21, uint8* iyuv, int width, int height);
+void NV21toYV12(const uint8* nv21, uint8* yv12, int width, int height);
+void IYUVtoNV21(const uint8* iyuv, uint8* nv21, int width, int height);
+void YV12toNV21(const uint8* yv12, uint8* nv21, int width, int height);
+
+} // namespace media
+
+#endif // MEDIA_TOOLS_OMX_TEST_COLOR_SPACE_UTIL_H_
diff --git a/media/tools/omx_test/file_reader_util.cc b/media/tools/omx_test/file_reader_util.cc
new file mode 100644
index 0000000..d95adaf
--- /dev/null
+++ b/media/tools/omx_test/file_reader_util.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2010 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/tools/omx_test/file_reader_util.h"
+
+#include <stdio.h>
+#include <string>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "media/ffmpeg/ffmpeg_common.h"
+#include "media/ffmpeg/file_protocol.h"
+#include "media/filters/bitstream_converter.h"
+#include "media/tools/omx_test/color_space_util.h"
+
+namespace media {
+
+//////////////////////////////////////////////////////////////////////////////
+// BasicFileReader
+BasicFileReader::BasicFileReader(const char* filename)
+ : filename_(filename),
+ file_(NULL) {
+}
+
+bool BasicFileReader::Initialize() {
+ file_.Set(file_util::OpenFile(filename_, "rb"));
+ if (!file_.get()) {
+ LOG(ERROR) << "unable to open " << filename_;
+ }
+ return file_.get() != NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// YuvFileReader
+YuvFileReader::YuvFileReader(const char* filename,
+ int width,
+ int height,
+ int loop_count,
+ bool enable_csc)
+ : BasicFileReader(filename),
+ width_(width),
+ height_(height),
+ loop_count_(loop_count),
+ output_nv21_(enable_csc) {
+}
+
+void YuvFileReader::Read(uint8** output, int* size) {
+ if (!file()) {
+ *size = 0;
+ *output = NULL;
+ return;
+ }
+
+ while (true) {
+ scoped_array<uint8> data;
+ int bytes_read = 0;
+
+ // OMX require encoder input are delivered in frames (or planes).
+ // Assume the input file is I420 YUV file.
+ const int kFrameSize = width_ * height_ * 3 / 2;
+ data.reset(new uint8[kFrameSize]);
+
+ if (output_nv21_) {
+ if (!csc_buf_.get())
+ csc_buf_.reset(new uint8[kFrameSize]);
+ bytes_read = fread(csc_buf_.get(), 1, kFrameSize, file());
+
+ // We do not convert partial frames.
+ if (bytes_read == kFrameSize)
+ IYUVtoNV21(csc_buf_.get(), data.get(), width_, height_);
+ else
+ bytes_read = 0; // force cleanup or loop around.
+ } else {
+ bytes_read = fread(data.get(), 1, kFrameSize, file());
+ }
+
+ if (bytes_read) {
+ *size = bytes_read;
+ *output = data.release();
+ break;
+ }
+
+ // Encounter the end of file.
+ if (loop_count_ == 1) {
+ // Signal end of stream.
+ *size = 0;
+ *output = data.release();
+ }
+
+ --loop_count_;
+ fseek(file(), 0, SEEK_SET);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BlockFileReader
+BlockFileReader::BlockFileReader(const char* filename,
+ int block_size)
+ : BasicFileReader(filename),
+ block_size_(block_size) {
+}
+
+void BlockFileReader::Read(uint8** output, int* size) {
+ CHECK(file());
+ *output = new uint8[block_size_];
+ *size = fread(*output, 1, block_size_, file());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// FFmpegFileReader
+FFmpegFileReader::FFmpegFileReader(const char* filename)
+ : filename_(filename),
+ format_context_(NULL),
+ codec_context_(NULL),
+ target_stream_(-1),
+ converter_(NULL) {
+}
+
+FFmpegFileReader::~FFmpegFileReader() {
+ if (format_context_)
+ av_close_input_file(format_context_);
+}
+
+bool FFmpegFileReader::Initialize() {
+ if (av_open_input_file(&format_context_,
+ filename_.c_str(), NULL, 0, NULL) < 0) {
+ LOG(ERROR) << "can't open file " << filename_;
+ return false;;
+ }
+ if (av_find_stream_info(format_context_) < 0) {
+ LOG(ERROR) << "can't use FFmpeg to parse stream info";
+ return false;
+ }
+
+ for (size_t i = 0; i < format_context_->nb_streams; ++i) {
+ codec_context_ = format_context_->streams[i]->codec;
+
+ // Find the video stream.
+ if (codec_context_->codec_type == CODEC_TYPE_VIDEO) {
+ target_stream_ = i;
+ break;
+ }
+ }
+ if (target_stream_ == -1) {
+ LOG(ERROR) << "no video in the stream";
+ return false;
+ }
+
+ // Initialize the bitstream filter if needed.
+ // TODO(hclam): find a better way to identify mp4 container.
+ if (codec_context_->codec_id == CODEC_ID_H264) {
+ converter_.reset(new media::FFmpegBitstreamConverter(
+ "h264_mp4toannexb", codec_context_));
+ } else if (codec_context_->codec_id == CODEC_ID_MPEG4) {
+ converter_.reset(new media::FFmpegBitstreamConverter(
+ "mpeg4video_es", codec_context_));
+ }
+
+ if (converter_.get() && !converter_->Initialize()) {
+ converter_.reset();
+ LOG(ERROR) << "failed to initialize h264_mp4toannexb filter";
+ return false;
+ }
+ return true;
+}
+
+void FFmpegFileReader::Read(uint8** output, int* size) {
+ if (!format_context_ || !codec_context_ || target_stream_ == -1) {
+ *size = 0;
+ *output = NULL;
+ return;
+ }
+
+ AVPacket packet;
+ bool found = false;
+ while (!found) {
+ int result = av_read_frame(format_context_, &packet);
+ if (result < 0) {
+ *output = NULL;
+ *size = 0;
+ return;
+ }
+ if (packet.stream_index == target_stream_) {
+ if (converter_.get() && !converter_->ConvertPacket(&packet)) {
+ LOG(ERROR) << "failed to convert AVPacket";
+ }
+ *output = new uint8[packet.size];
+ *size = packet.size;
+ memcpy(*output, packet.data, packet.size);
+ found = true;
+ }
+ av_free_packet(&packet);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// H264FileReader
+const int kH264ReadSize = 1024 * 1024;
+
+H264FileReader::H264FileReader(const char* filename)
+ : BasicFileReader(filename),
+ read_buf_(new uint8[kH264ReadSize]),
+ current_(0),
+ used_(0) {
+}
+
+void H264FileReader::Read(uint8** output, int *size) {
+ // Fill the buffer when it's less than half full.
+ int read = 0;
+ if (used_ < kH264ReadSize / 2) {
+ read = fread(read_buf_.get(), 1, kH264ReadSize - used_, file());
+ CHECK(read >= 0);
+ used_ += read;
+ }
+
+ // If we failed to read.
+ if (current_ == used_) {
+ *output = NULL;
+ *size = 0;
+ return;
+ }
+
+ // Try to find start code of 0x00, 0x00, 0x01.
+ bool found = false;
+ int pos = current_ + 3;
+ for (; pos < used_ - 2; ++pos) {
+ if (read_buf_[pos] == 0 &&
+ read_buf_[pos+1] == 0 &&
+ read_buf_[pos+2] == 1) {
+ found = true;
+ break;
+ }
+ }
+
+ // If next NALU is found.
+ if (found) {
+ CHECK(pos > current_);
+ *size = pos - current_;
+ *output = new uint8[*size];
+ memcpy(*output, read_buf_.get() + current_, *size);
+ current_ = pos;
+
+ // If we have used_ more than half of the available buffer.
+ // Then move the unused_ buffer to the front to give space
+ // for more incoming output.
+ if (current_ > used_ / 2) {
+ CHECK(used_ > current_);
+ memcpy(read_buf_.get(),
+ read_buf_.get() + current_,
+ used_ - current_);
+ used_ = used_ - current_;
+ current_ = 0;
+ }
+ return;
+ }
+
+ // If next NALU is not found, assume the remaining data is a NALU
+ // and return the data.
+ CHECK(used_ > current_);
+ *size = used_ - current_;
+ *output = new uint8[*size];
+ memcpy(*output, read_buf_.get() + current_, *size);
+ current_ = used_;
+}
+
+} // namespace media
diff --git a/media/tools/omx_test/file_reader_util.h b/media/tools/omx_test/file_reader_util.h
new file mode 100644
index 0000000..c1c0b2d
--- /dev/null
+++ b/media/tools/omx_test/file_reader_util.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 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.
+
+#ifndef MEDIA_TOOLS_OMX_TEST_FILE_READER_UTIL_H_
+#define MEDIA_TOOLS_OMX_TEST_FILE_READER_UTIL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
+
+struct AVCodecContext;
+struct AVFormatContext;
+
+namespace media {
+
+class BitstreamConverter;
+
+// A class to help reading and parsing input file for use in omx_test.
+class FileReader {
+ public:
+ virtual ~FileReader() {}
+
+ // Initialize FileReader object, returns true if successful.
+ virtual bool Initialize() = 0;
+
+ // Read the file into |output|, and output the number of bytes read to
+ // |size|.
+ virtual void Read(uint8** output, int* size) = 0;
+};
+
+class BasicFileReader : public FileReader {
+ public:
+ explicit BasicFileReader(const char* filename);
+ virtual bool Initialize();
+ virtual void Read(uint8** output, int* size) = 0;
+
+ protected:
+ FILE* file() const { return file_.get(); }
+
+ private:
+ std::string filename_;
+ ScopedStdioHandle file_;
+
+ DISALLOW_COPY_AND_ASSIGN(BasicFileReader);
+};
+
+class YuvFileReader : public BasicFileReader {
+ public:
+ // Construct a YUV file reader with looping and color space conversion
+ // ability. |loop_count| specifies the number of times the input file
+ // is read. If |enable_csc| is true, input in YV420 is converted to
+ // NV21.
+ // TODO(jiesun): Make color space more generic not a hard coded color
+ // space conversion.
+ YuvFileReader(const char* filename,
+ int width,
+ int height,
+ int loop_count,
+ bool output_nv21);
+ virtual void Read(uint8** output, int* size);
+
+ private:
+ int width_;
+ int height_;
+ int loop_count_;
+ bool output_nv21_;
+ scoped_array<uint8> csc_buf_;
+
+ DISALLOW_COPY_AND_ASSIGN(YuvFileReader);
+};
+
+class BlockFileReader : public BasicFileReader {
+ public:
+ BlockFileReader(const char* filename,
+ int block_size);
+ virtual void Read(uint8** output, int* size);
+
+ private:
+ int block_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockFileReader);
+};
+
+class FFmpegFileReader : public FileReader {
+ public:
+ explicit FFmpegFileReader(const char* filename);
+ virtual ~FFmpegFileReader();
+ virtual bool Initialize();
+ virtual void Read(uint8** output, int* size);
+
+ private:
+ std::string filename_;
+ AVFormatContext* format_context_;
+ AVCodecContext* codec_context_;
+ int target_stream_;
+ scoped_ptr<media::BitstreamConverter> converter_;
+
+ DISALLOW_COPY_AND_ASSIGN(FFmpegFileReader);
+};
+
+class H264FileReader : public BasicFileReader {
+ public:
+ explicit H264FileReader(const char* filename);
+ virtual void Read(uint8** output, int* size);
+
+ private:
+ scoped_array<uint8> read_buf_;
+ int current_;
+ int used_;
+
+ DISALLOW_COPY_AND_ASSIGN(H264FileReader);
+};
+
+} // namespace media
+
+#endif // MEDIA_TOOLS_OMX_TEST_FILE_READER_UTIL_H_
diff --git a/media/tools/omx_test/omx_test.cc b/media/tools/omx_test/omx_test.cc
index a86f6a6..c2a4ad4 100644
--- a/media/tools/omx_test/omx_test.cc
+++ b/media/tools/omx_test/omx_test.cc
@@ -4,20 +4,25 @@
// A test program that drives an OpenMAX video decoder module. This program
// will take video in elementary stream and read into the decoder.
-// Usage of this program:
-// ./omx_test --file=<file> --component=<component> --codec=<codec>
-// <file> = Input file name
-// <component> = Name of the OpenMAX component
-// <codec> = Codec to be used, available codecs: h264, vc1, mpeg4, h263.
+//
+// Run the following command to see usage:
+// ./omx_test
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
+#include "base/scoped_handle.h"
#include "base/time.h"
+#include "media/base/media.h"
+#include "media/ffmpeg/ffmpeg_common.h"
+#include "media/ffmpeg/file_protocol.h"
+#include "media/filters/bitstream_converter.h"
#include "media/omx/input_buffer.h"
#include "media/omx/omx_codec.h"
+#include "media/tools/omx_test/color_space_util.h"
+#include "media/tools/omx_test/file_reader_util.h"
// This is the driver object to feed the decoder with data from a file.
// It also provides callbacks for the decoder to receive events from the
@@ -29,23 +34,54 @@ class TestApp {
media::OmxCodec::OmxMediaFormat& input_format,
media::OmxCodec::OmxMediaFormat& output_format,
bool simulate_copy,
- bool measure_fps,
bool enable_csc,
+ bool use_ffmpeg,
int loop_count)
- : input_filename_(input_filename),
- output_filename_(output_filename),
+ : output_filename_(output_filename),
input_format_(input_format),
output_format_(output_format),
simulate_copy_(simulate_copy),
- measure_fps_(measure_fps),
enable_csc_(enable_csc),
copy_buf_size_(0),
csc_buf_size_(0),
- input_file_(NULL),
output_file_(NULL),
stopped_(false),
- error_(false),
- loop_count_(loop_count) {
+ error_(false) {
+ // Creates the FileReader to read input file.
+ if (input_format_.codec == media::OmxCodec::kCodecRaw) {
+ file_reader_.reset(new media::YuvFileReader(
+ input_filename,
+ input_format.video_header.width,
+ input_format.video_header.height,
+ loop_count,
+ enable_csc));
+ } else if (use_ffmpeg) {
+ file_reader_.reset(
+ new media::FFmpegFileReader(input_filename));
+ } else {
+ // Creates a reader that reads in blocks of 32KB.
+ const int kReadSize = 32768;
+ file_reader_.reset(
+ new media::BlockFileReader(input_filename, kReadSize));
+ }
+ }
+
+ bool Initialize() {
+ if (!file_reader_->Initialize()) {
+ file_reader_.reset();
+ LOG(ERROR) << "can't initialize file reader";
+ return false;;
+ }
+
+ // Opens the output file for writing.
+ if (!output_filename_.empty()) {
+ output_file_.Set(file_util::OpenFile(output_filename_, "wb"));
+ if (!output_file_.get()) {
+ LOG(ERROR) << "can't open dump file %s" << output_filename_;
+ return false;
+ }
+ }
+ return true;
}
void StopCallback() {
@@ -61,7 +97,7 @@ class TestApp {
void ErrorCallback() {
// In case of error, this method is called. Mark the error flag and
// exit the message loop because we have no more work to do.
- printf("Error callback received!\n");
+ LOG(ERROR) << "Error callback received!";
error_ = true;
message_loop_.Quit();
}
@@ -106,7 +142,7 @@ class TestApp {
if (stopped_ || error_)
return;
- if (measure_fps_ && !frame_count_)
+ if (!frame_count_)
first_sample_delivered_time_ = base::TimeTicks::HighResNow();
// If we are readding to the end, then stop.
@@ -119,13 +155,13 @@ class TestApp {
codec_->Read(NewCallback(this, &TestApp::ReadCompleteCallback));
// Copy the output of the decoder to user memory.
- if (simulate_copy_ || output_file_) { // |output_file_| implies a copy.
+ if (simulate_copy_ || output_file_.get()) { // Implies a copy.
if (size > copy_buf_size_) {
copy_buf_.reset(new uint8[size]);
copy_buf_size_ = size;
}
memcpy(copy_buf_.get(), buffer, size);
- if (output_file_)
+ if (output_file_.get())
DumpOutputFile(copy_buf_.get(), size);
}
@@ -134,148 +170,16 @@ class TestApp {
bit_count_ += size << 3;
}
- void ReadInputFileYuv(uint8** output, int* size) {
- while (true) {
- uint8* data = NULL;
- int bytes_read = 0;
- // OMX require encoder input are delivered in frames (or planes).
- // Assume the input file is I420 YUV file.
- int width = input_format_.video_header.width;
- int height = input_format_.video_header.height;
- int frame_size = width * height * 3 / 2;
- data = new uint8[frame_size];
- if (enable_csc_) {
- CHECK(csc_buf_size_ >= frame_size);
- bytes_read = fread(csc_buf_.get(), 1,
- frame_size, input_file_);
- // We do not convert partial frames.
- if (bytes_read == frame_size)
- IYUVtoNV21(csc_buf_.get(), data, width, height);
- else
- bytes_read = 0; // force cleanup or loop around.
- } else {
- bytes_read = fread(data, 1, frame_size, input_file_);
- }
-
- if (bytes_read) {
- *size = bytes_read;
- *output = data;
- break;
- } else {
- // Encounter the end of file.
- if (loop_count_ == 1) {
- // Signal end of stream.
- *size = 0;
- *output = data;
- break;
- } else {
- --loop_count_;
- delete [] data;
- fseek(input_file_, 0, SEEK_SET);
- }
- }
- }
- }
-
- void ReadInputFileArbitrary(uint8** data, int* size) {
- // Feeds the decoder with 32KB of input data.
- const int kSize = 32768;
- *data = new uint8[kSize];
- *size = fread(*data, 1, kSize, input_file_);
- }
-
- void ReadInputFileH264(uint8** data, int* size) {
- const int kSize = 1024 * 1024;
- static int current = 0;
- static int used = 0;
-
- // Allocate read buffer.
- if (!read_buf_.get())
- read_buf_.reset(new uint8[kSize]);
-
- // Fill the buffer when it's less than half full.
- int read = 0;
- if (used < kSize / 2) {
- read = fread(read_buf_.get(), 1, kSize - used, input_file_);
- CHECK(read >= 0);
- used += read;
- }
-
- // If we failed to read.
- if (current == read) {
- *data = new uint8[1];
- *size = 0;
- return;
- } else {
- // Try to find start code of 0x00, 0x00, 0x01.
- bool found = false;
- int pos = current + 3;
- for (; pos < used - 2; ++pos) {
- if (read_buf_[pos] == 0 &&
- read_buf_[pos+1] == 0 &&
- read_buf_[pos+2] == 1) {
- found = true;
- break;
- }
- }
- if (found) {
- CHECK(pos > current);
- *size = pos - current;
- *data = new uint8[*size];
- memcpy(*data, read_buf_.get() + current, *size);
- current = pos;
- } else {
- CHECK(used > current);
- *size = used - current;
- *data = new uint8[*size];
- memcpy(*data, read_buf_.get() + current, *size);
- current = used;
- }
- if (used - current < current) {
- CHECK(used > current);
- memcpy(read_buf_.get(),
- read_buf_.get() + current,
- used - current);
- used = used - current;
- current = 0;
- }
- return;
- }
- }
-
void FeedInputBuffer() {
uint8* data;
int read;
- if (input_format_.codec == media::OmxCodec::kCodecRaw)
- ReadInputFileYuv(&data, &read);
- else if (input_format_.codec == media::OmxCodec::kCodecH264)
- ReadInputFileH264(&data, &read);
- else
- ReadInputFileArbitrary(&data, &read);
+ file_reader_->Read(&data, &read);
codec_->Feed(new media::InputBuffer(data, read),
NewCallback(this, &TestApp::FeedCallback));
}
void Run() {
- // Open the input file.
- input_file_ = file_util::OpenFile(input_filename_, "rb");
- if (!input_file_) {
- printf("Error - can't open file %s\n", input_filename_);
- return;
- }
-
- // Open the dump file.
- if (strlen(output_filename_)) {
- output_file_ = file_util::OpenFile(output_filename_, "wb");
- if (!input_file_) {
- fclose(input_file_);
- printf("Error - can't open dump file %s\n", output_filename_);
- return;
- }
- }
-
- if (measure_fps_)
- StartProfiler();
+ StartProfiler();
// Setup the |codec_| with the message loop of the current thread. Also
// setup component name, codec format and callbacks.
@@ -294,12 +198,7 @@ class TestApp {
// will return when we call message_loop_.Quit().
message_loop_.Run();
- if (measure_fps_)
- StopProfiler();
-
- fclose(input_file_);
- if (output_file_)
- fclose(output_file_);
+ StopProfiler();
}
void StartProfiler() {
@@ -309,74 +208,23 @@ class TestApp {
}
void StopProfiler() {
+ base::TimeDelta duration = base::TimeTicks::HighResNow() - start_time_;
+ int64 duration_ms = duration.InMilliseconds();
+ int64 fps = 0;
+ if (duration_ms) {
+ fps = (static_cast<int64>(frame_count_) *
+ base::Time::kMillisecondsPerSecond) / duration_ms;
+ }
+ base::TimeDelta delay = first_sample_delivered_time_ - start_time_;
printf("\n<<< frame delivered : %d >>>", frame_count_);
- stop_time_ = base::TimeTicks::HighResNow();
- base::TimeDelta duration = stop_time_ - start_time_;
- int64 micro_sec = duration.InMicroseconds();
- int64 fps = (static_cast<int64>(frame_count_) *
- base::Time::kMicrosecondsPerSecond) / micro_sec;
- printf("\n<<< time used(us) : %d >>>", static_cast<int>(micro_sec));
+ printf("\n<<< time used(ms) : %d >>>", static_cast<int>(duration_ms));
printf("\n<<< fps : %d >>>", static_cast<int>(fps));
- duration = first_sample_delivered_time_ - start_time_;
- micro_sec = duration.InMicroseconds();
- printf("\n<<< initial delay used(us): %d >>>", static_cast<int>(micro_sec));
+ printf("\n<<< initial delay used(us): %d >>>",
+ static_cast<int>(delay.InMicroseconds()));
// printf("\n<<< bitrate>>> : %I64d\n", bit_count_ * 1000000 / micro_sec);
printf("\n");
}
- // Not intended to be used in production.
- static void NV21toIYUV(uint8* nv21, uint8* i420, int width, int height) {
- memcpy(i420, nv21, width * height * sizeof(uint8));
- i420 += width * height;
- nv21 += width * height;
- uint8* u = i420;
- uint8* v = i420 + width * height / 4;
-
- for (int i = 0; i < width * height / 4; ++i) {
- *v++ = *nv21++;
- *u++ = *nv21++;
- }
- }
-
- static void NV21toYV12(uint8* nv21, uint8* yv12, int width, int height) {
- memcpy(yv12, nv21, width * height * sizeof(uint8));
- yv12 += width * height;
- nv21 += width * height;
- uint8* v = yv12;
- uint8* u = yv12 + width * height / 4;
-
- for (int i = 0; i < width * height / 4; ++i) {
- *v++ = *nv21++;
- *u++ = *nv21++;
- }
- }
-
- static void IYUVtoNV21(uint8* i420, uint8* nv21, int width, int height) {
- memcpy(nv21, i420, width * height * sizeof(uint8));
- i420 += width * height;
- nv21 += width * height;
- uint8* u = i420;
- uint8* v = i420 + width * height / 4;
-
- for (int i = 0; i < width * height / 4; ++i) {
- *nv21++ = *v++;
- *nv21++ = *u++;
- }
- }
-
- static void YV12toNV21(uint8* yv12, uint8* nv21, int width, int height) {
- memcpy(nv21, yv12, width * height * sizeof(uint8));
- yv12 += width * height;
- nv21 += width * height;
- uint8* v = yv12;
- uint8* u = yv12 + width * height / 4;
-
- for (int i = 0; i < width * height / 4; ++i) {
- *nv21++ = *v++;
- *nv21++ = *u++;
- }
- }
-
void DumpOutputFile(uint8* in_buffer, int size) {
// Assume chroma format 4:2:0.
int width = input_format_.video_header.width;
@@ -392,34 +240,30 @@ class TestApp {
DCHECK_GE(csc_buf_size_, size);
out_buffer = csc_buf_.get();
// Now assume the raw output is NV21.
- NV21toIYUV(in_buffer, out_buffer, width, height);
+ media::NV21toIYUV(in_buffer, out_buffer, width, height);
}
- fwrite(out_buffer, sizeof(uint8), size, output_file_);
+ fwrite(out_buffer, sizeof(uint8), size, output_file_.get());
}
scoped_refptr<media::OmxCodec> codec_;
MessageLoop message_loop_;
- const char* input_filename_;
- const char* output_filename_;
+ std::string output_filename_;
media::OmxCodec::OmxMediaFormat input_format_;
media::OmxCodec::OmxMediaFormat output_format_;
bool simulate_copy_;
- bool measure_fps_;
bool enable_csc_;
scoped_array<uint8> copy_buf_;
int copy_buf_size_;
scoped_array<uint8> csc_buf_;
int csc_buf_size_;
- FILE *input_file_, *output_file_;
- scoped_array<uint8> read_buf_;
+ ScopedStdioHandle output_file_;
bool stopped_;
bool error_;
base::TimeTicks start_time_;
- base::TimeTicks stop_time_;
base::TimeTicks first_sample_delivered_time_;
int frame_count_;
int bit_count_;
- int loop_count_;
+ scoped_ptr<media::FileReader> file_reader_;
};
int main(int argc, char** argv) {
@@ -433,31 +277,29 @@ int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: omx_test --input-file=FILE --codec=CODEC"
" [--output-file=FILE] [--enable-csc]"
- " [--copy] [--measure-fps]\n");
+ " [--copy] [--use-ffmpeg]\n");
printf(" CODEC: h264/mpeg4/h263/vc1\n");
printf("\n");
printf("Optional Arguments\n");
printf(" --output-file Dump raw OMX output to file.\n");
printf(" --enable-csc Dump the CSCed output to file.\n");
printf(" --copy Simulate a memcpy from the output.\n");
- printf(" --measure-fps Measuring performance in fps\n");
- printf(" --loop=COUNT loop input stream\n");
+ printf(" --use-ffmpeg Use ffmpeg demuxer\n");
return 1;
}
} else {
if (argc < 7) {
- printf("Usage: omx_test --input-file=FILE --codec=CODEC"
+ printf("Usage: omx_test --encoder --input-file=FILE --codec=CODEC"
" --width=PIXEL_WIDTH --height=PIXEL_HEIGHT"
" --bitrate=BIT_PER_SECOND --framerate=FRAME_PER_SECOND"
" [--output-file=FILE] [--enable-csc]"
- " [--copy] [--measure-fps]\n");
+ " [--copy]\n");
printf(" CODEC: h264/mpeg4/h263/vc1\n");
printf("\n");
printf("Optional Arguments\n");
printf(" --output-file Dump raw OMX output to file.\n");
printf(" --enable-csc Dump the CSCed input from file.\n");
printf(" --copy Simulate a memcpy from the output.\n");
- printf(" --measure-fps Measuring performance in fps\n");
printf(" --loop=COUNT loop input streams\n");
return 1;
}
@@ -467,13 +309,26 @@ int main(int argc, char** argv) {
std::string output_filename = cmd_line->GetSwitchValueASCII("output-file");
std::string codec = cmd_line->GetSwitchValueASCII("codec");
bool copy = cmd_line->HasSwitch("copy");
- bool measure_fps = cmd_line->HasSwitch("measure-fps");
bool enable_csc = cmd_line->HasSwitch("enable-csc");
+ bool use_ffmpeg = cmd_line->HasSwitch("use-ffmpeg");
int loop_count = 1;
if (cmd_line->HasSwitch("loop"))
loop_count = StringToInt(cmd_line->GetSwitchValueASCII("loop"));
DCHECK_GE(loop_count, 1);
+ // If FFmpeg should be used for demuxing load the library here and do
+ // the initialization.
+ if (use_ffmpeg) {
+ if (!media::InitializeMediaLibrary(FilePath())) {
+ LOG(ERROR) << "Unable to initialize the media library.";
+ return 1;
+ }
+
+ avcodec_init();
+ av_register_all();
+ av_register_protocol(&kFFmpegFileProtocol);
+ }
+
media::OmxCodec::OmxMediaFormat input, output;
memset(&input, 0, sizeof(input));
memset(&output, 0, sizeof(output));
@@ -496,16 +351,16 @@ int main(int argc, char** argv) {
output.video_header.p_dist = 0;
} else {
input.codec = media::OmxCodec::kCodecNone;
- if (codec == "h264")
+ if (codec == "h264") {
input.codec = media::OmxCodec::kCodecH264;
- else if (codec == "mpeg4")
+ } else if (codec == "mpeg4") {
input.codec = media::OmxCodec::kCodecMpeg4;
- else if (codec == "h263")
+ } else if (codec == "h263") {
input.codec = media::OmxCodec::kCodecH263;
- else if (codec == "vc1")
+ } else if (codec == "vc1") {
input.codec = media::OmxCodec::kCodecVc1;
- else {
- printf("Unknown codec.\n");
+ } else {
+ LOG(ERROR) << "Unknown codec.";
return 1;
}
output.codec = media::OmxCodec::kCodecRaw;
@@ -517,12 +372,16 @@ int main(int argc, char** argv) {
input,
output,
copy,
- measure_fps,
enable_csc,
+ use_ffmpeg,
loop_count);
// This call will run the decoder until EOS is reached or an error
// is encountered.
+ if (!test.Initialize()) {
+ LOG(ERROR) << "can't initialize this application";
+ return -1;
+ }
test.Run();
return 0;
}