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

Side by Side Diff: media/gpu/media_foundation_video_encode_accelerator_win.cc

Issue 2058413003: H264 HW encode using MediaFoundation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 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 2016 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 "media/gpu/media_foundation_video_encode_accelerator_win.h"
6
7 #if !defined(OS_WIN)
8 // #error This file should only be built on Windows.
ananta 2016/06/28 22:50:01 Don't think we need this. The _win suffix should b
emircan 2016/07/02 00:07:38 Done.
9 #else
10 #pragma warning(push)
11 #pragma warning(disable : 4800) // Disable warning for added padding.
12 #endif // !defined(OS_WIN)
13
14 #include <codecapi.h>
15 #include <mferror.h>
16 #include <mftransform.h>
17
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "base/win/scoped_co_mem.h"
20 #include "base/win/scoped_variant.h"
21 #include "media/base/win/mf_helpers.h"
22 #include "media/base/win/mf_initializer.h"
23 #include "third_party/libyuv/include/libyuv.h"
24
25 using base::win::ScopedComPtr;
26 using media::mf::MediaBufferScopedPointer;
27
28 namespace media {
29
30 namespace {
31
32 const size_t kMaxFrameRateNumerator = 30;
33 const size_t kMaxFrameRateDenominator = 1;
34 const size_t kMaxResolutionWidth = 4096;
35 const size_t kMaxResolutionHeight = 2160;
36 const size_t kNumInputBuffers = 3;
37 const size_t kOneSecondInMicroseconds = 1000000;
38 const size_t kOutputSampleBufferSizeRatio = 4;
39
40 static const wchar_t* const kMediaFoundationVideoEncoderDLLs[] = {
41 L"mf.dll", L"mfplat.dll",
42 };
43
44 } // namespace
45
46 struct MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef {
47 BitstreamBufferRef(int32_t id,
48 std::unique_ptr<base::SharedMemory> shm,
49 size_t size)
50 : id(id), shm(std::move(shm)), size(size) {}
51 const int32_t id;
52 const std::unique_ptr<base::SharedMemory> shm;
53 const size_t size;
54
55 private:
56 DISALLOW_IMPLICIT_CONSTRUCTORS(BitstreamBufferRef);
57 };
58
59 MediaFoundationVideoEncodeAccelerator::MediaFoundationVideoEncodeAccelerator()
60 : win_version_(base::win::GetVersion()),
61 client_task_runner_(base::ThreadTaskRunnerHandle::Get()),
62 encoder_thread_("MFEncoderThread"),
63 encoder_task_weak_factory_(this) {
64 DVLOG(3) << __FUNCTION__;
65 encoder_weak_ptr_ = encoder_task_weak_factory_.GetWeakPtr();
66 }
67
68 MediaFoundationVideoEncodeAccelerator::
69 ~MediaFoundationVideoEncodeAccelerator() {
70 DVLOG(3) << __FUNCTION__;
71 DCHECK(thread_checker_.CalledOnValidThread());
72
73 Destroy();
74 DCHECK(!encoder_thread_.IsRunning());
75 DCHECK(!encoder_task_weak_factory_.HasWeakPtrs());
76 }
77
78 media::VideoEncodeAccelerator::SupportedProfiles
79 MediaFoundationVideoEncodeAccelerator::GetSupportedProfiles() {
80 DVLOG(3) << __FUNCTION__;
81 DCHECK(thread_checker_.CalledOnValidThread());
82
83 SupportedProfiles profiles;
84 if (base::win::GetVersion() < base::win::VERSION_WIN8) {
85 DLOG(ERROR) << "Windows versions earlier than 8 are not supported.";
86 return profiles;
87 }
88
89 SupportedProfile profile;
90 profile.profile = media::H264PROFILE_BASELINE;
ananta 2016/06/28 22:50:01 Does this always have to be the baseline profile?.
emircan 2016/07/02 00:07:37 OpenH264 SW only supports baseline right now. In c
91 profile.max_framerate_numerator = kMaxFrameRateNumerator;
92 profile.max_framerate_denominator = kMaxFrameRateDenominator;
93 profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
94 profiles.push_back(profile);
95 return profiles;
96 }
97
98 bool MediaFoundationVideoEncodeAccelerator::Initialize(
99 media::VideoPixelFormat format,
100 const gfx::Size& input_visible_size,
101 media::VideoCodecProfile output_profile,
102 uint32_t initial_bitrate,
ananta 2016/06/28 22:50:01 The Initialize function is very big. Please split
emircan 2016/07/02 00:07:37 Done.
103 Client* client) {
104 DVLOG(3) << __FUNCTION__
105 << ": input_format=" << media::VideoPixelFormatToString(format)
106 << ", input_visible_size=" << input_visible_size.ToString()
107 << ", output_profile=" << output_profile
108 << ", initial_bitrate=" << initial_bitrate;
109 DCHECK(thread_checker_.CalledOnValidThread());
110
111 if (media::PIXEL_FORMAT_I420 != format) {
112 DLOG(ERROR) << "Input format not supported= "
113 << media::VideoPixelFormatToString(format);
114 return false;
115 }
ananta 2016/06/28 22:50:01 newline here.
emircan 2016/07/02 00:07:37 Done.
116 if (media::H264PROFILE_BASELINE != output_profile) {
117 DLOG(ERROR) << "Output profile not supported= " << output_profile;
118 return false;
119 }
120
121 for (const wchar_t* mfdll : kMediaFoundationVideoEncoderDLLs) {
122 HMODULE dll = ::GetModuleHandle(mfdll);
123 if (!dll) {
124 DLOG(ERROR) << mfdll << " is required for encoding";
125 return false;
126 }
127 }
128 media::InitializeMediaFoundation();
ananta 2016/06/28 22:50:00 newline here.
emircan 2016/07/02 00:07:37 Done.
129
130 uint32_t flags = MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER;
131 MFT_REGISTER_TYPE_INFO input_info;
132 input_info.guidMajorType = MFMediaType_Video;
133 input_info.guidSubtype = MFVideoFormat_NV12;
134 MFT_REGISTER_TYPE_INFO output_info;
135 output_info.guidMajorType = MFMediaType_Video;
136 output_info.guidSubtype = MFVideoFormat_H264;
137
138 base::win::ScopedCoMem<CLSID> CLSIDs;
139 uint32_t count = 0;
140 HRESULT hr = MFTEnum(MFT_CATEGORY_VIDEO_ENCODER, flags, NULL, &output_info,
141 NULL, &CLSIDs, &count);
142 RETURN_ON_HR_FAILURE(hr, "Couldn't enumerate hardware encoder", false);
143 RETURN_ON_FAILURE((count > 0), "No HW encoder found", false);
144 DVLOG(3) << "HW encoder(s) found: " << count;
145 hr = encoder_.CreateInstance(CLSIDs[0]);
146 RETURN_ON_HR_FAILURE(hr, "Couldn't activate hardware encoder", false);
147
148 if (!encoder_thread_.Start()) {
149 DLOG(ERROR) << "Failed spawning encoder thread.";
150 return false;
151 }
152 encoder_thread_task_runner_ = encoder_thread_.task_runner();
ananta 2016/06/28 22:50:01 newline
emircan 2016/07/02 00:07:38 Done.
153
154 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
155 client_ = client_ptr_factory_->GetWeakPtr();
156 input_visible_size_ = input_visible_size;
157 frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
158 target_bitrate_ = initial_bitrate;
159 bitstream_buffer_size_ = input_visible_size.GetArea();
160
161 u_plane_offset_ =
162 VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kYPlane,
163 input_visible_size_)
164 .GetArea();
165 v_plane_offset_ =
166 u_plane_offset_ +
167 VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kUPlane,
168 input_visible_size_)
169 .GetArea();
170
171 hr = encoder_->GetStreamLimits(
172 &input_stream_count_min_, &input_stream_count_max_,
173 &output_stream_count_min_, &output_stream_count_max_);
174 RETURN_ON_HR_FAILURE(hr, "Couldn't query stream limits", false);
175 DVLOG(3) << "Stream limits: " << input_stream_count_min_ << ","
176 << input_stream_count_max_ << "," << output_stream_count_min_ << ","
177 << output_stream_count_max_;
178
179 base::win::ScopedComPtr<IMFMediaType> imf_output_media_type;
180 hr = MFCreateMediaType(imf_output_media_type.Receive());
181 hr &= imf_output_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
ananta 2016/06/28 22:50:01 what happens if MFCreateMediaType fails?. Addition
emircan 2016/07/02 00:07:38 I will add a return there if fails.
182 hr &= imf_output_media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
183 hr &= imf_output_media_type->SetUINT32(MF_MT_AVG_BITRATE, target_bitrate_);
184 hr &= MFSetAttributeRatio(imf_output_media_type.get(), MF_MT_FRAME_RATE,
185 frame_rate_, kMaxFrameRateDenominator);
186 hr &= MFSetAttributeSize(imf_output_media_type.get(), MF_MT_FRAME_SIZE,
ananta 2016/06/28 22:50:01 Move this to the next line for consistency
emircan 2016/07/02 00:07:37 Done.
187 input_visible_size_.width(),
188 input_visible_size_.height());
189 hr &= imf_output_media_type->SetUINT32(MF_MT_INTERLACE_MODE,
190 MFVideoInterlace_Progressive);
191 hr &= imf_output_media_type->SetUINT32(MF_MT_MPEG2_PROFILE,
192 eAVEncH264VProfile_Base);
193 RETURN_ON_HR_FAILURE(hr, "Couldn't set output params", false);
194 hr = encoder_->SetOutputType(0, imf_output_media_type.get(), 0);
195 RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", false);
196
197 base::win::ScopedComPtr<IMFMediaType> imf_input_media_type;
198 hr = MFCreateMediaType(imf_input_media_type.Receive());
199 hr &= imf_input_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
ananta 2016/06/28 22:50:01 Ditto for MFCreateMediaType and the other Set oper
emircan 2016/07/02 00:07:38 Done.
200 hr &= imf_input_media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
201 hr &= MFSetAttributeRatio(imf_input_media_type.get(), MF_MT_FRAME_RATE,
202 frame_rate_, kMaxFrameRateDenominator);
203 hr &= MFSetAttributeSize(imf_input_media_type.get(), MF_MT_FRAME_SIZE,
204 input_visible_size_.width(),
205 input_visible_size_.height());
206 hr &= imf_input_media_type->SetUINT32(MF_MT_INTERLACE_MODE,
207 MFVideoInterlace_Progressive);
208 RETURN_ON_HR_FAILURE(hr, "Couldn't set input params", false);
209 hr = encoder_->SetInputType(0, imf_input_media_type.get(), 0);
210 RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", false);
211
212 hr = encoder_.QueryInterface(IID_ICodecAPI, codec_api_.ReceiveVoid());
213 RETURN_ON_HR_FAILURE(hr, "Couldn't get ICodecAPI", false);
214 VARIANT var;
215 var.vt = VT_UI4;
216 var.ulVal = eAVEncCommonRateControlMode_CBR;
217 hr = codec_api_->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var);
218 RETURN_ON_HR_FAILURE(hr, "Couldn't set CommonRateControlMode", false);
219 var.ulVal = target_bitrate_;
220 hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
221 RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", false);
222 var.ulVal = eAVEncAdaptiveMode_FrameRate;
223 hr = codec_api_->SetValue(&CODECAPI_AVEncAdaptiveMode, &var);
224 RETURN_ON_HR_FAILURE(hr, "Couldn't set FrameRate", false);
225 var.vt = VT_BOOL;
226 var.boolVal = VARIANT_TRUE;
227 hr = codec_api_->SetValue(&CODECAPI_AVLowLatencyMode, &var);
228 RETURN_ON_HR_FAILURE(hr, "Couldn't set LowLatencyMode", false);
229
230 input_sample_.Attach(mf::CreateEmptySampleWithBuffer(
231 VideoFrame::AllocationSize(PIXEL_FORMAT_I420, input_visible_size), 2));
232 output_sample_.Attach(mf::CreateEmptySampleWithBuffer(
233 bitstream_buffer_size_ * kOutputSampleBufferSizeRatio, 2));
234
235 hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
236 RETURN_ON_HR_FAILURE(hr, "Couldn't set ProcessMessage", false);
237
238 client_task_runner_->PostTask(
239 FROM_HERE,
240 base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers,
241 input_visible_size_, bitstream_buffer_size_));
242 return SUCCEEDED(hr);
243 }
244
245 void MediaFoundationVideoEncodeAccelerator::Encode(
246 const scoped_refptr<media::VideoFrame>& frame,
247 bool force_keyframe) {
248 DVLOG(3) << __FUNCTION__;
249 DCHECK(thread_checker_.CalledOnValidThread());
250
251 encoder_thread_task_runner_->PostTask(
252 FROM_HERE, base::Bind(&MediaFoundationVideoEncodeAccelerator::EncodeTask,
253 encoder_weak_ptr_, frame, force_keyframe));
254 }
255
256 void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBuffer(
257 const media::BitstreamBuffer& buffer) {
258 DVLOG(3) << __FUNCTION__ << ": buffer size=" << buffer.size();
259 DCHECK(thread_checker_.CalledOnValidThread());
260
261 if (buffer.size() < bitstream_buffer_size_) {
262 DLOG(ERROR) << "Output BitstreamBuffer isn't big enough: " << buffer.size()
263 << " vs. " << bitstream_buffer_size_;
264 client_->NotifyError(kInvalidArgumentError);
265 return;
266 }
267
268 std::unique_ptr<base::SharedMemory> shm(
269 new base::SharedMemory(buffer.handle(), false));
270 if (!shm->Map(buffer.size())) {
271 DLOG(ERROR) << "Failed mapping shared memory.";
272 client_->NotifyError(kPlatformFailureError);
273 return;
274 }
275
276 std::unique_ptr<BitstreamBufferRef> buffer_ref(
277 new BitstreamBufferRef(buffer.id(), std::move(shm), buffer.size()));
278 encoder_thread_task_runner_->PostTask(
279 FROM_HERE,
280 base::Bind(
281 &MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
282 encoder_weak_ptr_, base::Passed(&buffer_ref)));
283 }
284
285 void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChange(
286 uint32_t bitrate,
287 uint32_t framerate) {
288 DVLOG(3) << __FUNCTION__ << ": bitrate=" << bitrate
289 << ": framerate=" << framerate;
290 DCHECK(thread_checker_.CalledOnValidThread());
291
292 encoder_thread_task_runner_->PostTask(
293 FROM_HERE, base::Bind(&MediaFoundationVideoEncodeAccelerator::
294 RequestEncodingParametersChangeTask,
295 encoder_weak_ptr_, bitrate, framerate));
296 }
297
298 void MediaFoundationVideoEncodeAccelerator::Destroy() {
299 DVLOG(3) << __FUNCTION__;
300 DCHECK(thread_checker_.CalledOnValidThread());
301
302 // Cancel all callbacks.
303 client_ptr_factory_.reset();
304
305 if (encoder_thread_.IsRunning()) {
306 encoder_thread_task_runner_->PostTask(
307 FROM_HERE,
308 base::Bind(&MediaFoundationVideoEncodeAccelerator::DestroyTask,
309 encoder_weak_ptr_));
310 encoder_thread_.Stop();
311 } else {
312 DestroyTask();
313 }
314 }
315
316 void MediaFoundationVideoEncodeAccelerator::EncodeTask(
317 const scoped_refptr<media::VideoFrame>& frame,
318 bool force_keyframe) {
319 DVLOG(3) << __FUNCTION__;
320 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
321
322 base::win::ScopedComPtr<IMFMediaBuffer> input_buffer;
323 input_sample_->GetBufferByIndex(0, input_buffer.Receive());
324 {
ananta 2016/06/28 22:50:01 newline here.
emircan 2016/07/02 00:07:37 Done.
325 MediaBufferScopedPointer scoped_buffer(input_buffer.get());
326 DCHECK(scoped_buffer.get());
327 libyuv::I420Copy(frame->visible_data(media::VideoFrame::kYPlane),
328 frame->stride(media::VideoFrame::kYPlane),
329 frame->visible_data(media::VideoFrame::kVPlane),
330 frame->stride(media::VideoFrame::kVPlane),
331 frame->visible_data(media::VideoFrame::kUPlane),
332 frame->stride(media::VideoFrame::kUPlane),
333 scoped_buffer.get(),
334 frame->stride(media::VideoFrame::kYPlane),
335 scoped_buffer.get() + u_plane_offset_,
336 frame->stride(media::VideoFrame::kUPlane),
337 scoped_buffer.get() + v_plane_offset_,
338 frame->stride(media::VideoFrame::kVPlane),
339 input_visible_size_.width(), input_visible_size_.height());
340 }
341 input_sample_->SetSampleTime(frame->timestamp().InMicroseconds() * 10);
ananta 2016/06/28 22:50:00 newline here.
emircan 2016/07/02 00:07:37 Done.
342 input_sample_->SetSampleDuration(kOneSecondInMicroseconds / frame_rate_);
343 HRESULT hr = encoder_->ProcessInput(0, input_sample_.get(), 0);
344 RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
345 DVLOG(3) << "Sent for encode " << hr;
346
347 ProcessOutput();
ananta 2016/06/28 22:50:01 Perhaps we need to check if ProcessOutput succeeds
emircan 2016/07/02 00:07:37 I answered below.
348 }
349
350 void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
ananta 2016/06/28 22:50:00 Should this function be returning void?. What happ
emircan 2016/07/02 00:07:37 We are fine if it fails. - If it says MF_E_TRANSF
351 DVLOG(3) << __FUNCTION__;
352 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
353
354 if (bitstream_buffer_queue_.empty()) {
355 DVLOG(3) << "No bitstream buffers.";
356 return;
357 }
358
359 MFT_OUTPUT_DATA_BUFFER output_data_buffer = {0};
360 output_data_buffer.dwStreamID = 0;
361 output_data_buffer.dwStatus = 0;
362 output_data_buffer.pEvents = NULL;
363 output_data_buffer.pSample = output_sample_.get();
364 DWORD status = 0;
365 HRESULT hr = encoder_->ProcessOutput(0, 1, &output_data_buffer, &status);
366 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
367 DVLOG(3) << "MF_E_TRANSFORM_NEED_MORE_INPUT";
368 return;
369 }
370 RETURN_ON_HR_FAILURE(hr, "Couldn't get encoded data", );
371 DVLOG(3) << "Got encoded data " << hr;
372
373 std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
374 buffer_ref = std::move(bitstream_buffer_queue_.front());
375 bitstream_buffer_queue_.pop_front();
376
377 base::win::ScopedComPtr<IMFMediaBuffer> output_buffer;
378 output_sample_->GetBufferByIndex(0, output_buffer.Receive());
379 DWORD size = 0;
ananta 2016/06/28 22:50:01 What happens if GetBufferByIndex fails
emircan 2016/07/02 00:07:37 Adding hr checks.
380 output_buffer->GetCurrentLength(&size);
381 {
382 MediaBufferScopedPointer scoped_buffer(output_buffer.get());
383 memcpy(buffer_ref->shm->memory(), scoped_buffer.get(), size);
384 }
385
386 const bool keyframe = MFGetAttributeUINT32(
387 output_sample_.get(), MFSampleExtension_CleanPoint, false);
388 DVLOG(3) << "We HAVE encoded data with size:" << size << " keyframe "
389 << keyframe;
390
391 client_task_runner_->PostTask(
392 FROM_HERE,
393 base::Bind(&Client::BitstreamBufferReady, client_, buffer_ref->id, size,
394 keyframe, base::Time::Now() - base::Time()));
395 ProcessOutput();
396 }
397
398 void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
399 std::unique_ptr<BitstreamBufferRef> buffer_ref) {
400 DVLOG(3) << __FUNCTION__;
401 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
402
403 bitstream_buffer_queue_.push_back(std::move(buffer_ref));
404 }
405
406 void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
407 uint32_t bitrate,
408 uint32_t framerate) {
409 DVLOG(3) << __FUNCTION__;
410 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
411
412 frame_rate_ = framerate > 1 ? framerate : 1;
413 target_bitrate_ = bitrate > 1 ? bitrate : 1;
414
415 VARIANT var;
416 var.vt = VT_UI4;
417 var.ulVal = target_bitrate_;
418 HRESULT hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
419 RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", );
420
421 base::win::ScopedComPtr<IMFMediaType> imf_output_media_type;
422 hr = MFCreateMediaType(imf_output_media_type.Receive());
423 hr &= imf_output_media_type->SetUINT32(MF_MT_AVG_BITRATE, target_bitrate_);
424 hr &= MFSetAttributeRatio(imf_output_media_type.get(), MF_MT_FRAME_RATE,
425 framerate, kMaxFrameRateDenominator);
426 }
427
428 void MediaFoundationVideoEncodeAccelerator::DestroyTask() {
429 DVLOG(3) << __FUNCTION__;
430 DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
431
432 encoder_.Release();
433 }
434
435 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698