OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/mf/mft_h264_decoder.h" | 5 #include "build/build_config.h" // For OS_WIN. |
6 | 6 |
7 #include <algorithm> | 7 #if defined(OS_WIN) |
8 #include <string> | |
9 | 8 |
10 #include <d3d9.h> | 9 #include <d3d9.h> |
10 #include <dxva2api.h> | |
11 #include <evr.h> | 11 #include <evr.h> |
12 #include <initguid.h> | 12 #include <initguid.h> |
13 #include <mfapi.h> | 13 #include <mfapi.h> |
14 #include <mferror.h> | 14 #include <mferror.h> |
15 #include <mfidl.h> | |
16 #include <shlwapi.h> | |
17 #include <wmcodecdsp.h> | 15 #include <wmcodecdsp.h> |
18 | 16 |
19 #include "base/callback.h" | 17 #include "base/time.h" |
20 #include "base/logging.h" | |
21 #include "base/message_loop.h" | 18 #include "base/message_loop.h" |
22 #include "base/scoped_comptr_win.h" | 19 #include "media/mf/mft_h264_decoder.h" |
23 #include "media/base/data_buffer.h" | |
24 #include "media/base/video_frame.h" | |
25 | 20 |
21 #pragma comment(lib, "delayimp") | |
Alpha Left Google
2010/08/24 23:03:33
What is this new lib for?
imcheng
2010/08/24 23:40:51
Not sure why it was there. Removed it.
| |
22 #pragma comment(lib, "dxva2.lib") | |
26 #pragma comment(lib, "d3d9.lib") | 23 #pragma comment(lib, "d3d9.lib") |
27 #pragma comment(lib, "dxva2.lib") | 24 #pragma comment(lib, "mf.lib") |
28 #pragma comment(lib, "evr.lib") | |
29 #pragma comment(lib, "mfuuid.lib") | |
30 #pragma comment(lib, "mfplat.lib") | 25 #pragma comment(lib, "mfplat.lib") |
26 #pragma comment(lib, "strmiids.lib") | |
Alpha Left Google
2010/08/24 23:03:33
What is this new lib for?
imcheng
2010/08/24 23:40:51
Needed for MR_BUFFER_SERVICE (getting d3d surface
| |
31 | 27 |
32 namespace media { | 28 namespace { |
33 | |
34 // Returns Media Foundation's H.264 decoder as an MFT, or NULL if not found | |
35 // (e.g. Not using Windows 7) | |
36 static IMFTransform* GetH264Decoder() { | |
37 // Use __uuidof() to avoid linking to a library just for the CLSID. | |
38 IMFTransform* dec; | |
39 HRESULT hr = CoCreateInstance(__uuidof(CMSH264DecoderMFT), NULL, | |
40 CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dec)); | |
41 if (FAILED(hr)) { | |
42 LOG(ERROR) << "CoCreateInstance failed " << std::hex << std::showbase << hr; | |
43 return NULL; | |
44 } | |
45 return dec; | |
46 } | |
47 | 29 |
48 // Creates an empty Media Foundation sample with no buffers. | 30 // Creates an empty Media Foundation sample with no buffers. |
49 static IMFSample* CreateEmptySample() { | 31 static IMFSample* CreateEmptySample() { |
50 HRESULT hr; | 32 HRESULT hr; |
51 ScopedComPtr<IMFSample> sample; | 33 ScopedComPtr<IMFSample> sample; |
52 hr = MFCreateSample(sample.Receive()); | 34 hr = MFCreateSample(sample.Receive()); |
53 if (FAILED(hr)) { | 35 if (FAILED(hr)) { |
54 LOG(ERROR) << "Unable to create an empty sample"; | 36 LOG(ERROR) << "Unable to create an empty sample"; |
55 return NULL; | 37 return NULL; |
56 } | 38 } |
57 return sample.Detach(); | 39 return sample.Detach(); |
58 } | 40 } |
59 | 41 |
60 // Creates a Media Foundation sample with one buffer of length |buffer_length| | 42 // Creates a Media Foundation sample with one buffer of length |buffer_length| |
61 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. | 43 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. |
62 // If |align| is 0, then no alignment is specified. | 44 // If |align| is 0, then no alignment is specified. |
63 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { | 45 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { |
64 CHECK_GT(buffer_length, 0); | 46 CHECK_GT(buffer_length, 0); |
65 ScopedComPtr<IMFSample> sample; | 47 ScopedComPtr<IMFSample> sample; |
66 sample.Attach(CreateEmptySample()); | 48 sample.Attach(CreateEmptySample()); |
67 if (!sample.get()) | 49 if (!sample.get()) |
68 return NULL; | 50 return NULL; |
69 ScopedComPtr<IMFMediaBuffer> buffer; | 51 ScopedComPtr<IMFMediaBuffer> buffer; |
70 HRESULT hr; | 52 HRESULT hr; |
71 if (align == 0) { | 53 if (align == 0) { |
72 // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer | 54 // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer |
73 // with the align argument being 0. | 55 // with the align argument being 0. |
74 hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); | 56 hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); |
75 } else { | 57 } else { |
76 hr = MFCreateAlignedMemoryBuffer(buffer_length, align-1, buffer.Receive()); | 58 hr = MFCreateAlignedMemoryBuffer(buffer_length, |
59 align - 1, | |
60 buffer.Receive()); | |
77 } | 61 } |
78 if (FAILED(hr)) { | 62 if (FAILED(hr)) { |
79 LOG(ERROR) << "Unable to create an empty buffer"; | 63 LOG(ERROR) << "Unable to create an empty buffer"; |
80 return NULL; | 64 return NULL; |
81 } | 65 } |
82 hr = sample->AddBuffer(buffer.get()); | 66 hr = sample->AddBuffer(buffer.get()); |
83 if (FAILED(hr)) { | 67 if (FAILED(hr)) { |
84 LOG(ERROR) << "Failed to add empty buffer to sample"; | 68 LOG(ERROR) << "Failed to add empty buffer to sample"; |
85 return NULL; | 69 return NULL; |
86 } | 70 } |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
140 CHECK(SUCCEEDED(buffer->Unlock())); | 124 CHECK(SUCCEEDED(buffer->Unlock())); |
141 hr = buffer->SetCurrentLength(size); | 125 hr = buffer->SetCurrentLength(size); |
142 if (FAILED(hr)) { | 126 if (FAILED(hr)) { |
143 LOG(ERROR) << "Failed to set current length to " << size; | 127 LOG(ERROR) << "Failed to set current length to " << size; |
144 return NULL; | 128 return NULL; |
145 } | 129 } |
146 LOG(INFO) << __FUNCTION__ << " wrote " << size << " bytes into input sample"; | 130 LOG(INFO) << __FUNCTION__ << " wrote " << size << " bytes into input sample"; |
147 return sample.Detach(); | 131 return sample.Detach(); |
148 } | 132 } |
149 | 133 |
150 // Public methods | 134 } // namespace |
135 | |
136 namespace media { | |
137 | |
138 // public methods | |
151 | 139 |
152 MftH264Decoder::MftH264Decoder(bool use_dxva) | 140 MftH264Decoder::MftH264Decoder(bool use_dxva) |
153 : read_input_callback_(NULL), | 141 : use_dxva_(use_dxva), |
154 output_avail_callback_(NULL), | 142 d3d9_(NULL), |
155 output_error_callback_(NULL), | 143 device_(NULL), |
144 device_manager_(NULL), | |
145 device_window_(NULL), | |
156 decoder_(NULL), | 146 decoder_(NULL), |
157 initialized_(false), | 147 input_stream_info_(), |
158 use_dxva_(use_dxva), | 148 output_stream_info_(), |
159 drain_message_sent_(false), | 149 state_(kUninitialized), |
160 next_frame_discontinuous_(false), | 150 event_handler_(NULL) { |
161 in_buffer_size_(0), | 151 memset(&config_, 0, sizeof(config_)); |
162 in_buffer_alignment_(0), | 152 memset(&info_, 0, sizeof(info_)); |
163 out_buffer_size_(0), | |
164 out_buffer_alignment_(0), | |
165 frames_read_(0), | |
166 frames_decoded_(0), | |
167 width_(0), | |
168 height_(0), | |
169 stride_(0), | |
170 output_format_(use_dxva ? MFVideoFormat_NV12 : MFVideoFormat_YV12) { | |
171 } | 153 } |
172 | 154 |
173 MftH264Decoder::~MftH264Decoder() { | 155 MftH264Decoder::~MftH264Decoder() { |
174 // |decoder_| has to be destroyed before the library uninitialization. | |
175 if (decoder_) | |
176 decoder_->Release(); | |
177 if (FAILED(MFShutdown())) { | |
178 LOG(WARNING) << "Warning: MF failed to shutdown"; | |
179 } | |
180 CoUninitialize(); | |
181 } | 156 } |
182 | 157 |
183 bool MftH264Decoder::Init(IDirect3DDeviceManager9* dev_manager, | 158 void MftH264Decoder::Initialize( |
184 int frame_rate_num, int frame_rate_denom, | 159 MessageLoop* message_loop, |
185 int width, int height, | 160 VideoDecodeEngine::EventHandler* event_handler, |
186 int aspect_num, int aspect_denom, | 161 const VideoCodecConfig& config) { |
187 ReadInputCallback* read_input_cb, | 162 LOG(INFO) << "MftH264Decoder::Initialize"; |
188 OutputReadyCallback* output_avail_cb, | 163 if (state_ != kUninitialized) { |
189 OutputErrorCallback* output_error_cb) { | 164 LOG(ERROR) << "Initialize: invalid state"; |
190 if (initialized_) | 165 return; |
191 return true; | |
192 if (!read_input_cb || !output_avail_cb || !output_error_cb) { | |
193 LOG(ERROR) << "Callbacks missing in Init"; | |
194 return false; | |
195 } | 166 } |
196 read_input_callback_.reset(read_input_cb); | 167 if (!message_loop || !event_handler) { |
197 output_avail_callback_.reset(output_avail_cb); | 168 LOG(ERROR) << "MftH264Decoder::Initialize: parameters cannot be NULL"; |
198 output_error_callback_.reset(output_error_cb); | 169 return; |
199 if (!InitComMfLibraries()) | 170 } |
200 return false; | |
201 if (!InitDecoder(dev_manager, frame_rate_num, frame_rate_denom, | |
202 width, height, aspect_num, aspect_denom)) | |
203 return false; | |
204 if (!GetStreamsInfoAndBufferReqs()) | |
205 return false; | |
206 if (!SendStartMessage()) | |
207 return false; | |
208 initialized_ = true; | |
209 return true; | |
210 } | |
211 | 171 |
212 static const char* const ProcessOutputStatusToCString(HRESULT hr) { | 172 config_ = config; |
213 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) | 173 event_handler_ = event_handler; |
214 return "media stream change occurred, need to set output type"; | |
215 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) | |
216 return "decoder needs more samples"; | |
217 else | |
218 return "unhandled error from ProcessOutput"; | |
219 } | |
220 | 174 |
221 void MftH264Decoder::GetOutput() { | 175 info_.provides_buffers_ = false; |
222 CHECK(initialized_); | |
223 | 176 |
224 ScopedComPtr<IMFSample> output_sample; | 177 // TODO(jiesun): Actually it is more likely an NV12 D3DSuface9. |
225 if (!use_dxva_) { | 178 // Until we had hardware composition working. |
226 // If DXVA is enabled, the decoder will allocate the sample for us. | 179 if (use_dxva_) { |
227 output_sample.Attach(CreateEmptySampleWithBuffer(out_buffer_size_, | 180 info_.stream_info_.surface_format_ = VideoFrame::YV12; |
228 out_buffer_alignment_)); | 181 info_.stream_info_.surface_type_ = VideoFrame::TYPE_SYSTEM_MEMORY; |
229 if (!output_sample.get()) { | 182 } else { |
230 LOG(ERROR) << "GetSample: failed to create empty output sample"; | 183 info_.stream_info_.surface_format_ = VideoFrame::YV12; |
231 output_error_callback_->Run(kNoMemory); | 184 info_.stream_info_.surface_type_ = VideoFrame::TYPE_SYSTEM_MEMORY; |
232 return; | |
233 } | |
234 } | 185 } |
235 MFT_OUTPUT_DATA_BUFFER output_data_buffer; | |
236 HRESULT hr; | |
237 DWORD status; | |
238 for (;;) { | |
239 output_data_buffer.dwStreamID = 0; | |
240 output_data_buffer.pSample = output_sample.get(); | |
241 output_data_buffer.dwStatus = 0; | |
242 output_data_buffer.pEvents = NULL; | |
243 hr = decoder_->ProcessOutput(0, // No flags | |
244 1, // # of out streams to pull from | |
245 &output_data_buffer, | |
246 &status); | |
247 IMFCollection* events = output_data_buffer.pEvents; | |
248 if (events) { | |
249 LOG(INFO) << "Got events from ProcessOuput, but discarding"; | |
250 events->Release(); | |
251 } | |
252 if (FAILED(hr)) { | |
253 LOG(INFO) << "ProcessOutput failed with status " << std::hex << hr | |
254 << ", meaning..." << ProcessOutputStatusToCString(hr); | |
255 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { | |
256 if (!SetDecoderOutputMediaType(output_format_)) { | |
257 LOG(ERROR) << "Failed to reset output type"; | |
258 output_error_callback_->Run(kResetOutputStreamFailed); | |
259 return; | |
260 } else { | |
261 LOG(INFO) << "Reset output type done"; | |
262 continue; | |
263 } | |
264 } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { | |
265 // If we have read everything then we should've sent a drain message | |
266 // to the MFT. If the drain message is sent but it doesn't give out | |
267 // anymore output then we know the decoder has processed everything. | |
268 if (drain_message_sent_) { | |
269 LOG(INFO) << "Drain message was already sent + no output => done"; | |
270 output_error_callback_->Run(kNoMoreOutput); | |
271 return; | |
272 } else { | |
273 if (!ReadInput()) { | |
274 LOG(INFO) << "Failed to read/process input. Sending drain message"; | |
275 if (!SendEndOfStreamMessage() || !SendDrainMessage()) { | |
276 LOG(ERROR) << "Failed to send drain message"; | |
277 output_error_callback_->Run(kNoMoreOutput); | |
278 return; | |
279 } | |
280 } | |
281 continue; | |
282 } | |
283 } else { | |
284 output_error_callback_->Run(kUnspecifiedError); | |
285 return; | |
286 } | |
287 } else { | |
288 // A decoded sample was successfully obtained. | |
289 LOG(INFO) << "Got a decoded sample from decoder"; | |
290 if (use_dxva_) { | |
291 // If dxva is enabled, we did not provide a sample to ProcessOutput, | |
292 // i.e. output_sample is NULL. | |
293 output_sample.Attach(output_data_buffer.pSample); | |
294 if (!output_sample.get()) { | |
295 LOG(ERROR) << "Output sample using DXVA is NULL - ProcessOutput did " | |
296 << "not provide it!"; | |
297 output_error_callback_->Run(kOutputSampleError); | |
298 return; | |
299 } | |
300 } | |
301 int64 timestamp, duration; | |
302 hr = output_sample->GetSampleTime(×tamp); | |
303 hr = output_sample->GetSampleDuration(&duration); | |
304 if (FAILED(hr)) { | |
305 LOG(ERROR) << "Failed to get sample duration or timestamp " | |
306 << std::hex << hr; | |
307 output_error_callback_->Run(kOutputSampleError); | |
308 return; | |
309 } | |
310 | 186 |
311 // The duration and timestamps are in 100-ns units, so divide by 10 | 187 // codec_info.stream_info_.surface_width_/height_ are initialized |
312 // to convert to microseconds. | 188 // in InitInternal(). |
313 timestamp /= 10; | 189 info_.success_ = InitInternal(); |
314 duration /= 10; | 190 if (info_.success_) { |
315 | 191 state_ = kNormal; |
316 // Sanity checks for checking if there is really something in the sample. | 192 event_handler_->OnInitializeComplete(info_); |
317 DWORD buf_count; | 193 } else { |
318 hr = output_sample->GetBufferCount(&buf_count); | 194 LOG(ERROR) << "MftH264Decoder::Initialize failed"; |
319 if (FAILED(hr)) { | |
320 LOG(ERROR) << "Failed to get buff count, hr = " << std::hex << hr; | |
321 output_error_callback_->Run(kOutputSampleError); | |
322 return; | |
323 } | |
324 if (buf_count == 0) { | |
325 LOG(ERROR) << "buf_count is 0, dropping sample"; | |
326 output_error_callback_->Run(kOutputSampleError); | |
327 return; | |
328 } | |
329 ScopedComPtr<IMFMediaBuffer> out_buffer; | |
330 hr = output_sample->GetBufferByIndex(0, out_buffer.Receive()); | |
331 if (FAILED(hr)) { | |
332 LOG(ERROR) << "Failed to get decoded output buffer"; | |
333 output_error_callback_->Run(kOutputSampleError); | |
334 return; | |
335 } | |
336 | |
337 // To obtain the data, the caller should call the Lock() method instead | |
338 // of using the data field. | |
339 // In NV12, there are only 2 planes - the Y plane, and the interleaved UV | |
340 // plane. Both have the same strides. | |
341 uint8* null_data[3] = { NULL, NULL, NULL }; | |
342 int32 uv_stride = output_format_ == MFVideoFormat_NV12 ? stride_ | |
343 : stride_ / 2; | |
344 int32 strides[3] = { stride_, uv_stride, uv_stride }; | |
345 scoped_refptr<VideoFrame> decoded_frame; | |
346 VideoFrame::CreateFrameExternal( | |
347 use_dxva_ ? VideoFrame::TYPE_DIRECT3DSURFACE : | |
348 VideoFrame::TYPE_MFBUFFER, | |
349 output_format_ == MFVideoFormat_NV12 ? VideoFrame::NV12 | |
350 : VideoFrame::YV12, | |
351 width_, | |
352 height_, | |
353 2, | |
354 null_data, | |
355 strides, | |
356 base::TimeDelta::FromMicroseconds(timestamp), | |
357 base::TimeDelta::FromMicroseconds(duration), | |
358 out_buffer.Detach(), | |
359 &decoded_frame); | |
360 CHECK(decoded_frame.get()); | |
361 frames_decoded_++; | |
362 output_avail_callback_->Run(decoded_frame); | |
363 return; | |
364 } | |
365 } | 195 } |
366 } | 196 } |
367 | 197 |
368 bool MftH264Decoder::Flush() { | 198 void MftH264Decoder::Uninitialize() { |
369 CHECK(initialized_); | 199 LOG(INFO) << "MftH264Decoder::Uninitialize"; |
370 HRESULT hr = decoder_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL); | 200 if (state_ == kUninitialized) { |
371 if (FAILED(hr)) { | 201 LOG(ERROR) << "Uninitialize: invalid state"; |
372 LOG(ERROR) << "Failed to send the flush message to decoder"; | 202 return; |
373 return false; | |
374 } | 203 } |
375 next_frame_discontinuous_ = true; | 204 |
376 return true; | 205 // TODO(imcheng): |
206 // Cannot shutdown COM libraries here because the COM objects still needs | |
207 // to be Release()'ed. We can explicitly release them here, or move the | |
208 // uninitialize to GpuVideoService... | |
209 if (device_window_) | |
210 DestroyWindow(device_window_); | |
211 decoder_.Release(); | |
212 device_manager_.Release(); | |
213 device_.Release(); | |
214 d3d9_.Release(); | |
215 ShutdownComLibraries(); | |
216 state_ = kUninitialized; | |
217 event_handler_->OnUninitializeComplete(); | |
377 } | 218 } |
378 | 219 |
379 // Private methods | 220 void MftH264Decoder::Flush() { |
221 LOG(INFO) << "MftH264Decoder::Flush"; | |
222 if (state_ != kNormal) { | |
223 LOG(ERROR) << "Flush: invalid state"; | |
224 return; | |
225 } | |
226 state_ = kFlushing; | |
227 if (!SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH)) { | |
228 LOG(WARNING) << "MftH264Decoder::Flush failed to send message"; | |
229 } | |
230 state_ = kNormal; | |
231 event_handler_->OnFlushComplete(); | |
232 } | |
380 | 233 |
381 bool MftH264Decoder::InitComMfLibraries() { | 234 void MftH264Decoder::Seek() { |
235 if (state_ != kNormal) { | |
236 LOG(ERROR) << "Seek: invalid state"; | |
237 return; | |
238 } | |
239 LOG(INFO) << "MftH264Decoder::Seek"; | |
240 // Seek not implemented. | |
241 event_handler_->OnSeekComplete(); | |
242 } | |
243 | |
244 void MftH264Decoder::EmptyThisBuffer(scoped_refptr<Buffer> buffer) { | |
245 LOG(INFO) << "MftH264Decoder::EmptyThisBuffer"; | |
246 if (state_ == kUninitialized) { | |
247 LOG(ERROR) << "EmptyThisBuffer: invalid state"; | |
248 } | |
249 ScopedComPtr<IMFSample> sample; | |
250 if (!buffer->IsEndOfStream()) { | |
251 sample.Attach( | |
252 CreateInputSample(buffer->GetData(), | |
253 buffer->GetDataSize(), | |
254 buffer->GetTimestamp().InMicroseconds() * 10, | |
255 buffer->GetDuration().InMicroseconds() * 10, | |
256 input_stream_info_.cbSize, | |
257 input_stream_info_.cbAlignment)); | |
258 if (!sample.get()) { | |
259 LOG(ERROR) << "Failed to create an input sample"; | |
260 } else { | |
261 if (FAILED(decoder_->ProcessInput(0, sample.get(), 0))) { | |
262 event_handler_->OnError(); | |
263 } | |
264 } | |
265 } else { | |
266 if (state_ != MftH264Decoder::kEosDrain) { | |
267 // End of stream, send drain messages. | |
268 if (!SendMFTMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM) || | |
269 !SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN)) { | |
270 LOG(ERROR) << "Failed to send EOS / drain messages to MFT"; | |
271 event_handler_->OnError(); | |
272 } else { | |
273 state_ = MftH264Decoder::kEosDrain; | |
274 } | |
275 } | |
276 } | |
277 DoDecode(); | |
278 } | |
279 | |
280 void MftH264Decoder::FillThisBuffer(scoped_refptr<VideoFrame> frame) { | |
281 LOG(INFO) << "MftH264Decoder::FillThisBuffer"; | |
282 if (state_ == kUninitialized) { | |
283 LOG(ERROR) << "FillThisBuffer: invalid state"; | |
284 return; | |
285 } | |
286 scoped_refptr<Buffer> buffer; | |
287 event_handler_->OnEmptyBufferCallback(buffer); | |
288 } | |
289 | |
290 // private methods | |
291 | |
292 // static | |
293 bool MftH264Decoder::StartupComLibraries() { | |
382 HRESULT hr; | 294 HRESULT hr; |
383 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); | 295 hr = CoInitializeEx(NULL, |
296 COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); | |
384 if (FAILED(hr)) { | 297 if (FAILED(hr)) { |
385 LOG(ERROR) << "CoInit fail"; | 298 LOG(ERROR) << "CoInit fail"; |
386 return false; | 299 return false; |
387 } | 300 } |
301 | |
388 hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); | 302 hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); |
389 if (FAILED(hr)) { | 303 if (FAILED(hr)) { |
390 LOG(ERROR) << "MFStartup fail"; | 304 LOG(ERROR) << "MFStartup fail"; |
391 CoUninitialize(); | 305 CoUninitialize(); |
392 return false; | 306 return false; |
393 } | 307 } |
394 return true; | 308 return true; |
395 } | 309 } |
396 | 310 |
397 bool MftH264Decoder::InitDecoder(IDirect3DDeviceManager9* dev_manager, | 311 // static |
398 int frame_rate_num, int frame_rate_denom, | 312 void MftH264Decoder::ShutdownComLibraries() { |
399 int width, int height, | 313 HRESULT hr; |
400 int aspect_num, int aspect_denom) { | 314 hr = MFShutdown(); |
401 decoder_ = GetH264Decoder(); | 315 if (FAILED(hr)) { |
402 if (!decoder_) | 316 LOG(WARNING) << "Warning: MF failed to shutdown"; |
317 } | |
318 CoUninitialize(); | |
319 } | |
320 | |
321 bool MftH264Decoder::CreateD3DDevManager() { | |
322 d3d9_.Attach(Direct3DCreate9(D3D_SDK_VERSION)); | |
323 if (d3d9_.get() == NULL) { | |
324 LOG(ERROR) << "Failed to create D3D9"; | |
403 return false; | 325 return false; |
404 if (use_dxva_ && !SetDecoderD3d9Manager(dev_manager)) | 326 } |
327 static const TCHAR windowName[] = TEXT("MFT Decoder Hidden Window"); | |
328 static const TCHAR className[] = TEXT("STATIC"); | |
329 device_window_ = CreateWindowEx(WS_EX_NOACTIVATE, | |
330 className, | |
331 windowName, | |
332 WS_DISABLED | WS_POPUP, | |
333 0, 0, 1, 1, | |
334 HWND_MESSAGE, | |
335 NULL, | |
336 GetModuleHandle(NULL), | |
337 NULL); | |
338 CHECK(device_window_); | |
339 | |
340 D3DPRESENT_PARAMETERS present_params = {0}; | |
341 present_params.BackBufferWidth = 1; | |
342 present_params.BackBufferHeight = 1; | |
343 present_params.BackBufferFormat = D3DFMT_UNKNOWN; | |
344 present_params.BackBufferCount = 1; | |
345 present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; | |
346 present_params.hDeviceWindow = device_window_; | |
347 present_params.Windowed = TRUE; | |
348 present_params.Flags = D3DPRESENTFLAG_VIDEO; | |
349 present_params.FullScreen_RefreshRateInHz = 0; | |
350 present_params.PresentationInterval = 0; | |
351 | |
352 // D3DCREATE_HARDWARE_VERTEXPROCESSING specifies hardware vertex processing. | |
353 // (Is it even needed for just video decoding?) | |
354 HRESULT hr = d3d9_->CreateDevice(D3DADAPTER_DEFAULT, | |
355 D3DDEVTYPE_HAL, | |
356 device_window_, | |
357 D3DCREATE_HARDWARE_VERTEXPROCESSING, | |
358 &present_params, | |
359 device_.Receive()); | |
360 if (FAILED(hr)) { | |
361 LOG(ERROR) << "Failed to create D3D Device"; | |
405 return false; | 362 return false; |
406 if (!SetDecoderMediaTypes(frame_rate_num, frame_rate_denom, | 363 } |
407 width, height, | 364 |
408 aspect_num, aspect_denom)) { | 365 UINT dev_manager_reset_token = 0; |
366 hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, | |
367 device_manager_.Receive()); | |
368 if (FAILED(hr)) { | |
369 LOG(ERROR) << "Couldn't create D3D Device manager"; | |
370 return false; | |
371 } | |
372 | |
373 hr = device_manager_->ResetDevice(device_.get(), | |
374 dev_manager_reset_token); | |
375 if (FAILED(hr)) { | |
376 LOG(ERROR) << "Failed to set device to device manager"; | |
409 return false; | 377 return false; |
410 } | 378 } |
411 return true; | 379 return true; |
412 } | 380 } |
413 | 381 |
414 bool MftH264Decoder::SetDecoderD3d9Manager( | 382 bool MftH264Decoder::InitInternal() { |
415 IDirect3DDeviceManager9* dev_manager) { | 383 if (!StartupComLibraries()) |
416 if (!use_dxva_) { | 384 return false; |
417 LOG(ERROR) << "SetDecoderD3d9Manager should only be called if DXVA is " | 385 if (use_dxva_ && !CreateD3DDevManager()) |
418 << "enabled"; | 386 return false; |
387 if (!InitDecoder()) | |
388 return false; | |
389 if (!GetStreamsInfoAndBufferReqs()) | |
390 return false; | |
391 return SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING); | |
392 } | |
393 | |
394 bool MftH264Decoder::InitDecoder() { | |
395 // TODO(jiesun): use MFEnum to get decoder CLSID. | |
396 HRESULT hr = CoCreateInstance(__uuidof(CMSH264DecoderMFT), | |
397 NULL, | |
398 CLSCTX_INPROC_SERVER, | |
399 __uuidof(IMFTransform), | |
400 reinterpret_cast<void**>(decoder_.Receive())); | |
401 if (FAILED(hr) || !decoder_.get()) { | |
402 LOG(ERROR) << "CoCreateInstance failed " << std::hex << std::showbase << hr; | |
419 return false; | 403 return false; |
420 } | 404 } |
421 if (!dev_manager) { | 405 |
422 LOG(ERROR) << "dev_manager cannot be NULL"; | 406 if (!CheckDecoderDxvaSupport()) |
407 return false; | |
408 | |
409 if (use_dxva_) { | |
410 hr = decoder_->ProcessMessage( | |
411 MFT_MESSAGE_SET_D3D_MANAGER, | |
412 reinterpret_cast<ULONG_PTR>(device_manager_.get())); | |
413 if (FAILED(hr)) { | |
414 LOG(ERROR) << "Failed to set D3D9 device to decoder"; | |
415 return false; | |
416 } | |
417 } | |
418 | |
419 return SetDecoderMediaTypes(); | |
420 } | |
421 | |
422 bool MftH264Decoder::CheckDecoderDxvaSupport() { | |
423 ScopedComPtr<IMFAttributes> attributes; | |
424 HRESULT hr = decoder_->GetAttributes(attributes.Receive()); | |
425 if (FAILED(hr)) { | |
426 LOG(ERROR) << "Unlock: Failed to get attributes, hr = " | |
427 << std::hex << std::showbase << hr; | |
423 return false; | 428 return false; |
424 } | 429 } |
425 HRESULT hr; | 430 |
426 hr = decoder_->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, | 431 UINT32 dxva; |
427 reinterpret_cast<ULONG_PTR>(dev_manager)); | 432 hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); |
428 if (FAILED(hr)) { | 433 if (FAILED(hr) || !dxva) { |
429 LOG(ERROR) << "Failed to set D3D9 device to decoder"; | 434 LOG(ERROR) << "Failed to get DXVA attr, hr = " |
435 << std::hex << std::showbase << hr | |
436 << "this might not be the right decoder."; | |
430 return false; | 437 return false; |
431 } | 438 } |
432 return true; | 439 return true; |
433 } | 440 } |
434 | 441 |
435 bool MftH264Decoder::SetDecoderMediaTypes(int frame_rate_num, | 442 bool MftH264Decoder::SetDecoderMediaTypes() { |
436 int frame_rate_denom, | 443 if (!SetDecoderInputMediaType()) |
437 int width, int height, | |
438 int aspect_num, int aspect_denom) { | |
439 DCHECK(decoder_); | |
440 if (!SetDecoderInputMediaType(frame_rate_num, frame_rate_denom, | |
441 width, height, | |
442 aspect_num, aspect_denom)) | |
443 return false; | 444 return false; |
444 if (!SetDecoderOutputMediaType(output_format_)) { | 445 return SetDecoderOutputMediaType(use_dxva_ ? MFVideoFormat_NV12 |
445 return false; | 446 : MFVideoFormat_YV12); |
446 } | |
447 return true; | |
448 } | 447 } |
449 | 448 |
450 bool MftH264Decoder::SetDecoderInputMediaType(int frame_rate_num, | 449 bool MftH264Decoder::SetDecoderInputMediaType() { |
451 int frame_rate_denom, | |
452 int width, int height, | |
453 int aspect_num, | |
454 int aspect_denom) { | |
455 ScopedComPtr<IMFMediaType> media_type; | 450 ScopedComPtr<IMFMediaType> media_type; |
456 HRESULT hr; | 451 HRESULT hr = MFCreateMediaType(media_type.Receive()); |
457 hr = MFCreateMediaType(media_type.Receive()); | |
458 if (FAILED(hr)) { | 452 if (FAILED(hr)) { |
459 LOG(ERROR) << "Failed to create empty media type object"; | 453 LOG(ERROR) << "Failed to create empty media type object"; |
460 return false; | 454 return false; |
461 } | 455 } |
456 | |
462 hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); | 457 hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); |
463 if (FAILED(hr)) { | 458 if (FAILED(hr)) { |
464 LOG(ERROR) << "SetGUID for major type failed"; | 459 LOG(ERROR) << "SetGUID for major type failed"; |
465 return false; | 460 return false; |
466 } | 461 } |
462 | |
467 hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); | 463 hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); |
468 if (FAILED(hr)) { | 464 if (FAILED(hr)) { |
469 LOG(ERROR) << "SetGUID for subtype failed"; | 465 LOG(ERROR) << "SetGUID for subtype failed"; |
470 return false; | 466 return false; |
471 } | 467 } |
472 | 468 |
473 // Provide additional info to the decoder to avoid a format change during | |
474 // streaming. | |
475 if (frame_rate_num > 0 && frame_rate_denom > 0) { | |
476 hr = MFSetAttributeRatio(media_type.get(), MF_MT_FRAME_RATE, | |
477 frame_rate_num, frame_rate_denom); | |
478 if (FAILED(hr)) { | |
479 LOG(ERROR) << "Failed to set frame rate"; | |
480 return false; | |
481 } | |
482 } | |
483 if (width > 0 && height > 0) { | |
484 hr = MFSetAttributeSize(media_type.get(), MF_MT_FRAME_SIZE, width, height); | |
485 if (FAILED(hr)) { | |
486 LOG(ERROR) << "Failed to set frame size"; | |
487 return false; | |
488 } | |
489 } | |
490 | |
491 // TODO(imcheng): Not sure about this, but this is the recommended value by | |
492 // MSDN. | |
493 hr = media_type->SetUINT32(MF_MT_INTERLACE_MODE, | |
494 MFVideoInterlace_MixedInterlaceOrProgressive); | |
495 if (FAILED(hr)) { | |
496 LOG(ERROR) << "Failed to set interlace mode"; | |
497 return false; | |
498 } | |
499 if (aspect_num > 0 && aspect_denom > 0) { | |
500 hr = MFSetAttributeRatio(media_type.get(), MF_MT_PIXEL_ASPECT_RATIO, | |
501 aspect_num, aspect_denom); | |
502 if (FAILED(hr)) { | |
503 LOG(ERROR) << "Failed to get aspect ratio"; | |
504 return false; | |
505 } | |
506 } | |
507 hr = decoder_->SetInputType(0, media_type.get(), 0); // No flags | 469 hr = decoder_->SetInputType(0, media_type.get(), 0); // No flags |
508 if (FAILED(hr)) { | 470 if (FAILED(hr)) { |
509 LOG(ERROR) << "Failed to set decoder's input type"; | 471 LOG(ERROR) << "Failed to set decoder's input type"; |
510 return false; | 472 return false; |
511 } | 473 } |
474 | |
512 return true; | 475 return true; |
513 } | 476 } |
514 | 477 |
515 bool MftH264Decoder::SetDecoderOutputMediaType(const GUID subtype) { | 478 bool MftH264Decoder::SetDecoderOutputMediaType(const GUID subtype) { |
516 DWORD i = 0; | 479 DWORD i = 0; |
517 IMFMediaType* out_media_type; | 480 IMFMediaType* out_media_type; |
518 bool found = false; | 481 bool found = false; |
519 while (SUCCEEDED(decoder_->GetOutputAvailableType(0, i, &out_media_type))) { | 482 while (SUCCEEDED(decoder_->GetOutputAvailableType(0, i, &out_media_type))) { |
520 GUID out_subtype; | 483 GUID out_subtype; |
521 HRESULT hr; | 484 HRESULT hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype); |
522 hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype); | |
523 if (FAILED(hr)) { | 485 if (FAILED(hr)) { |
524 LOG(ERROR) << "Failed to GetGUID() on GetOutputAvailableType() " << i; | 486 LOG(ERROR) << "Failed to GetGUID() on GetOutputAvailableType() " << i; |
525 out_media_type->Release(); | 487 out_media_type->Release(); |
526 continue; | 488 continue; |
527 } | 489 } |
528 if (out_subtype == subtype) { | 490 if (out_subtype == subtype) { |
529 LOG(INFO) << "|subtype| is at index " | |
530 << i << " in GetOutputAvailableType()"; | |
531 hr = decoder_->SetOutputType(0, out_media_type, 0); // No flags | 491 hr = decoder_->SetOutputType(0, out_media_type, 0); // No flags |
532 hr = MFGetAttributeSize(out_media_type, MF_MT_FRAME_SIZE, | 492 hr = MFGetAttributeSize(out_media_type, MF_MT_FRAME_SIZE, |
533 reinterpret_cast<UINT32*>(&width_), | 493 reinterpret_cast<UINT32*>(&info_.stream_info_.surface_width_), |
534 reinterpret_cast<UINT32*>(&height_)); | 494 reinterpret_cast<UINT32*>(&info_.stream_info_.surface_height_)); |
535 hr = MFGetStrideForBitmapInfoHeader(output_format_.Data1, | 495 config_.width_ = info_.stream_info_.surface_width_; |
536 width_, | 496 config_.height_ = info_.stream_info_.surface_height_; |
537 reinterpret_cast<LONG*>(&stride_)); | |
538 if (FAILED(hr)) { | 497 if (FAILED(hr)) { |
539 LOG(ERROR) << "Failed to SetOutputType to |subtype| or obtain " | 498 LOG(ERROR) << "Failed to SetOutputType to |subtype| or obtain " |
540 << "width/height/stride " << std::hex << hr; | 499 << "width/height " << std::hex << hr; |
541 } else { | 500 } else { |
542 found = true; | |
543 out_media_type->Release(); | 501 out_media_type->Release(); |
544 break; | 502 return true; |
545 } | 503 } |
546 } | 504 } |
547 i++; | 505 i++; |
548 out_media_type->Release(); | 506 out_media_type->Release(); |
549 } | 507 } |
550 if (!found) { | 508 return false; |
551 LOG(ERROR) << "|subtype| was not found in GetOutputAvailableType()"; | |
552 return false; | |
553 } | |
554 return true; | |
555 } | 509 } |
556 | 510 |
557 bool MftH264Decoder::SendStartMessage() { | 511 bool MftH264Decoder::SendMFTMessage(MFT_MESSAGE_TYPE msg) { |
558 HRESULT hr; | 512 HRESULT hr = decoder_->ProcessMessage(msg, NULL); |
559 hr = decoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL); | 513 return SUCCEEDED(hr); |
560 if (FAILED(hr)) { | |
561 LOG(ERROR) << "Process start message failed, hr = " | |
562 << std::hex << std::showbase << hr; | |
563 return false; | |
564 } else { | |
565 LOG(INFO) << "Sent a message to decoder to indicate start of stream"; | |
566 return true; | |
567 } | |
568 } | 514 } |
569 | 515 |
570 // Prints out info about the input/output streams, gets the minimum buffer sizes | 516 // Prints out info about the input/output streams, gets the minimum buffer sizes |
571 // for input and output samples. | 517 // for input and output samples. |
572 // The MFT will not allocate buffer for neither input nor output, so we have | 518 // The MFT will not allocate buffer for neither input nor output, so we have |
573 // to do it ourselves and make sure they're the correct size. | 519 // to do it ourselves and make sure they're the correct size. |
574 // Exception is when dxva is enabled, the decoder will allocate output. | 520 // Exception is when dxva is enabled, the decoder will allocate output. |
575 bool MftH264Decoder::GetStreamsInfoAndBufferReqs() { | 521 bool MftH264Decoder::GetStreamsInfoAndBufferReqs() { |
576 DCHECK(decoder_); | 522 HRESULT hr = decoder_->GetInputStreamInfo(0, &input_stream_info_); |
577 HRESULT hr; | |
578 MFT_INPUT_STREAM_INFO input_stream_info; | |
579 hr = decoder_->GetInputStreamInfo(0, &input_stream_info); | |
580 if (FAILED(hr)) { | 523 if (FAILED(hr)) { |
581 LOG(ERROR) << "Failed to get input stream info"; | 524 LOG(ERROR) << "Failed to get input stream info"; |
582 return false; | 525 return false; |
583 } | 526 } |
584 LOG(INFO) << "Input stream info: "; | 527 LOG(INFO) << "Input stream info: "; |
585 LOG(INFO) << "Max latency: " << input_stream_info.hnsMaxLatency; | 528 LOG(INFO) << "Max latency: " << input_stream_info_.hnsMaxLatency; |
586 | 529 |
587 // There should be three flags, one for requiring a whole frame be in a | 530 // There should be three flags, one for requiring a whole frame be in a |
588 // single sample, one for requiring there be one buffer only in a single | 531 // single sample, one for requiring there be one buffer only in a single |
589 // sample, and one that specifies a fixed sample size. (as in cbSize) | 532 // sample, and one that specifies a fixed sample size. (as in cbSize) |
590 LOG(INFO) << "Flags: " | 533 LOG(INFO) << "Flags: " |
591 << std::hex << std::showbase << input_stream_info.dwFlags; | 534 << std::hex << std::showbase << input_stream_info_.dwFlags; |
592 CHECK_EQ(input_stream_info.dwFlags, 0x7u); | 535 CHECK_EQ(input_stream_info_.dwFlags, 0x7u); |
593 LOG(INFO) << "Min buffer size: " << input_stream_info.cbSize; | 536 LOG(INFO) << "Min buffer size: " << input_stream_info_.cbSize; |
594 LOG(INFO) << "Max lookahead: " << input_stream_info.cbMaxLookahead; | 537 LOG(INFO) << "Max lookahead: " << input_stream_info_.cbMaxLookahead; |
595 LOG(INFO) << "Alignment: " << input_stream_info.cbAlignment; | 538 LOG(INFO) << "Alignment: " << input_stream_info_.cbAlignment; |
596 in_buffer_alignment_ = input_stream_info.cbAlignment; | 539 |
597 in_buffer_size_ = input_stream_info.cbSize; | 540 hr = decoder_->GetOutputStreamInfo(0, &output_stream_info_); |
598 | |
599 MFT_OUTPUT_STREAM_INFO output_stream_info; | |
600 hr = decoder_->GetOutputStreamInfo(0, &output_stream_info); | |
601 if (FAILED(hr)) { | 541 if (FAILED(hr)) { |
602 LOG(ERROR) << "Failed to get output stream info"; | 542 LOG(ERROR) << "Failed to get output stream info"; |
603 return false; | 543 return false; |
604 } | 544 } |
605 LOG(INFO) << "Output stream info: "; | 545 LOG(INFO) << "Output stream info: "; |
606 | |
607 // The flags here should be the same and mean the same thing, except when | 546 // The flags here should be the same and mean the same thing, except when |
608 // DXVA is enabled, there is an extra 0x100 flag meaning decoder will | 547 // DXVA is enabled, there is an extra 0x100 flag meaning decoder will |
609 // allocate its own sample. | 548 // allocate its own sample. |
610 LOG(INFO) << "Flags: " | 549 LOG(INFO) << "Flags: " |
611 << std::hex << std::showbase << output_stream_info.dwFlags; | 550 << std::hex << std::showbase << output_stream_info_.dwFlags; |
612 CHECK_EQ(output_stream_info.dwFlags, use_dxva_ ? 0x107u : 0x7u); | 551 CHECK_EQ(output_stream_info_.dwFlags, use_dxva_ ? 0x107u : 0x7u); |
613 LOG(INFO) << "Min buffer size: " << output_stream_info.cbSize; | 552 LOG(INFO) << "Min buffer size: " << output_stream_info_.cbSize; |
614 LOG(INFO) << "Alignment: " << output_stream_info.cbAlignment; | 553 LOG(INFO) << "Alignment: " << output_stream_info_.cbAlignment; |
615 out_buffer_alignment_ = output_stream_info.cbAlignment; | |
616 out_buffer_size_ = output_stream_info.cbSize; | |
617 | 554 |
618 return true; | 555 return true; |
619 } | 556 } |
620 | 557 |
621 bool MftH264Decoder::ReadInput() { | 558 bool MftH264Decoder::DoDecode() { |
622 scoped_refptr<DataBuffer> input; | 559 if (state_ != kNormal && state_ != kEosDrain) { |
623 read_input_callback_->Run(&input); | 560 LOG(ERROR) << "DoDecode: not in normal or drain state"; |
624 if (!input.get() || input->IsEndOfStream()) { | |
625 LOG(INFO) << "No more input"; | |
626 return false; | 561 return false; |
562 } | |
563 scoped_refptr<VideoFrame> frame; | |
564 ScopedComPtr<IMFSample> output_sample; | |
565 if (!use_dxva_) { | |
566 output_sample.Attach( | |
567 CreateEmptySampleWithBuffer(output_stream_info_.cbSize, | |
568 output_stream_info_.cbAlignment)); | |
569 if (!output_sample.get()) { | |
570 LOG(ERROR) << "GetSample: failed to create empty output sample"; | |
571 event_handler_->OnError(); | |
572 return false; | |
573 } | |
574 } | |
575 MFT_OUTPUT_DATA_BUFFER output_data_buffer; | |
576 memset(&output_data_buffer, 0, sizeof(output_data_buffer)); | |
577 output_data_buffer.dwStreamID = 0; | |
578 output_data_buffer.pSample = output_sample; | |
579 | |
580 DWORD status; | |
581 HRESULT hr = decoder_->ProcessOutput(0, // No flags | |
582 1, // # of out streams to pull from | |
583 &output_data_buffer, | |
584 &status); | |
585 | |
586 IMFCollection* events = output_data_buffer.pEvents; | |
587 if (events != NULL) { | |
588 LOG(INFO) << "Got events from ProcessOuput, but discarding"; | |
589 events->Release(); | |
590 } | |
591 | |
592 if (FAILED(hr)) { | |
593 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { | |
594 hr = SetDecoderOutputMediaType(use_dxva_ ? MFVideoFormat_NV12 | |
595 : MFVideoFormat_YV12); | |
596 if (SUCCEEDED(hr)) { | |
597 event_handler_->OnFormatChange(info_.stream_info_); | |
598 return true; | |
599 } else { | |
600 event_handler_->OnError(); | |
601 return false; | |
602 } | |
603 } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { | |
604 if (state_ == kEosDrain) { | |
605 // No more output from the decoder. Notify EOS and stop playback. | |
606 scoped_refptr<VideoFrame> frame; | |
607 VideoFrame::CreateEmptyFrame(&frame); | |
608 event_handler_->OnFillBufferCallback(frame); | |
609 state_ = MftH264Decoder::kStopped; | |
610 return false; | |
611 } | |
612 return true; | |
613 } else { | |
614 LOG(ERROR) << "Unhandled error in DoDecode()"; | |
615 state_ = MftH264Decoder::kStopped; | |
616 event_handler_->OnError(); | |
617 return false; | |
618 } | |
619 } | |
620 | |
621 // We succeeded in getting an output sample. | |
622 if (use_dxva_) { | |
623 // For DXVA we didn't provide the sample, i.e. output_sample was NULL. | |
624 output_sample.Attach(output_data_buffer.pSample); | |
625 } | |
626 if (!output_sample.get()) { | |
627 LOG(ERROR) << "ProcessOutput succeeded, but did not get a sample back"; | |
628 event_handler_->OnError(); | |
629 return true; | |
630 } | |
631 | |
632 int64 timestamp = 0, duration = 0; | |
633 if (FAILED(output_sample->GetSampleTime(×tamp)) || | |
634 FAILED(output_sample->GetSampleDuration(&duration))) { | |
635 LOG(WARNING) << "Failed to get timestamp/duration from output"; | |
636 } | |
637 | |
638 // The duration and timestamps are in 100-ns units, so divide by 10 | |
639 // to convert to microseconds. | |
640 timestamp /= 10; | |
641 duration /= 10; | |
642 | |
643 // Sanity checks for checking if there is really something in the sample. | |
644 DWORD buf_count; | |
645 hr = output_sample->GetBufferCount(&buf_count); | |
646 if (FAILED(hr) || buf_count != 1) { | |
647 LOG(ERROR) << "Failed to get buffer count, or buffer count mismatch"; | |
648 return true; | |
649 } | |
650 | |
651 ScopedComPtr<IMFMediaBuffer> output_buffer; | |
652 hr = output_sample->GetBufferByIndex(0, output_buffer.Receive()); | |
653 if (FAILED(hr)) { | |
654 LOG(ERROR) << "Failed to get buffer from sample"; | |
655 return true; | |
656 } | |
657 | |
658 VideoFrame::CreateFrame(info_.stream_info_.surface_format_, | |
659 info_.stream_info_.surface_width_, | |
660 info_.stream_info_.surface_height_, | |
661 base::TimeDelta::FromMicroseconds(timestamp), | |
662 base::TimeDelta::FromMicroseconds(duration), | |
663 &frame); | |
664 if (!frame.get()) { | |
665 LOG(ERROR) << "Failed to allocate video frame"; | |
666 event_handler_->OnError(); | |
667 return true; | |
668 } | |
669 | |
670 if (use_dxva_) { | |
671 // temporary until we figure out how to send a D3D9 surface handle. | |
672 ScopedComPtr<IDirect3DSurface9> surface; | |
673 hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, | |
674 IID_PPV_ARGS(surface.Receive())); | |
675 if (FAILED(hr)) | |
676 return true; | |
677 | |
678 // TODO(imcheng): | |
679 // This is causing some problems (LockRect does not work always). | |
680 // We won't need this when we figure out how to use the d3d | |
681 // surface directly. | |
682 // NV12 to YV12 | |
683 D3DLOCKED_RECT d3dlocked_rect; | |
684 hr = surface->LockRect(&d3dlocked_rect, NULL, D3DLOCK_READONLY); | |
685 if (FAILED(hr)) { | |
686 LOG(ERROR) << "LockRect"; | |
687 return true; | |
688 } | |
689 D3DSURFACE_DESC desc; | |
690 hr = surface->GetDesc(&desc); | |
691 if (FAILED(hr)) { | |
692 LOG(ERROR) << "GetDesc"; | |
693 CHECK(SUCCEEDED(surface->UnlockRect())); | |
694 return true; | |
695 } | |
696 | |
697 uint32 src_stride = d3dlocked_rect.Pitch; | |
698 uint32 dst_stride = config_.width_; | |
699 uint8* src_y = static_cast<uint8*>(d3dlocked_rect.pBits); | |
700 uint8* src_uv = src_y + src_stride * desc.Height; | |
701 uint8* dst_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); | |
702 uint8* dst_u = static_cast<uint8*>(frame->data(VideoFrame::kVPlane)); | |
703 uint8* dst_v = static_cast<uint8*>(frame->data(VideoFrame::kUPlane)); | |
704 | |
705 for (int y = 0; y < config_.height_; ++y) { | |
706 for (int x = 0; x < config_.width_; ++x) { | |
707 dst_y[x] = src_y[x]; | |
708 if (!(y & 1)) { | |
709 if (x & 1) | |
710 dst_v[x>>1] = src_uv[x]; | |
711 else | |
712 dst_u[x>>1] = src_uv[x]; | |
713 } | |
714 } | |
715 dst_y += dst_stride; | |
716 src_y += src_stride; | |
717 if (!(y & 1)) { | |
718 src_uv += src_stride; | |
719 dst_v += dst_stride >> 1; | |
720 dst_u += dst_stride >> 1; | |
721 } | |
722 } | |
723 CHECK(SUCCEEDED(surface->UnlockRect())); | |
627 } else { | 724 } else { |
628 // We read an input stream, we can feed it into the decoder. | 725 // Not DXVA. |
629 return SendInput(input->GetData(), input->GetDataSize(), | 726 uint8* src_y; |
630 input->GetTimestamp().InMicroseconds() * 10, | 727 DWORD max_length, current_length; |
631 input->GetDuration().InMicroseconds() * 10); | 728 HRESULT hr = output_buffer->Lock(&src_y, &max_length, ¤t_length); |
632 } | 729 if (FAILED(hr)) |
633 } | 730 return true; |
634 | 731 uint8* dst_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); |
635 bool MftH264Decoder::SendInput(const uint8* data, int size, int64 timestamp, | 732 |
636 int64 duration) { | 733 memcpy(dst_y, src_y, current_length); |
637 CHECK(initialized_); | 734 CHECK(SUCCEEDED(output_buffer->Unlock())); |
638 CHECK(data); | 735 } |
639 CHECK_GT(size, 0); | 736 // TODO(jiesun): non-System memory case |
640 | 737 event_handler_->OnFillBufferCallback(frame); |
641 bool current_frame_discontinuous = next_frame_discontinuous_; | |
642 next_frame_discontinuous_ = true; | |
643 | |
644 if (drain_message_sent_) { | |
645 LOG(ERROR) << "Drain message was already sent, but trying to send more " | |
646 << "input to decoder"; | |
647 return false; | |
648 } | |
649 ScopedComPtr<IMFSample> sample; | |
650 sample.Attach(CreateInputSample(data, size, timestamp, duration, | |
651 in_buffer_size_, in_buffer_alignment_)); | |
652 if (!sample.get()) { | |
653 LOG(ERROR) << "Failed to convert input stream to sample"; | |
654 return false; | |
655 } | |
656 HRESULT hr; | |
657 if (current_frame_discontinuous) { | |
658 hr = sample->SetUINT32(MFSampleExtension_Discontinuity, TRUE); | |
659 if (FAILED(hr)) { | |
660 LOG(ERROR) << "Failed to set sample discontinuity " << std::hex << hr; | |
661 } | |
662 } | |
663 hr = decoder_->ProcessInput(0, sample.get(), 0); | |
664 if (FAILED(hr)) { | |
665 LOG(ERROR) << "Failed to ProcessInput, hr = " << std::hex << hr; | |
666 return false; | |
667 } | |
668 frames_read_++; | |
669 next_frame_discontinuous_ = false; | |
670 return true; | 738 return true; |
671 } | 739 } |
672 | 740 |
673 bool MftH264Decoder::SendEndOfStreamMessage() { | |
674 CHECK(initialized_); | |
675 // Send the eos message with no parameters. | |
676 HRESULT hr = decoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0); | |
677 if (FAILED(hr)) { | |
678 LOG(ERROR) << "Failed to send the drain message to decoder"; | |
679 return false; | |
680 } | |
681 return true; | |
682 } | |
683 | |
684 bool MftH264Decoder::SendDrainMessage() { | |
685 CHECK(initialized_); | |
686 if (drain_message_sent_) { | |
687 LOG(ERROR) << "Drain message was already sent before!"; | |
688 return false; | |
689 } | |
690 | |
691 // Send the drain message with no parameters. | |
692 HRESULT hr = decoder_->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, NULL); | |
693 if (FAILED(hr)) { | |
694 LOG(ERROR) << "Failed to send the drain message to decoder"; | |
695 return false; | |
696 } | |
697 drain_message_sent_ = true; | |
698 return true; | |
699 } | |
700 | |
701 } // namespace media | 741 } // namespace media |
742 | |
743 #endif // defined(OS_WIN) | |
OLD | NEW |