diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-30 00:57:51 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-30 00:57:51 +0000 |
commit | 599c04f92812016bb927fc1844a4d8fb532a32d4 (patch) | |
tree | a8322ac510a8eab9a4c256eb18e800cf0b57b967 /media | |
parent | 7106f7918a629ec51411ed5dc8d3a01372b3443d (diff) | |
download | chromium_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.gyp | 9 | ||||
-rw-r--r-- | media/tools/omx_test/color_space_util.cc | 61 | ||||
-rw-r--r-- | media/tools/omx_test/color_space_util.h | 24 | ||||
-rw-r--r-- | media/tools/omx_test/file_reader_util.cc | 268 | ||||
-rw-r--r-- | media/tools/omx_test/file_reader_util.h | 119 | ||||
-rw-r--r-- | media/tools/omx_test/omx_test.cc | 341 |
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; } |