diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 20:25:04 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-15 20:25:04 +0000 |
commit | 83dc95e047700330a6fd369a3e7c051b734947f1 (patch) | |
tree | 02e7c433b5b80e2d8439ec660f328fcad8c48d2e /media/omx | |
parent | c4514c3c5719c148aacfafb85edabc6cd51ee501 (diff) | |
download | chromium_src-83dc95e047700330a6fd369a3e7c051b734947f1.zip chromium_src-83dc95e047700330a6fd369a3e7c051b734947f1.tar.gz chromium_src-83dc95e047700330a6fd369a3e7c051b734947f1.tar.bz2 |
enable simple encoding in omx_codec
1. add Format callback to notify media format change.
2. add timestamp to encoder input sample.
3. add loop count to input streams.
4. add enable-csc in encoder path.
5. measure initial delay
6. move color space conversion inside TestApp.(according to Alpha).
7. copy component name in OmxCodec ( Andrew ).
BUG=31804
Submitted for: jiesun@chromium.org
Reviewed: http://codereview.chromium.org/519084/show
Review URL: http://codereview.chromium.org/552016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36400 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/omx')
-rw-r--r-- | media/omx/omx_codec.cc | 356 | ||||
-rw-r--r-- | media/omx/omx_codec.h | 37 | ||||
-rw-r--r-- | media/omx/omx_test.cc | 438 |
3 files changed, 570 insertions, 261 deletions
diff --git a/media/omx/omx_codec.cc b/media/omx/omx_codec.cc index 6a33a87..55d02f5 100644 --- a/media/omx/omx_codec.cc +++ b/media/omx/omx_codec.cc @@ -2,6 +2,8 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. +#include <string> + #include "base/logging.h" #include "base/message_loop.h" #include "base/stl_util-inl.h" @@ -28,7 +30,6 @@ OmxCodec::OmxCodec(MessageLoop* message_loop) output_eos_(false), state_(kEmpty), next_state_(kEmpty), - component_name_(NULL), component_handle_(NULL), message_loop_(message_loop) { } @@ -44,7 +45,7 @@ OmxCodec::~OmxCodec() { } void OmxCodec::Setup( - const char* component_name, + const std::string& component_name, const OmxCodec::OmxMediaFormat& input_format, const OmxCodec::OmxMediaFormat& output_format) { DCHECK_EQ(kEmpty, state_); @@ -52,7 +53,6 @@ void OmxCodec::Setup( component_name_ = component_name; input_format_ = input_format; output_format_ = output_format; - encoder_ = input_format_.codec == kCodecRaw; } void OmxCodec::SetErrorCallback(Callback* callback) { @@ -60,6 +60,11 @@ void OmxCodec::SetErrorCallback(Callback* callback) { error_callback_.reset(callback); } +void OmxCodec::SetFormatCallback(FormatCallback* callback) { + DCHECK_EQ(kEmpty, state_); + format_callback_.reset(callback); +} + void OmxCodec::Start() { DCHECK_NE(kCodecNone, input_format_.codec); @@ -110,6 +115,8 @@ void OmxCodec::SetNextState(State state) { void OmxCodec::StartTask() { DCHECK_EQ(message_loop_, MessageLoop::current()); + next_sample_timestamp_ = 0; + StateTransitionTask(kLoaded); } @@ -286,7 +293,7 @@ void OmxCodec::Transition_EmptyToLoaded() { // TODO(hclam): move this out. OMX_ERRORTYPE omxresult = OMX_Init(); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - Failed to Init OpenMAX core"; + LOG(ERROR) << "Failed to Init OpenMAX core"; StateTransitionTask(kError); return; } @@ -294,7 +301,7 @@ void OmxCodec::Transition_EmptyToLoaded() { // 2. Get the handle to the component. After OMX_GetHandle(), // the component is in loaded state. // TODO(hclam): We should have a list of componant names instead. - OMX_STRING component = const_cast<OMX_STRING>(component_name_); + OMX_STRING component = const_cast<OMX_STRING>(component_name_.c_str()); OMX_HANDLETYPE handle = reinterpret_cast<OMX_HANDLETYPE>(component_handle_); omxresult = OMX_GetHandle(&handle, component, this, &callback); component_handle_ = reinterpret_cast<OMX_COMPONENTTYPE*>(handle); @@ -311,7 +318,7 @@ void OmxCodec::Transition_EmptyToLoaded() { omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamVideoInit, &port_param); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "ERROR - Failed to get Port Param"; + LOG(ERROR) << "Failed to get Port Param"; StateTransitionTask(kError); return; } @@ -320,111 +327,59 @@ void OmxCodec::Transition_EmptyToLoaded() { // 4. Device specific configurations. if (!DeviceSpecificConfig()) { - LOG(ERROR) << "Error - device specific configurations failed"; + LOG(ERROR) << "Device specific configurations failed"; StateTransitionTask(kError); return; } - // 5. Configure the input port. - // Query the decoder input port's minimum buffer requirements. - // Note that port_param.nStartPortNumber defines the index of the - // input port. - OMX_PARAM_PORTDEFINITIONTYPE port_format; - ResetPortHeader(*this, &port_format); - port_format.nPortIndex = input_port_; - omxresult = OMX_GetParameter(component_handle_, - OMX_IndexParamPortDefinition, - &port_format); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - GetParameter failed"; - StateTransitionTask(kError); - return; - } - if(OMX_DirInput != port_format.eDir) { - LOG(ERROR) << "Error - Expect Input Port"; - StateTransitionTask(kError); - return; - } - if (input_format_.codec == kCodecH264) - port_format.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; - else if (input_format_.codec == kCodecMpeg4) - port_format.format.video.eCompressionFormat = OMX_VIDEO_CodingMPEG4; - else if (input_format_.codec == kCodecH263) - port_format.format.video.eCompressionFormat = OMX_VIDEO_CodingH263; - else if (input_format_.codec == kCodecVc1) - port_format.format.video.eCompressionFormat = OMX_VIDEO_CodingWMV; - else - LOG(ERROR) << "Error: Unsupported codec " << input_format_.codec; - // Assume QCIF. - // TODO(ajwong): This MUST come from the client library somehow. - port_format.format.video.nFrameWidth = 720; - port_format.format.video.nFrameHeight = 480; - omxresult = OMX_SetParameter(component_handle_, - OMX_IndexParamPortDefinition, - &port_format); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SetParameter failed"; + // 5. Input/output ports media format configuration. + if (!ConfigureIOPorts()) { + LOG(ERROR) << "Media format configurations failed"; StateTransitionTask(kError); return; } - // 6. Configure the output port. - // There's nothing to be done here. - - // 7. Obtain the information about the input port. - // This will have the new mini buffer count in port_format.nBufferCountMin. + // 6. Obtain the information about the input port. + // This will have the new mini buffer count in |port_format.nBufferCountMin|. // Save this value to input_buf_count. - port_format.nPortIndex = port_param.nStartPortNumber; + OMX_PARAM_PORTDEFINITIONTYPE port_format; + ResetPortHeader(*this, &port_format); + port_format.nPortIndex = input_port_; omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - GetParameter failed"; + LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; StateTransitionTask(kError); return; } if (OMX_DirInput != port_format.eDir) { - LOG(ERROR) << "Error - Expect input port"; + LOG(ERROR) << "Expected input port"; StateTransitionTask(kError); return; } input_buffer_count_ = port_format.nBufferCountMin; input_buffer_size_ = port_format.nBufferSize; - // 8. Obtain the information about the output port. + // 7. Obtain the information about the output port. ResetPortHeader(*this, &port_format); port_format.nPortIndex = output_port_; omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - GetParameter failed"; + LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; StateTransitionTask(kError); return; } if (OMX_DirOutput != port_format.eDir) { - LOG(ERROR) << "Error - Expect Output Port"; + LOG(ERROR) << "Expect Output Port"; StateTransitionTask(kError); return; } output_buffer_count_ = port_format.nBufferCountMin; output_buffer_size_ = port_format.nBufferSize; - // 9. Codec specific configurations. - // This sets the NAL length size. 0 means we are using a 3 byte start code. - // Other values specifies number of bytes of the NAL length. - if (input_format_.codec == kCodecH264) { - OMX_VIDEO_CONFIG_NALSIZE naluSize; - naluSize.nNaluBytes = 0; - omxresult = OMX_SetConfig(component_handle_, - OMX_IndexConfigVideoNalSize, (OMX_PTR)&naluSize); - if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SetConfig failed"; - StateTransitionTask(kError); - return; - } - } - // After we have done all the configurations, we are considered loaded. DoneStateTransitionTask(); } @@ -443,21 +398,21 @@ void OmxCodec::Transition_LoadedToIdle() { OMX_CommandStateSet, OMX_StateIdle, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } // 2. Allocate buffer for the input port. if (!AllocateInputBuffers()) { - LOG(ERROR) << "Error - OMX_AllocateBuffer Input buffer error"; + LOG(ERROR) << "OMX_AllocateBuffer() Input buffer error"; StateTransitionTask(kError); return; } // 3. Allocate buffer for the output port. if (!AllocateOutputBuffers()) { - LOG(ERROR) << "Error - OMX_AllocateBuffer Output buffer error"; + LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; StateTransitionTask(kError); return; } @@ -475,7 +430,7 @@ void OmxCodec::Transition_IdleToExecuting() { OMX_CommandStateSet, OMX_StateExecuting, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } @@ -494,7 +449,7 @@ void OmxCodec::Transition_ExecutingToDisable() { OMX_CommandPortDisable, output_port_, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandPortDisable) failed"; StateTransitionTask(kError); return; } @@ -517,7 +472,7 @@ void OmxCodec::Transition_DisableToEnable() { OMX_CommandPortEnable, output_port_, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandPortEnable) failed"; StateTransitionTask(kError); return; } @@ -529,21 +484,32 @@ void OmxCodec::Transition_DisableToEnable() { omxresult = OMX_GetParameter(component_handle_, OMX_IndexParamPortDefinition, &port_format); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - GetParameter failed"; + LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) failed"; StateTransitionTask(kError); return; } if (OMX_DirOutput != port_format.eDir) { - LOG(ERROR) << "Error - Expect Output Port"; + LOG(ERROR) << "Expected Output Port"; StateTransitionTask(kError); return; } + // Update the output format. + // TODO(jiesun): check if the format really change. ( we had omit some + // information such as frame rate / bit rate / vbv buffer info now. ) + output_format_.video_header.height = port_format.format.video.nFrameHeight; + output_format_.video_header.width = port_format.format.video.nFrameWidth; + output_format_.video_header.stride = port_format.format.video.nStride; + input_format_.video_header.height = output_format_.video_header.height; + input_format_.video_header.width = output_format_.video_header.width; + input_format_.video_header.stride = output_format_.video_header.stride; + ReportFormatChange(); + // Update the ports in buffer. output_buffer_count_ = port_format.nBufferCountActual; output_buffer_size_ = port_format.nBufferSize; if (!AllocateOutputBuffers()) { - LOG(ERROR) << "Error - OMX_AllocateBuffer Output buffer error"; + LOG(ERROR) << "OMX_AllocateBuffer() Output buffer error"; StateTransitionTask(kError); return; } @@ -560,7 +526,7 @@ void OmxCodec::Transition_DisableToIdle() { OMX_CommandStateSet, OMX_StateIdle, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } @@ -585,7 +551,7 @@ void OmxCodec::Transition_EnableToIdle() { OMX_CommandStateSet, OMX_StateIdle, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } @@ -602,7 +568,7 @@ void OmxCodec::Transition_ExecutingToIdle() { OMX_CommandStateSet, OMX_StateIdle, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } @@ -621,7 +587,7 @@ void OmxCodec::Transition_IdleToLoaded() { OMX_CommandStateSet, OMX_StateLoaded, 0); if (omxresult != OMX_ErrorNone) { - LOG(ERROR) << "Error - SendCommand failed"; + LOG(ERROR) << "SendCommand(OMX_CommandStateSet) failed"; StateTransitionTask(kError); return; } @@ -641,7 +607,7 @@ void OmxCodec::Transition_LoadedToEmpty() { // Free the decoder handle. OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); if (result != OMX_ErrorNone) { - LOG(ERROR) << "Error - Terminate: OMX_FreeHandle error. " + LOG(ERROR) << "Terminate: OMX_FreeHandle() error. " "Error code: " << result; } component_handle_ = NULL; @@ -689,7 +655,7 @@ void OmxCodec::Transition_Error() { if (component_handle_) { OMX_ERRORTYPE result = OMX_FreeHandle(component_handle_); if (result != OMX_ErrorNone) - LOG(ERROR) << "Error - OMX_FreeHandle error. Error code: " << result; + LOG(ERROR) << "OMX_FreeHandle() error. Error code: " << result; component_handle_ = NULL; } @@ -852,6 +818,203 @@ void OmxCodec::ReportError() { error_callback_.reset(); } +void OmxCodec::ReportFormatChange() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + if (!format_callback_.get()) + return; + format_callback_->Run(&input_format_, &output_format_); +} + +bool OmxCodec::ConfigureIOPorts() { + OMX_PARAM_PORTDEFINITIONTYPE input_port_def, output_port_def; + OMX_ERRORTYPE omxresult = OMX_ErrorNone; + // Get default input port definition. + ResetPortHeader(*this, &input_port_def); + input_port_def.nPortIndex = input_port_; + omxresult = OMX_GetParameter(component_handle_, + OMX_IndexParamPortDefinition, + &input_port_def); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " + "for input port failed"; + return false; + } + if (OMX_DirInput != input_port_def.eDir) { + LOG(ERROR) << "Expected Input Port"; + return false; + } + + // Get default output port definition. + ResetPortHeader(*this, &output_port_def); + output_port_def.nPortIndex = output_port_; + omxresult = OMX_GetParameter(component_handle_, + OMX_IndexParamPortDefinition, + &output_port_def); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "GetParameter(OMX_IndexParamPortDefinition) " + "for output port failed"; + return false; + } + if (OMX_DirOutput != output_port_def.eDir) { + LOG(ERROR) << "Expected Output Port"; + return false; + } + + return encoder() ? + ConfigureAsEncoder(&input_port_def, &output_port_def) : + ConfigureAsDecoder(&input_port_def, &output_port_def); +} + +bool OmxCodec::ConfigureAsEncoder( + OMX_PARAM_PORTDEFINITIONTYPE* input_port_def, + OMX_PARAM_PORTDEFINITIONTYPE* output_port_def) { + // TODO(jiesun): Add support for other format than MPEG4. + DCHECK_EQ(kCodecMpeg4, output_format_.codec); + // Configure the input port. + input_port_def->format.video.nFrameWidth = + input_format_.video_header.width; + input_port_def->format.video.nFrameHeight = + input_format_.video_header.height; + OMX_ERRORTYPE omxresult = OMX_ErrorNone; + omxresult = OMX_SetParameter(component_handle_, + OMX_IndexParamPortDefinition, + input_port_def); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) " + "for input port failed"; + return false; + } + + // Configure the output port + output_port_def->format.video.nFrameWidth = + input_format_.video_header.width; + output_port_def->format.video.nFrameHeight = + input_format_.video_header.height; + omxresult = OMX_SetParameter(component_handle_, + OMX_IndexParamPortDefinition, + output_port_def); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) " + "for output port failed"; + return false; + } + + if (output_format_.codec == kCodecMpeg4) { + OMX_VIDEO_PARAM_MPEG4TYPE mp4_type; + omxresult = OMX_GetParameter(component_handle_, + OMX_IndexParamVideoMpeg4, + &mp4_type); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "GetParameter(OMX_IndexParamVideoMpeg4) failed"; + return false; + } + // TODO(jiesun): verify if other vendors had the same definition. + // Specify the frame rate. + mp4_type.nTimeIncRes = output_format_.video_header.frame_rate * 2; + // Specify how many P frames between adjacent intra frames. + mp4_type.nPFrames = output_format_.video_header.i_dist - 1; + omxresult = OMX_SetParameter(component_handle_, + OMX_IndexParamVideoMpeg4, + &mp4_type); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexParamVideoMpeg4) failed"; + return false; + } + } + + OMX_VIDEO_PARAM_BITRATETYPE bitrate; + omxresult = OMX_GetParameter(component_handle_, + OMX_IndexParamVideoBitrate, + &bitrate); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "GetParameter(OMX_IndexParamVideoBitrate) failed"; + return false; + } + // TODO(jiesun): expose other rate control method that matters. + bitrate.eControlRate = OMX_Video_ControlRateConstant; + bitrate.nTargetBitrate = output_format_.video_header.bit_rate; + omxresult = OMX_SetParameter(component_handle_, + OMX_IndexParamVideoBitrate, + &bitrate); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexParamVideoBitrate) failed"; + return false; + } + + OMX_CONFIG_FRAMERATETYPE framerate; + omxresult = OMX_GetConfig(component_handle_, + OMX_IndexConfigVideoFramerate, + &framerate); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "GetParameter(OMX_IndexConfigVideoFramerate) failed"; + return false; + } + + framerate.xEncodeFramerate = + output_format_.video_header.frame_rate << 16; // Q16 format. + omxresult = OMX_SetConfig(component_handle_, + OMX_IndexConfigVideoFramerate, + &framerate); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexConfigVideoFramerate) failed"; + return false; + } + + // Emulate a format change to conform with decoder case. + ReportFormatChange(); + return true; +} + +bool OmxCodec::ConfigureAsDecoder( + OMX_PARAM_PORTDEFINITIONTYPE* input_port_def, + OMX_PARAM_PORTDEFINITIONTYPE* output_port_def) { + // Configure the input port. + if (input_format_.codec == kCodecNone) { + LOG(ERROR) << "Unsupported codec " << input_format_.codec; + return false; + } + if (input_format_.codec == kCodecH264) + input_port_def->format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; + else if (input_format_.codec == kCodecMpeg4) + input_port_def->format.video.eCompressionFormat = OMX_VIDEO_CodingMPEG4; + else if (input_format_.codec == kCodecH263) + input_port_def->format.video.eCompressionFormat = OMX_VIDEO_CodingH263; + else if (input_format_.codec == kCodecVc1) + input_port_def->format.video.eCompressionFormat = OMX_VIDEO_CodingWMV; + // Assume QCIF. + // TODO(ajwong): This MUST come from the client library somehow. + input_port_def->format.video.nFrameWidth = 720; + input_port_def->format.video.nFrameHeight = 480; + OMX_ERRORTYPE omxresult = OMX_ErrorNone; + omxresult = OMX_SetParameter(component_handle_, + OMX_IndexParamPortDefinition, + input_port_def); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetParameter(OMX_IndexParamPortDefinition) " + "for input port failed"; + return false; + } + + // Codec specific configurations. + // This sets the NAL length size. 0 means we are using a 3 byte start code. + // Other values specifies number of bytes of the NAL length. + if (input_format_.codec == kCodecH264) { + OMX_VIDEO_CONFIG_NALSIZE naluSize; + ResetPortHeader(*this, &naluSize); + naluSize.nPortIndex = input_port_; + naluSize.nNaluBytes = 0; + omxresult = OMX_SetConfig(component_handle_, + OMX_IndexConfigVideoNalSize, + (OMX_PTR)&naluSize); + if (omxresult != OMX_ErrorNone) { + LOG(ERROR) << "SetConfig(OMX_IndexConfigVideoNalSize) failed"; + return false; + } + } + return true; +} + bool OmxCodec::CanEmptyBuffer() { // We can call empty buffer while we are in executing or enabling / disabling // the output port. @@ -922,11 +1085,16 @@ void OmxCodec::EmptyBufferTask() { omx_buffer->nFilledLen = filled; omx_buffer->pAppPrivate = this; omx_buffer->nFlags |= input_eos_ ? OMX_BUFFERFLAG_EOS : 0; + if (encoder()) { + next_sample_timestamp_ += base::Time::kMicrosecondsPerSecond / + output_format_.video_header.frame_rate; + omx_buffer->nTimeStamp = next_sample_timestamp_; + } // Give this buffer to OMX. OMX_ERRORTYPE ret = OMX_EmptyThisBuffer(component_handle_, omx_buffer); if (ret != OMX_ErrorNone) { - LOG(ERROR) << "ERROR - OMX_EmptyThisBuffer failed with result " << ret; + LOG(ERROR) << "OMX_EmptyThisBuffer() failed with result " << ret; StateTransitionTask(kError); return; } @@ -976,7 +1144,7 @@ void OmxCodec::FillBufferTask() { omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); if (OMX_ErrorNone != ret) { - LOG(ERROR) << "Error - OMX_FillThisBuffer failed with result " << ret; + LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; StateTransitionTask(kError); return; } @@ -1015,7 +1183,7 @@ void OmxCodec::InitialFillBuffer() { OMX_ERRORTYPE ret = OMX_FillThisBuffer(component_handle_, omx_buffer); if (OMX_ErrorNone != ret) { - LOG(ERROR) << "Error - OMX_FillThisBuffer failed with result " << ret; + LOG(ERROR) << "OMX_FillThisBuffer() failed with result " << ret; StateTransitionTask(kError); return; } @@ -1023,10 +1191,10 @@ void OmxCodec::InitialFillBuffer() { } void OmxCodec::EventHandlerInternal(OMX_HANDLETYPE component, - OMX_EVENTTYPE event, - OMX_U32 data1, - OMX_U32 data2, - OMX_PTR event_data) { + OMX_EVENTTYPE event, + OMX_U32 data1, + OMX_U32 data2, + OMX_PTR event_data) { switch(event) { case OMX_EventCmdComplete: { // If the last command was successful, we have completed diff --git a/media/omx/omx_codec.h b/media/omx/omx_codec.h index 4aa69af..c198766 100644 --- a/media/omx/omx_codec.h +++ b/media/omx/omx_codec.h @@ -20,6 +20,7 @@ // output_format.codec = OmxCodec::kCodecRaw; // decoder->Setup(component_name, input_format, output_format); // decoder->SetErrorCallback(NewCallback(this, &Client::ErrorCallback)); +// decoder->SetFormatCallback(NewCallback(this, &Client::FormatCallback)); // // // Start is asynchronous. But we don't need to wait for it to proceed. // decoder->Start(); @@ -91,6 +92,7 @@ #include "base/task.h" #include "third_party/openmax/il/OMX_Component.h" #include "third_party/openmax/il/OMX_Core.h" +#include "third_party/openmax/il/OMX_Video.h" class InputBuffer; class MessageLoop; @@ -99,6 +101,9 @@ namespace media { class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { public: + struct OmxMediaFormat; // forward declaration. + // TODO(jiesun): remove callback parameters. + typedef Callback2<OmxMediaFormat*, OmxMediaFormat*>::Type FormatCallback; typedef Callback1<InputBuffer*>::Type FeedCallback; typedef Callback2<uint8*, int>::Type ReadCallback; typedef Callback0::Type Callback; @@ -132,12 +137,10 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { }; struct OmxMediaFormatVideoRaw { - OmxMediaFormatVideoHeader h; OmxSurfaceFormat color_space; }; struct OmxMediaFormatVideoH264 { - OmxMediaFormatVideoHeader h; int slice_enable; int max_ref_frames; int num_ref_l0, num_ref_l1; @@ -150,7 +153,6 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { }; struct OmxMediaFormatVideoMPEG4 { - OmxMediaFormatVideoHeader h; int ac_pred_enable; int time_inc_res; int slice_enable; @@ -159,6 +161,7 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { struct OmxMediaFormat { // TODO(jiesun): instead of codec type, we should have media format. Codec codec; + OmxMediaFormatVideoHeader video_header; union { OmxMediaFormatVideoRaw raw; OmxMediaFormatVideoH264 h264; @@ -166,18 +169,21 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { }; }; - OmxCodec(MessageLoop* message_loop); + explicit OmxCodec(MessageLoop* message_loop); virtual ~OmxCodec(); // Set the component name and input/output media format. // TODO(hclam): Remove |component|. - void Setup(const char* component_name, + void Setup(const std::string& component_name, const OmxMediaFormat& input_format, const OmxMediaFormat& output_format); // Set the error callback. In case of error the callback will be called. void SetErrorCallback(Callback* callback); + // Set the format change callback. In case of input stream changes. + void SetFormatCallback(FormatCallback* callback); + // Start the decoder, this will start the initialization asynchronously. // Client can start feeding to and reading from the decoder. void Start(); @@ -201,13 +207,16 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { OMX_COMPONENTTYPE* component_handle() { return component_handle_; } int input_port() { return input_port_; } int output_port() { return output_port_; } + bool encoder() const { return input_format_.codec == kCodecRaw; } // Subclass can provide a different value. virtual int current_omx_spec_version() const { return 0x00000101; } protected: // Returns the component name given the codec. - virtual const char* GetComponentName(Codec codec) { return component_name_; } + virtual const char* GetComponentName(Codec codec) { + return component_name_.c_str(); + } // Inherit from subclass to allow device specific configurations. virtual bool DeviceSpecificConfig() { return true; } @@ -243,6 +252,18 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { // state is done. void ReportError(); + // Helper method to call |start_callback_| after a foramt change. + // used when decoder output port had done with port reconfigure and + // return to enabled state. + void ReportFormatChange(); + + // Helper method to configure port format at LOADED state. + bool ConfigureIOPorts(); + bool ConfigureAsDecoder(OMX_PARAM_PORTDEFINITIONTYPE* input_port_def, + OMX_PARAM_PORTDEFINITIONTYPE* output_port_def); + bool ConfigureAsEncoder(OMX_PARAM_PORTDEFINITIONTYPE* input_port_def, + OMX_PARAM_PORTDEFINITIONTYPE* output_port_def); + // Methods and free input and output buffers. bool AllocateInputBuffers(); bool AllocateOutputBuffers(); @@ -354,13 +375,15 @@ class OmxCodec : public base::RefCountedThreadSafe<OmxCodec> { State next_state_; // TODO(hclam): We should keep a list of component names. - const char* component_name_; + std::string component_name_; OMX_COMPONENTTYPE* component_handle_; bool encoder_; + int64 next_sample_timestamp_; OmxMediaFormat input_format_; OmxMediaFormat output_format_; MessageLoop* message_loop_; + scoped_ptr<FormatCallback> format_callback_; scoped_ptr<Callback> stop_callback_; scoped_ptr<Callback> error_callback_; diff --git a/media/omx/omx_test.cc b/media/omx/omx_test.cc index 08d91f0..caab772 100644 --- a/media/omx/omx_test.cc +++ b/media/omx/omx_test.cc @@ -26,21 +26,28 @@ class TestApp { public: TestApp(const char* input_filename, const char* output_filename, - const char* component, + const std::string& component_name, media::OmxCodec::OmxMediaFormat& input_format, media::OmxCodec::OmxMediaFormat& output_format, - bool simulate_copy) + bool simulate_copy, + bool measure_fps, + bool enable_csc, + int loop_count) : input_filename_(input_filename), output_filename_(output_filename), - component_(component), + component_name_(component_name), 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) { + error_(false), + loop_count_(loop_count) { } void StopCallback() { @@ -61,6 +68,28 @@ class TestApp { message_loop_.Quit(); } + void FormatCallback( + media::OmxCodec::OmxMediaFormat* input_format, + media::OmxCodec::OmxMediaFormat* output_format) { + // This callback will be called when port reconfiguration is done. + // Input format and output format will be used in the codec. + + // Make a copy of the changed format. + input_format_ = *input_format; + output_format_ = *output_format; + + DCHECK_EQ(input_format->video_header.width, + output_format->video_header.width); + DCHECK_EQ(input_format->video_header.height, + output_format->video_header.height); + int size = input_format_.video_header.width * + input_format_.video_header.height * 3 / 2; + if (enable_csc_ && size > csc_buf_size_) { + csc_buf_.reset(new uint8[size]); + csc_buf_size_ = size; + } + } + 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. @@ -70,7 +99,7 @@ class TestApp { bool eos = buffer->IsEndOfStream(); delete buffer; if (!eos && !stopped_ && !error_) - FeedDecoder(); + FeedInputBuffer(); } void ReadCompleteCallback(uint8* buffer, int size) { @@ -79,14 +108,17 @@ class TestApp { if (stopped_ || error_) return; + if (measure_fps_ && !frame_count_) + first_sample_delivered_time_ = base::TimeTicks::HighResNow(); + // If we are readding to the end, then stop. if (!size) { - decoder_->Stop(NewCallback(this, &TestApp::StopCallback)); + codec_->Stop(NewCallback(this, &TestApp::StopCallback)); return; } // Read one more from the decoder. - decoder_->Read(NewCallback(this, &TestApp::ReadCompleteCallback)); + 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. @@ -96,7 +128,7 @@ class TestApp { } memcpy(copy_buf_.get(), buffer, size); if (output_file_) - fwrite(copy_buf_.get(), sizeof(uint8), size, output_file_); + DumpOutputFile(copy_buf_.get(), size); } // could OMX IL return patial sample for decoder? @@ -104,13 +136,54 @@ class TestApp { bit_count_ += size << 3; } - 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, input_file_); - decoder_->Feed(new media::InputBuffer(data, read), - NewCallback(this, &TestApp::FeedCallback)); + void FeedInputBuffer() { + bool encoder = input_format_.codec == media::OmxCodec::kCodecRaw; + while (true) { + uint8* data = NULL; + int read = 0; + if (!encoder) { + // This method feeds the decoder with 32KB of input data. + const int kSize = 32768; + data = new uint8[kSize]; + read = fread(data, 1, kSize, input_file_); + } else { + // 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 size = width * height * 3 / 2; + data = new uint8[size]; + if (enable_csc_) { + CHECK(csc_buf_size_ >= size); + read = fread(csc_buf_.get(), 1, size, input_file_); + // We do not convert partial frames. + if (read == size) + IYUVtoNV21(csc_buf_.get(), data, width, height); + else + read = 0; // force cleanup or loop around. + } else { + read = fread(data, 1, size, input_file_); + } + } + + if (read) { + codec_->Feed(new media::InputBuffer(data, read), + NewCallback(this, &TestApp::FeedCallback)); + break; + } else { + // Encounter the end of file. + if (loop_count_ == 1) { + // Signal end of stream. + codec_->Feed(new media::InputBuffer(data, 0), + NewCallback(this, &TestApp::FeedCallback)); + break; + } else { + --loop_count_; + delete [] data; + fseek(input_file_, 0, SEEK_SET); + } + } + } } void Run() { @@ -131,214 +204,259 @@ class TestApp { } } - // 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_, input_format_, output_format_); - decoder_->SetErrorCallback(NewCallback(this, &TestApp::ErrorCallback)); + if (measure_fps_) + StartProfiler(); - // Start the decoder. - decoder_->Start(); + // Setup the |codec_| with the message loop of the current thread. Also + // setup component name, codec format and callbacks. + codec_ = new media::OmxCodec(&message_loop_); + codec_->Setup(component_name_, input_format_, output_format_); + codec_->SetErrorCallback(NewCallback(this, &TestApp::ErrorCallback)); + codec_->SetFormatCallback(NewCallback(this, &TestApp::FormatCallback)); + + // Start the |codec_|. + codec_->Start(); for (int i = 0; i < 20; ++i) - FeedDecoder(); - decoder_->Read(NewCallback(this, &TestApp::ReadCompleteCallback)); + FeedInputBuffer(); + codec_->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(); + if (measure_fps_) + StopProfiler(); + fclose(input_file_); if (output_file_) fclose(output_file_); } void StartProfiler() { - start_time_ = base::Time::Now(); + start_time_ = base::TimeTicks::HighResNow(); frame_count_ = 0; bit_count_ = 0; } void StopProfiler() { - base::Time stop_time = base::Time::Now(); - base::TimeDelta duration = stop_time - start_time_; - int micro_sec = static_cast<int>(duration.InMicroseconds()); printf("\n<<< frame delivered : %d >>>", frame_count_); - printf("\n<<< time used(us) : %d >>>", micro_sec); - printf("\n<<< fps : %d >>>", frame_count_ * 1000000 / micro_sec); + 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<<< 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<<< bitrate>>> : %I64d\n", bit_count_ * 1000000 / micro_sec); printf("\n"); } - scoped_refptr<media::OmxCodec> decoder_; + // 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; + int height = input_format_.video_header.height; + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + + uint8* out_buffer = in_buffer; + // Color space conversion. + bool encoder = input_format_.codec == media::OmxCodec::kCodecRaw; + if (enable_csc_ && !encoder) { + DCHECK_EQ(size, width * height * 3 / 2); + 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); + } + fwrite(out_buffer, sizeof(uint8), size, output_file_); + } + + scoped_refptr<media::OmxCodec> codec_; MessageLoop message_loop_; const char* input_filename_; const char* output_filename_; - const char* component_; + std::string component_name_; 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_; bool stopped_; bool error_; - base::Time start_time_; + base::TimeTicks start_time_; + base::TimeTicks stop_time_; + base::TimeTicks first_sample_delivered_time_; int frame_count_; int bit_count_; + int loop_count_; }; -// Not intended to be used in production. -void NV21toI420(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++; - } -} - -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++; - } -} - -void I420toNV21(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++; - } -} - -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++; - } -} - 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 --input-file=FILE" - " --component=COMPONENT --codec=CODEC" - " [--output-file=FILE] [--enable-csc=FILE]" - " [--copy] [--measure-fps]\n"); - printf(" COMPONENT: OpenMAX component name\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 of decoder.\n"); - printf(" --measure-fps Measuring performance in fps\n"); - return 1; + bool encoder = cmd_line->HasSwitch("encoder"); + if (!encoder) { + if (argc < 3) { + printf("Usage: omx_test --input-file=FILE" + " --component=COMPONENT --codec=CODEC" + " [--output-file=FILE] [--enable-csc]" + " [--copy] [--measure-fps]\n"); + printf(" COMPONENT: OpenMAX component name\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"); + return 1; + } + } else { + if (argc < 7) { + printf("Usage: omx_test --input-file=FILE" + " --component=COMPONENT --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"); + printf(" COMPONENT: OpenMAX component name\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; + } } std::string input_filename = cmd_line->GetSwitchValueASCII("input-file"); std::string output_filename = cmd_line->GetSwitchValueASCII("output-file"); - std::string component = cmd_line->GetSwitchValueASCII("component"); + std::string component_name = cmd_line->GetSwitchValueASCII("component"); 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"); + int loop_count = 1; + if (cmd_line->HasSwitch("loop")) + loop_count = StringToInt(cmd_line->GetSwitchValueASCII("loop")); + DCHECK_GE(loop_count, 1); media::OmxCodec::OmxMediaFormat input, output; - input.codec = media::OmxCodec::kCodecNone; - if (codec == "h264") - input.codec = media::OmxCodec::kCodecH264; - else if (codec == "mpeg4") - input.codec = media::OmxCodec::kCodecMpeg4; - else if (codec == "h263") - input.codec = media::OmxCodec::kCodecH263; - else if (codec == "vc1") - input.codec = media::OmxCodec::kCodecVc1; - else { - printf("Unknown codec.\n"); - return 1; + if (encoder) { + input.codec = media::OmxCodec::kCodecRaw; + // TODO(jiesun): make other format available. + output.codec = media::OmxCodec::kCodecMpeg4; + output.video_header.width = input.video_header.width = + StringToInt(cmd_line->GetSwitchValueASCII("width")); + output.video_header.height = input.video_header.height = + StringToInt(cmd_line->GetSwitchValueASCII("height")); + output.video_header.frame_rate = input.video_header.frame_rate = + StringToInt(cmd_line->GetSwitchValueASCII("framerate")); + // TODO(jiesun): assume constant bitrate now. + output.video_header.bit_rate = + StringToInt(cmd_line->GetSwitchValueASCII("bitrate")); + // TODO(jiesun): one I frame per second now. make it configurable. + output.video_header.i_dist = output.video_header.frame_rate; + // TODO(jiesun): disable B frame now. does they support it? + output.video_header.p_dist = 0; + } else { + input.codec = media::OmxCodec::kCodecNone; + if (codec == "h264") + input.codec = media::OmxCodec::kCodecH264; + else if (codec == "mpeg4") + input.codec = media::OmxCodec::kCodecMpeg4; + else if (codec == "h263") + input.codec = media::OmxCodec::kCodecH263; + else if (codec == "vc1") + input.codec = media::OmxCodec::kCodecVc1; + else { + printf("Unknown codec.\n"); + return 1; + } + output.codec = media::OmxCodec::kCodecRaw; } - output.codec = media::OmxCodec::kCodecRaw; // Create a TestApp object and run the decoder. TestApp test(input_filename.c_str(), output_filename.c_str(), - component.c_str(), + component_name, input, output, - copy); - - - if (measure_fps) - test.StartProfiler(); + copy, + measure_fps, + enable_csc, + loop_count); // This call will run the decoder until EOS is reached or an error // is encountered. test.Run(); - - if (measure_fps) - test.StopProfiler(); - - // Color space conversion. - if (!output_filename.empty()) { - std::string dumpyuv_name = cmd_line->GetSwitchValueASCII("enable-csc"); - if (!dumpyuv_name.empty()) { - // now assume the raw output is NV21; - // now assume decoder. - FILE* dump_raw = file_util::OpenFile(output_filename.c_str(), "rb"); - FILE* dump_yuv = file_util::OpenFile(dumpyuv_name.c_str(), "wb"); - if (!dump_raw || !dump_yuv) { - printf("Error - can't open file for color conversion %s\n", - dumpyuv_name.c_str()); - } else { - // TODO(jiesun): get rid of hard coded value when Startup() - // call back function is ready. - int width = 352; - int height = 288; - int frame_size = width * height * 3 / 2; // assume 4:2:0 chroma format. - scoped_array<uint8> in_buffer(new uint8[frame_size]); - scoped_array<uint8> out_buffer(new uint8[frame_size]); - while (true) { - int read; - read = fread(in_buffer.get(), sizeof(uint8), frame_size, dump_raw); - if (read != frame_size) - break; - NV21toI420(in_buffer.get(), out_buffer.get(), width, height); - fwrite(out_buffer.get(), sizeof(uint8), frame_size, dump_yuv); - } - } - if (dump_raw) - fclose(dump_raw); - if (dump_yuv) - fclose(dump_yuv); - } - } - return 0; } |