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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdio.h>
6 #include <string>
7
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/logging.h"
12 #include "content/common/gpu/media/vaapi_h264_decoder.h"
13 #include "media/base/video_decoder_config.h"
14
15 // This program is run like this:
16 // DISPLAY=:0 ./vaapi_h264_decoder_test -v=1 input.264 output.yuv
17 // The input is read from input.264 and output written to output.yuv
18
19 namespace content {
20
21 // This class encapsulates the use of VaapiH264Decoder to a simpler interface.
22 // It reads an H.264 Annex B bytestream from a file and outputs the decoded
23 // frames (in I420 format) to another file.
24 //
25 // To use the class, construct an instance, call Initialize() to specify the
26 // input and output file path, then call Run() to decode the whole stream and
27 // output the frames.
28 //
29 // This class must be created, called and destroyed on a single thread, and
30 // does nothing internally on any other thread.
31 class VaapiH264DecoderLoop {
32 public:
33 VaapiH264DecoderLoop();
34 ~VaapiH264DecoderLoop();
35
36 // Initialize the decoder. Return true if successful.
37 bool Initialize(base::FilePath input_path, base::FilePath output_path);
38
39 // Run the decode loop. The decoded data is written to the file specified by
40 // output_path in Initialize(). Return true if all decoding is successful.
41 bool Run();
42
43 private:
44 void ReportToUMA(VaapiH264Decoder::VAVDAH264DecoderFailure error) {}
45
46 // Callback from the decoder when a picture is decoded.
47 void OutputPicture(int32 input_id,
48 const scoped_refptr<VASurface>& va_surface);
49
50 // Recycle one surface and put it on available_surfaces_ list.
51 void RecycleSurface(VASurfaceID va_surface_id);
52
53 // Give all surfaces in available_surfaces_ to the decoder.
54 void RefillSurfaces();
55
56 // Free the current set of surfaces and allocate a new set of
57 // surfaces. Returns true when successful.
58 bool AllocateNewSurfaces();
59
60 scoped_ptr<VaapiH264Decoder> decoder_;
61 scoped_ptr<VaapiWrapper> wrapper_;
62 std::string data_; // data read from input_path
63 std::vector<VASurfaceID> available_surfaces_;
64
65 // 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.
66 // need to be initialized and freed manually.
67 Display* x_display_;
68 FILE* output_file_; // output data is written to this file
69 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.
70 size_t num_surfaces_; // number of surfaces in the current set of surfaces
71 };
72
73 VaapiH264DecoderLoop::VaapiH264DecoderLoop()
74 : x_display_(NULL),
75 output_file_(NULL),
76 picture_count_(0),
77 num_surfaces_(0) {}
78
79 VaapiH264DecoderLoop::~VaapiH264DecoderLoop() {
80 // We need to destruct decoder and wrapper first because:
81 // (1) The decoder has a reference to the wrapper.
82 // (2) The wrapper has a reference to x_display_.
83 decoder_.reset();
84 wrapper_.reset();
85
86 if (x_display_) {
87 XCloseDisplay(x_display_);
88 }
89 if (output_file_) {
90 fclose(output_file_);
91 }
92 }
93
94 bool VaapiH264DecoderLoop::Initialize(base::FilePath input_path,
95 base::FilePath output_path) {
96 x_display_ = XOpenDisplay(NULL);
97 if (!x_display_) {
98 LOG(ERROR) << "Can't open X display";
99 return false;
100 }
101
102 media::VideoCodecProfile profile = media::H264PROFILE_HIGH;
103 base::Closure report_error_cb = base::Bind(&VaapiH264DecoderLoop::ReportToUMA,
104 base::Unretained(this),
105 VaapiH264Decoder::VAAPI_ERROR);
106 wrapper_ = VaapiWrapper::Create(profile, x_display_, report_error_cb);
107 if (!wrapper_.get()) {
108 LOG(ERROR) << "Can't create vaapi wrapper";
109 return false;
110 }
111
112 decoder_.reset(new VaapiH264Decoder(
113 wrapper_.get(),
114 base::Bind(&VaapiH264DecoderLoop::OutputPicture, base::Unretained(this)),
115 base::Bind(&VaapiH264DecoderLoop::ReportToUMA, base::Unretained(this))));
116
117 if (!base::ReadFileToString(input_path, &data_)) {
118 LOG(ERROR) << "failed to read input data from " << input_path.value();
119 return false;
120 }
121
122 const int input_id = 0; // We don't use input_id in this class.
123 decoder_->SetStream(
124 reinterpret_cast<const uint8*>(data_.c_str()), data_.size(), input_id);
125
126 output_file_ = fopen(output_path.value().c_str(), "w");
127 if (!output_file_) {
128 return false;
129 }
130
131 return true;
132 }
133
134 bool VaapiH264DecoderLoop::Run() {
135 while (1) {
136 switch (decoder_->Decode()) {
137 case VaapiH264Decoder::kDecodeError:
138 LOG(ERROR) << "Decode Error";
139 return false;
140 case VaapiH264Decoder::kAllocateNewSurfaces:
141 VLOG(1) << "Allocate new surfaces";
142 if (!AllocateNewSurfaces()) {
143 LOG(ERROR) << "Failed to allocate new surfaces";
144 return false;
145 }
146 break;
147 case VaapiH264Decoder::kRanOutOfStreamData: {
148 bool rc = decoder_->Flush();
149 VLOG(1) << "Flush returns " << rc;
150 return rc;
151 }
152 case VaapiH264Decoder::kRanOutOfSurfaces:
153 VLOG(1) << "Ran out of surfaces";
154 RefillSurfaces();
155 break;
156 }
157 }
158 }
159
160 static bool WriteVideoFrameToFile(const scoped_refptr<media::VideoFrame>& frame,
161 FILE* file) {
162 for (size_t i = 0; i < media::VideoFrame::NumPlanes(frame->format()); i++) {
163 uint8* data = frame->data(i);
164 int bytes = frame->row_bytes(i);
165 for (int j = 0; j < frame->rows(i); j++) {
166 const char* buf = reinterpret_cast<const char*>(data);
167 if (fwrite(buf, bytes, 1, file) != 1) {
168 return false;
169 }
170 data += frame->stride(i);
171 }
172 }
173 return true;
174 }
175
176 void VaapiH264DecoderLoop::OutputPicture(
177 int32 input_id,
178 const scoped_refptr<VASurface>& va_surface) {
179 VLOG(1) << "OutputPicture: picture " << picture_count_++;
180 scoped_refptr<media::VideoFrame> frame =
181 wrapper_->GetI420FromSurface(va_surface->id());
182 if (frame.get()) {
183 if (!WriteVideoFrameToFile(frame, output_file_)) {
184 LOG(ERROR) << "fwrite failed";
185 }
186 } else {
187 LOG(ERROR) << "Cannot convert surface to I420.";
188 }
189 }
190
191 void VaapiH264DecoderLoop::RecycleSurface(VASurfaceID va_surface_id) {
192 available_surfaces_.push_back(va_surface_id);
193 }
194
195 void VaapiH264DecoderLoop::RefillSurfaces() {
196 for (size_t i = 0; i < available_surfaces_.size(); i++) {
197 VASurface::ReleaseCB release_cb = base::Bind(
198 &VaapiH264DecoderLoop::RecycleSurface, base::Unretained(this));
199 scoped_refptr<VASurface> surface(
200 new VASurface(available_surfaces_[i], release_cb));
201 decoder_->ReuseSurface(surface);
202 }
203 available_surfaces_.clear();
204 }
205
206 bool VaapiH264DecoderLoop::AllocateNewSurfaces() {
207 CHECK_EQ(num_surfaces_, available_surfaces_.size())
208 << "not all surfaces are returned";
209
210 available_surfaces_.clear();
211 wrapper_->DestroySurfaces();
212
213 gfx::Size size = decoder_->GetPicSize();
214 num_surfaces_ = decoder_->GetRequiredNumOfPictures();
215 return wrapper_->CreateSurfaces(size, num_surfaces_, &available_surfaces_);
216 }
217
218 } // namespace content
219
220 int main(int argc, char** argv) {
221 CommandLine::Init(argc, argv);
222
223 // Needed to enable DVLOG through --vmodule.
224 logging::LoggingSettings settings;
225 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
226 settings.dcheck_state =
227 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
228 CHECK(logging::InitLogging(settings));
229
230 // Process command line.
231 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
232 CHECK(cmd_line);
233 const CommandLine::StringVector& args = cmd_line->GetArgs();
234 if (args.size() != 2) {
235 LOG(ERROR) << "Usage: vaapi_h264_decoder_test input_file output_file";
236 return EXIT_FAILURE;
237 }
238
239 // We are not in a sandbox, but we still need to do the initialization.
240 content::VaapiWrapper::PreSandboxInitialization();
241
242 base::FilePath input_path(args[0]);
243 base::FilePath output_path(args[1]);
244
245 VLOG(1) << "Input File: " << input_path.value();
246 VLOG(1) << "Output File: " << output_path.value();
247
248 content::VaapiH264DecoderLoop loop;
249 if (!loop.Initialize(input_path, output_path)) {
250 LOG(ERROR) << "initialize decoder loop failed";
251 return EXIT_FAILURE;
252 }
253 if (!loop.Run()) {
254 LOG(ERROR) << "run decoder loop failed";
255 return EXIT_FAILURE;
256 }
257 return 0;
258 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698