| // Copyright (c) 2009 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. |
| |
| // 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. |
| |
| #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 "media/omx/input_buffer.h" |
| #include "media/omx/omx_codec.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 |
| // decoder. |
| class TestApp { |
| public: |
| TestApp(const char* filename, |
| const char* component, |
| media::OmxCodec::Codec codec, |
| bool simulate_copy) |
| : filename_(filename), |
| component_(component), |
| codec_(codec), |
| simulate_copy_(simulate_copy), |
| copy_buf_size_(0), |
| stopped_(false), |
| error_(false) { |
| } |
| |
| void StopCallback() { |
| // If this callback is received, mark the |stopped_| flag so that we don't |
| // feed more buffers into the decoder. |
| // We need to exit the current message loop because we have no more work |
| // to do on the message loop. This is done by calling |
| // message_loop_.Quit(). |
| stopped_ = true; |
| message_loop_.Quit(); |
| } |
| |
| 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"); |
| error_ = true; |
| message_loop_.Quit(); |
| } |
| |
| void FeedCallback(media::InputBuffer* buffer) { |
| // We receive this callback when the decoder has consumed an input buffer. |
| // In this case, delete the previous buffer and enqueue a new one. |
| // There are some conditions we don't want to enqueue, for example when |
| // the last buffer is an end-of-stream buffer, when we have stopped, and |
| // when we have received an error. |
| bool eos = buffer->IsEndOfStream(); |
| delete buffer; |
| if (!eos && !stopped_ && !error_) |
| FeedDecoder(); |
| } |
| |
| void ReadCompleteCallback(uint8* buffer, int size) { |
| // This callback is received when the decoder has completed a decoding |
| // task and given us some output data. The buffer is owned by the decoder. |
| if (stopped_ || error_) |
| return; |
| |
| // If we are readding to the end, then stop. |
| if (!size) { |
| decoder_->Stop(NewCallback(this, &TestApp::StopCallback)); |
| return; |
| } |
| |
| // Read one more from the decoder. |
| decoder_->Read(NewCallback(this, &TestApp::ReadCompleteCallback)); |
| |
| // Copy the output of the decoder to user memory. |
| if (simulate_copy_) { |
| if (size > copy_buf_size_) { |
| copy_buf_.reset(new uint8[size]); |
| copy_buf_size_ = size; |
| } |
| memcpy(copy_buf_.get(), buffer, size); |
| } |
| } |
| |
| void FeedDecoder() { |
| // This method feeds the decoder with 32KB of input data. |
| const int kSize = 32768; |
| uint8* data = new uint8[kSize]; |
| int read = fread(data, 1, kSize, file_); |
| decoder_->Feed(new media::InputBuffer(data, read), |
| NewCallback(this, &TestApp::FeedCallback)); |
| } |
| |
| void Run() { |
| // Open the input file. |
| file_ = file_util::OpenFile(filename_, "rb"); |
| if (!file_) { |
| printf("Error - can't open file %s\n", filename_); |
| return; |
| } |
| |
| // Setup the decoder with the message loop of the current thread. Also |
| // setup component name, codec and callbacks. |
| decoder_ = new media::OmxCodec(&message_loop_); |
| decoder_->Setup(component_, codec_); |
| decoder_->SetErrorCallback(NewCallback(this, &TestApp::ErrorCallback)); |
| |
| // Start the decoder. |
| decoder_->Start(); |
| for (int i = 0; i < 20; ++i) |
| FeedDecoder(); |
| decoder_->Read(NewCallback(this, &TestApp::ReadCompleteCallback)); |
| |
| // Execute the message loop so that we can run tasks on it. This call |
| // will return when we call message_loop_.Quit(). |
| message_loop_.Run(); |
| } |
| |
| scoped_refptr<media::OmxCodec> decoder_; |
| MessageLoop message_loop_; |
| const char* filename_; |
| const char* component_; |
| media::OmxCodec::Codec codec_; |
| bool simulate_copy_; |
| scoped_array<uint8> copy_buf_; |
| int copy_buf_size_; |
| FILE* file_; |
| bool stopped_; |
| bool error_; |
| }; |
| |
| int main(int argc, char** argv) { |
| base::AtExitManager at_exit_manager; |
| |
| CommandLine::Init(argc, argv); |
| const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| |
| if (argc < 2) { |
| printf("Usage: omx_test --file=FILE --component=COMPONENT --codec=CODEC" |
| " [--copy]\n"); |
| printf(" COMPONENT: OpenMAX component name\n"); |
| printf(" CODEC: h264/mpeg4/h263/vc1\n"); |
| printf("\n"); |
| printf("Optional Arguments\n"); |
| printf(" --copy Simulate a memcpy from the output of decoder.\n"); |
| return 1; |
| } |
| |
| std::string filename = cmd_line->GetSwitchValueASCII("file"); |
| std::string component = cmd_line->GetSwitchValueASCII("component"); |
| std::string codec = cmd_line->GetSwitchValueASCII("codec"); |
| bool copy = cmd_line->HasSwitch("copy"); |
| |
| media::OmxCodec::Codec codec_id = media::OmxCodec::kCodecNone; |
| if (codec == "h264") |
| codec_id = media::OmxCodec::kCodecH264; |
| else if (codec == "mpeg4") |
| codec_id = media::OmxCodec::kCodecMpeg4; |
| else if (codec == "h263") |
| codec_id = media::OmxCodec::kCodecH263; |
| else if (codec == "vc1") |
| codec_id = media::OmxCodec::kCodecVc1; |
| else { |
| printf("Unknown codec.\n"); |
| return 1; |
| } |
| |
| // Create a TestApp object and run the decoder. |
| TestApp test(filename.c_str(), component.c_str(), codec_id, copy); |
| |
| // This call will run the decoder until EOS is reached or an error |
| // is encountered. |
| test.Run(); |
| return 0; |
| } |