Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Unified Diff: content/common/gpu/media/vaapi_h264_decoder_test.cc

Issue 27498002: Add vaapi_h264_decoder_test. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address review comments Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/gpu/media/vaapi_h264_decoder_test.cc
diff --git a/content/common/gpu/media/vaapi_h264_decoder_test.cc b/content/common/gpu/media/vaapi_h264_decoder_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f26df049fcd37e39088547470a3652a284a1d8a7
--- /dev/null
+++ b/content/common/gpu/media/vaapi_h264_decoder_test.cc
@@ -0,0 +1,258 @@
+// Copyright 2013 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 <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "content/common/gpu/media/vaapi_h264_decoder.h"
+#include "media/base/video_decoder_config.h"
+
+// This program is run like this:
+// DISPLAY=:0 ./vaapi_h264_decoder_test -v=1 input.264 output.yuv
+// The input is read from input.264 and output written to output.yuv
+
+namespace content {
+
+// This class encapsulates the use of VaapiH264Decoder to a simpler interface.
+// It reads an H.264 Annex B bytestream from a file and outputs the decoded
+// frames (in I420 format) to another file.
+//
+// To use the class, construct an instance, call Initialize() to specify the
+// input and output file path, then call Run() to decode the whole stream and
+// output the frames.
+//
+// This class must be created, called and destroyed on a single thread, and
+// does nothing internally on any other thread.
+class VaapiH264DecoderLoop {
+ public:
+ VaapiH264DecoderLoop();
+ ~VaapiH264DecoderLoop();
+
+ // Initialize the decoder. Return true if successful.
+ bool Initialize(base::FilePath input_path, base::FilePath output_path);
+
+ // Run the decode loop. The decoded data is written to the file specified by
+ // output_path in Initialize(). Return true if all decoding is successful.
+ bool Run();
+
+ private:
+ void ReportToUMA(VaapiH264Decoder::VAVDAH264DecoderFailure error) {}
+
+ // Callback from the decoder when a picture is decoded.
+ void OutputPicture(int32 input_id,
+ const scoped_refptr<VASurface>& va_surface);
+
+ // Recycle one surface and put it on available_surfaces_ list.
+ void RecycleSurface(VASurfaceID va_surface_id);
+
+ // Give all surfaces in available_surfaces_ to the decoder.
+ void RefillSurfaces();
+
+ // Free the current set of surfaces and allocate a new set of
+ // surfaces. Returns true when successful.
+ bool AllocateNewSurfaces();
+
+ scoped_ptr<VaapiH264Decoder> decoder_;
+ scoped_ptr<VaapiWrapper> wrapper_;
+ std::string data_; // data read from input_path
+ std::vector<VASurfaceID> available_surfaces_;
+
+ // These members (x_display_, output_file_, picture_count_, num_surfaces_)
Pawel Osciak 2013/11/21 06:31:41 picture_count_ and num_surfaces are not really fre
chihchung 2013/11/21 12:17:32 Clarified a bit.
+ // need to be initialized and freed manually.
+ Display* x_display_;
+ FILE* output_file_; // output data is written to this file
+ int picture_count_; // number of pictures already outputted
Pawel Osciak 2013/11/21 06:31:41 s/picture_count_/num_outputted_pictures_
chihchung 2013/11/21 12:17:32 Done.
+ size_t num_surfaces_; // number of surfaces in the current set of surfaces
+};
+
+VaapiH264DecoderLoop::VaapiH264DecoderLoop()
+ : x_display_(NULL),
+ output_file_(NULL),
+ picture_count_(0),
+ num_surfaces_(0) {}
+
+VaapiH264DecoderLoop::~VaapiH264DecoderLoop() {
+ // We need to destruct decoder and wrapper first because:
+ // (1) The decoder has a reference to the wrapper.
+ // (2) The wrapper has a reference to x_display_.
+ decoder_.reset();
+ wrapper_.reset();
+
+ if (x_display_) {
+ XCloseDisplay(x_display_);
+ }
+ if (output_file_) {
+ fclose(output_file_);
+ }
+}
+
+bool VaapiH264DecoderLoop::Initialize(base::FilePath input_path,
+ base::FilePath output_path) {
+ x_display_ = XOpenDisplay(NULL);
+ if (!x_display_) {
+ LOG(ERROR) << "Can't open X display";
+ return false;
+ }
+
+ media::VideoCodecProfile profile = media::H264PROFILE_HIGH;
+ base::Closure report_error_cb = base::Bind(&VaapiH264DecoderLoop::ReportToUMA,
+ base::Unretained(this),
+ VaapiH264Decoder::VAAPI_ERROR);
+ wrapper_ = VaapiWrapper::Create(profile, x_display_, report_error_cb);
+ if (!wrapper_.get()) {
+ LOG(ERROR) << "Can't create vaapi wrapper";
+ return false;
+ }
+
+ decoder_.reset(new VaapiH264Decoder(
+ wrapper_.get(),
+ base::Bind(&VaapiH264DecoderLoop::OutputPicture, base::Unretained(this)),
+ base::Bind(&VaapiH264DecoderLoop::ReportToUMA, base::Unretained(this))));
+
+ if (!base::ReadFileToString(input_path, &data_)) {
+ LOG(ERROR) << "failed to read input data from " << input_path.value();
+ return false;
+ }
+
+ const int input_id = 0; // We don't use input_id in this class.
+ decoder_->SetStream(
+ reinterpret_cast<const uint8*>(data_.c_str()), data_.size(), input_id);
+
+ output_file_ = fopen(output_path.value().c_str(), "w");
+ if (!output_file_) {
+ return false;
+ }
+
+ return true;
+}
+
+bool VaapiH264DecoderLoop::Run() {
+ while (1) {
+ switch (decoder_->Decode()) {
+ case VaapiH264Decoder::kDecodeError:
+ LOG(ERROR) << "Decode Error";
+ return false;
+ case VaapiH264Decoder::kAllocateNewSurfaces:
+ VLOG(1) << "Allocate new surfaces";
+ if (!AllocateNewSurfaces()) {
+ LOG(ERROR) << "Failed to allocate new surfaces";
+ return false;
+ }
+ break;
+ case VaapiH264Decoder::kRanOutOfStreamData: {
+ bool rc = decoder_->Flush();
+ VLOG(1) << "Flush returns " << rc;
+ return rc;
+ }
+ case VaapiH264Decoder::kRanOutOfSurfaces:
+ VLOG(1) << "Ran out of surfaces";
+ RefillSurfaces();
+ break;
+ }
+ }
+}
+
+static bool WriteVideoFrameToFile(const scoped_refptr<media::VideoFrame>& frame,
+ FILE* file) {
+ for (size_t i = 0; i < media::VideoFrame::NumPlanes(frame->format()); i++) {
+ uint8* data = frame->data(i);
+ int bytes = frame->row_bytes(i);
+ for (int j = 0; j < frame->rows(i); j++) {
+ const char* buf = reinterpret_cast<const char*>(data);
+ if (fwrite(buf, bytes, 1, file) != 1) {
+ return false;
+ }
+ data += frame->stride(i);
+ }
+ }
+ return true;
+}
+
+void VaapiH264DecoderLoop::OutputPicture(
+ int32 input_id,
+ const scoped_refptr<VASurface>& va_surface) {
+ VLOG(1) << "OutputPicture: picture " << picture_count_++;
+ scoped_refptr<media::VideoFrame> frame =
+ wrapper_->GetI420FromSurface(va_surface->id());
+ if (frame.get()) {
+ if (!WriteVideoFrameToFile(frame, output_file_)) {
+ LOG(ERROR) << "fwrite failed";
+ }
+ } else {
+ LOG(ERROR) << "Cannot convert surface to I420.";
+ }
+}
+
+void VaapiH264DecoderLoop::RecycleSurface(VASurfaceID va_surface_id) {
+ available_surfaces_.push_back(va_surface_id);
+}
+
+void VaapiH264DecoderLoop::RefillSurfaces() {
+ for (size_t i = 0; i < available_surfaces_.size(); i++) {
+ VASurface::ReleaseCB release_cb = base::Bind(
+ &VaapiH264DecoderLoop::RecycleSurface, base::Unretained(this));
+ scoped_refptr<VASurface> surface(
+ new VASurface(available_surfaces_[i], release_cb));
+ decoder_->ReuseSurface(surface);
+ }
+ available_surfaces_.clear();
+}
+
+bool VaapiH264DecoderLoop::AllocateNewSurfaces() {
+ CHECK_EQ(num_surfaces_, available_surfaces_.size())
+ << "not all surfaces are returned";
+
+ available_surfaces_.clear();
+ wrapper_->DestroySurfaces();
+
+ gfx::Size size = decoder_->GetPicSize();
+ num_surfaces_ = decoder_->GetRequiredNumOfPictures();
+ return wrapper_->CreateSurfaces(size, num_surfaces_, &available_surfaces_);
+}
+
+} // namespace content
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+
+ // Needed to enable DVLOG through --vmodule.
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ settings.dcheck_state =
+ logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+ CHECK(logging::InitLogging(settings));
+
+ // Process command line.
+ CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ CHECK(cmd_line);
+ const CommandLine::StringVector& args = cmd_line->GetArgs();
+ if (args.size() != 2) {
+ LOG(ERROR) << "Usage: vaapi_h264_decoder_test input_file output_file";
+ return EXIT_FAILURE;
+ }
+
+ // We are not in a sandbox, but we still need to do the initialization.
+ content::VaapiWrapper::PreSandboxInitialization();
+
+ base::FilePath input_path(args[0]);
+ base::FilePath output_path(args[1]);
+
+ VLOG(1) << "Input File: " << input_path.value();
+ VLOG(1) << "Output File: " << output_path.value();
+
+ content::VaapiH264DecoderLoop loop;
+ if (!loop.Initialize(input_path, output_path)) {
+ LOG(ERROR) << "initialize decoder loop failed";
+ return EXIT_FAILURE;
+ }
+ if (!loop.Run()) {
+ LOG(ERROR) << "run decoder loop failed";
+ return EXIT_FAILURE;
+ }
+ return 0;
+}

Powered by Google App Engine
This is Rietveld 408576698