Skip to content

Commit

Permalink
VP8 decoder for chromoting
Browse files Browse the repository at this point in the history
Added DecoderVp8 and unit test for chromoting.

TEST=remoting_unittests
BUG=50235

Review URL: http://codereview.chromium.org/3032047

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60960 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
hclam@chromium.org committed Sep 29, 2010
1 parent 3190f36 commit 622b788
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 5 deletions.
8 changes: 6 additions & 2 deletions media/base/media.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ bool InitializeOpenMaxLibrary(const FilePath& module_dir);

// This is temporary to get the address of vpx_codec_vp8_cx_algo in FFmpeg.
// This method should only be called after media library is loaded.

// TODO(hclam): Remove this after we have a getter function for the same
// purpose in libvpx.
// See bug: http://code.google.com/p/webm/issues/detail?id=169
void* GetVp8CxAlgoAddress();

// This is temporary to get the address of vpx_codec_vp8_dx_algo in FFmpeg.
// This method should only be called after media library is loaded.
// TODO(hclam): Remove this after we have a getter function for the same
// purpose in libvpx.
void* GetVp8DxAlgoAddress();

} // namespace media

#endif // MEDIA_BASE_MEDIA_H_
9 changes: 9 additions & 0 deletions media/base/media_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ std::string GetDSOName(tp_ffmpeg::StubModules stub_key) {
// Address of vpx_codec_vp8_cx_algo.
static void* vp8_cx_algo_address = NULL;

// Address of vpx_codec_vp8_dx_algo.
static void* vp8_dx_algo_address = NULL;

// Attempts to initialize the media library (loading DSOs, etc.).
// Returns true if everything was successfully initialized, false otherwise.
bool InitializeMediaLibrary(const FilePath& module_dir) {
Expand All @@ -99,6 +102,8 @@ bool InitializeMediaLibrary(const FilePath& module_dir) {
if (sumo_lib) {
vp8_cx_algo_address = base::GetFunctionPointerFromNativeLibrary(
sumo_lib, "vpx_codec_vp8_cx_algo");
vp8_dx_algo_address = base::GetFunctionPointerFromNativeLibrary(
sumo_lib, "vpx_codec_vp8_dx_algo");
}
return ret;
}
Expand Down Expand Up @@ -133,4 +138,8 @@ void* GetVp8CxAlgoAddress() {
return vp8_cx_algo_address;
}

void* GetVp8DxAlgoAddress() {
return vp8_dx_algo_address;
}

} // namespace media
9 changes: 9 additions & 0 deletions media/base/media_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ FilePath::CharType* GetDLLName(FFmpegDLLKeys dll_key) {
// Address of vpx_codec_vp8_cx_algo.
static void* vp8_cx_algo_address = NULL;

// Address of vpx_codec_vp8_dx_algo.
static void* vp8_dx_algo_address = NULL;

// Attempts to initialize the media library (loading DLLs, DSOs, etc.).
// Returns true if everything was successfully initialized, false otherwise.
bool InitializeMediaLibrary(const FilePath& base_path) {
Expand Down Expand Up @@ -90,6 +93,8 @@ bool InitializeMediaLibrary(const FilePath& base_path) {
if (avcodec_lib) {
vp8_cx_algo_address = base::GetFunctionPointerFromNativeLibrary(
avcodec_lib, "vpx_codec_vp8_cx_algo");
vp8_dx_algo_address = base::GetFunctionPointerFromNativeLibrary(
avcodec_lib, "vpx_codec_vp8_dx_algo");
}

// Check that we loaded all libraries successfully. We only need to check the
Expand All @@ -115,4 +120,8 @@ void* GetVp8CxAlgoAddress() {
return vp8_cx_algo_address;
}

void* GetVp8DxAlgoAddress() {
return vp8_dx_algo_address;
}

} // namespace media
6 changes: 3 additions & 3 deletions remoting/base/codec_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,15 @@ class DecoderTester {

void ReceivedMessage(ChromotingHostMessage* message) {
if (message->has_update_stream_packet()) {
decoder_->PartialDecode(message);
EXPECT_TRUE(decoder_->PartialDecode(message));
return;
}

if (message->has_begin_update_stream()) {
decoder_->BeginDecode(
EXPECT_TRUE(decoder_->BeginDecode(
frame_, &update_rects_,
NewRunnableMethod(this, &DecoderTester::OnPartialDecodeDone),
NewRunnableMethod(this, &DecoderTester::OnDecodeDone));
NewRunnableMethod(this, &DecoderTester::OnDecodeDone)));
}

if (message->has_end_update_stream()) {
Expand Down
165 changes: 165 additions & 0 deletions remoting/base/decoder_vp8.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// 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 "remoting/base/decoder_vp8.h"

#include "media/base/media.h"
#include "media/base/yuv_convert.h"
#include "remoting/base/protocol_util.h"

extern "C" {
#define VPX_CODEC_DISABLE_COMPAT 1
#include "third_party/libvpx/include/vpx/vpx_codec.h"
#include "third_party/libvpx/include/vpx/vpx_decoder.h"
#include "third_party/libvpx/include/vpx/vp8dx.h"
}

namespace remoting {

DecoderVp8::DecoderVp8()
: state_(kWaitingForBeginRect),
rect_x_(0),
rect_y_(0),
rect_width_(0),
rect_height_(0),
updated_rects_(NULL),
codec_(NULL) {
}

DecoderVp8::~DecoderVp8() {
if (codec_) {
vpx_codec_err_t ret = vpx_codec_destroy(codec_);
CHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
}
delete codec_;
}

bool DecoderVp8::BeginDecode(scoped_refptr<media::VideoFrame> frame,
UpdatedRects* updated_rects,
Task* partial_decode_done,
Task* decode_done) {
DCHECK(!partial_decode_done_.get());
DCHECK(!decode_done_.get());
DCHECK(!updated_rects_);
DCHECK_EQ(kWaitingForBeginRect, state_);

partial_decode_done_.reset(partial_decode_done);
decode_done_.reset(decode_done);
updated_rects_ = updated_rects;

if (frame->format() != media::VideoFrame::RGB32) {
LOG(INFO) << "DecoderVp8 only supports RGB32 as output";
return false;
}
frame_ = frame;
return true;
}

bool DecoderVp8::PartialDecode(ChromotingHostMessage* message) {
scoped_ptr<ChromotingHostMessage> msg_deleter(message);
DCHECK(message->has_update_stream_packet());

bool ret = true;
if (message->update_stream_packet().has_begin_rect())
ret = HandleBeginRect(message);
if (ret && message->update_stream_packet().has_rect_data())
ret = HandleRectData(message);
if (ret && message->update_stream_packet().has_end_rect())
ret = HandleEndRect(message);
return ret;
}

void DecoderVp8::EndDecode() {
DCHECK_EQ(kWaitingForBeginRect, state_);
decode_done_->Run();

partial_decode_done_.reset();
decode_done_.reset();
frame_ = NULL;
updated_rects_ = NULL;
}

bool DecoderVp8::HandleBeginRect(ChromotingHostMessage* message) {
DCHECK_EQ(kWaitingForBeginRect, state_);
state_ = kWaitingForRectData;

rect_width_ = message->update_stream_packet().begin_rect().width();
rect_height_ = message->update_stream_packet().begin_rect().height();
rect_x_ = message->update_stream_packet().begin_rect().x();
rect_y_ = message->update_stream_packet().begin_rect().y();

PixelFormat pixel_format =
message->update_stream_packet().begin_rect().pixel_format();
if (pixel_format != PixelFormatYv12)
return false;
return true;
}

bool DecoderVp8::HandleRectData(ChromotingHostMessage* message) {
DCHECK_EQ(kWaitingForRectData, state_);
DCHECK_EQ(0,
message->update_stream_packet().rect_data().sequence_number());

// Initialize the codec as needed.
if (!codec_) {
codec_ = new vpx_codec_ctx_t();
vpx_codec_err_t ret =
vpx_codec_dec_init(
codec_,
(const vpx_codec_iface_t*)media::GetVp8DxAlgoAddress(), NULL, 0);
if (ret != VPX_CODEC_OK) {
LOG(INFO) << "Cannot initialize codec.";
delete codec_;
codec_ = NULL;
return false;
}
}

// Do the actual decoding.
vpx_codec_err_t ret = vpx_codec_decode(
codec_,
(uint8_t*)message->update_stream_packet().rect_data().data().c_str(),
message->update_stream_packet().rect_data().data().size(),
NULL, 0);
if (ret != VPX_CODEC_OK) {
LOG(INFO) << "Decoding failed:"
<< vpx_codec_err_to_string(ret)
<< "\n"
<< "Details: "
<< vpx_codec_error(codec_)
<< "\n"
<< vpx_codec_error_detail(codec_);
return false;
}

// Gets the decoded data.
vpx_codec_iter_t iter = NULL;
vpx_image_t* image = vpx_codec_get_frame(codec_, &iter);
if (!image) {
LOG(INFO) << "No video frame decoded";
return false;
}

// Perform YUV conversion.
media::ConvertYUVToRGB32(image->planes[0], image->planes[1], image->planes[2],
frame_->data(media::VideoFrame::kRGBPlane),
rect_width_, rect_height_,
image->stride[0], image->stride[1],
frame_->stride(media::VideoFrame::kRGBPlane),
media::YV12);

updated_rects_->clear();
updated_rects_->push_back(gfx::Rect(rect_x_, rect_y_,
rect_width_, rect_height_));
partial_decode_done_->Run();
return true;
}

bool DecoderVp8::HandleEndRect(ChromotingHostMessage* message) {
DCHECK_EQ(kWaitingForRectData, state_);
state_ = kWaitingForBeginRect;
return true;
}

} // namespace remoting
56 changes: 56 additions & 0 deletions remoting/base/decoder_vp8.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 REMOTING_BASE_DECODER_VP8_H_
#define REMOTING_BASE_DECODER_VP8_H_

#include "remoting/base/decoder.h"

typedef struct vpx_codec_ctx vpx_codec_ctx_t;

namespace remoting {

class DecoderVp8 : public Decoder {
public:
DecoderVp8();
~DecoderVp8();

// Decoder implementations.
virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame,
UpdatedRects* update_rects,
Task* partial_decode_done,
Task* decode_done);
virtual bool PartialDecode(ChromotingHostMessage* message);
virtual void EndDecode();

private:
bool HandleBeginRect(ChromotingHostMessage* message);
bool HandleRectData(ChromotingHostMessage* message);
bool HandleEndRect(ChromotingHostMessage* message);

// The internal state of the decoder.
State state_;

// Keeps track of the updating rect.
int rect_x_;
int rect_y_;
int rect_width_;
int rect_height_;

// Tasks to call when decode is done.
scoped_ptr<Task> partial_decode_done_;
scoped_ptr<Task> decode_done_;

// The video frame to write to.
scoped_refptr<media::VideoFrame> frame_;
UpdatedRects* updated_rects_;

vpx_codec_ctx_t* codec_;

DISALLOW_COPY_AND_ASSIGN(DecoderVp8);
};

} // namespace remoting

#endif // REMOTING_BASE_DECODER_VP8_H_
21 changes: 21 additions & 0 deletions remoting/base/decoder_vp8_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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/base/video_frame.h"
#include "remoting/base/codec_test.h"
#include "remoting/base/decoder_vp8.h"
#include "remoting/base/encoder_vp8.h"
#include "remoting/client/mock_objects.h"
#include "testing/gtest/include/gtest/gtest.h"


namespace remoting {

TEST(DecoderVp8Test, EncodeAndDecode) {
EncoderVp8 encoder;
DecoderVp8 decoder;
TestEncoderDecoder(&encoder, &decoder, false);
}

} // namespace remoting
3 changes: 3 additions & 0 deletions remoting/remoting.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@
'base/decoder.h',
'base/decoder_verbatim.cc',
'base/decoder_verbatim.h',
'base/decoder_vp8.cc',
'base/decoder_vp8.h',
'base/decoder_zlib.cc',
'base/decoder_zlib.h',
'base/decompressor.h',
Expand Down Expand Up @@ -400,6 +402,7 @@
'base/codec_test.h',
'base/compressor_zlib_unittest.cc',
'base/decoder_verbatim_unittest.cc',
'base/decoder_vp8_unittest.cc',
'base/decoder_zlib_unittest.cc',
'base/decompressor_zlib_unittest.cc',
'base/encoder_verbatim_unittest.cc',
Expand Down

0 comments on commit 622b788

Please sign in to comment.