OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/base/android/media_codec_decoder.h" | 5 #include "media/base/android/media_codec_decoder.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 19 matching lines...) Expand all Loading... | |
30 // Timeout for dequeuing an output buffer from MediaCodec in milliseconds. | 30 // Timeout for dequeuing an output buffer from MediaCodec in milliseconds. |
31 const int kOutputBufferTimeout = 20; | 31 const int kOutputBufferTimeout = 20; |
32 } | 32 } |
33 | 33 |
34 MediaCodecDecoder::MediaCodecDecoder( | 34 MediaCodecDecoder::MediaCodecDecoder( |
35 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 35 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
36 const base::Closure& external_request_data_cb, | 36 const base::Closure& external_request_data_cb, |
37 const base::Closure& starvation_cb, | 37 const base::Closure& starvation_cb, |
38 const base::Closure& decoder_drained_cb, | 38 const base::Closure& decoder_drained_cb, |
39 const base::Closure& stop_done_cb, | 39 const base::Closure& stop_done_cb, |
40 const base::Closure& key_required_cb, | |
40 const base::Closure& error_cb, | 41 const base::Closure& error_cb, |
41 const char* decoder_thread_name) | 42 const char* decoder_thread_name) |
42 : media_task_runner_(media_task_runner), | 43 : media_task_runner_(media_task_runner), |
43 decoder_thread_(decoder_thread_name), | 44 decoder_thread_(decoder_thread_name), |
44 needs_reconfigure_(false), | 45 needs_reconfigure_(false), |
45 drain_decoder_(false), | 46 drain_decoder_(false), |
46 always_reconfigure_for_tests_(false), | 47 always_reconfigure_for_tests_(false), |
47 external_request_data_cb_(external_request_data_cb), | 48 external_request_data_cb_(external_request_data_cb), |
48 starvation_cb_(starvation_cb), | 49 starvation_cb_(starvation_cb), |
49 decoder_drained_cb_(decoder_drained_cb), | 50 decoder_drained_cb_(decoder_drained_cb), |
50 stop_done_cb_(stop_done_cb), | 51 stop_done_cb_(stop_done_cb), |
52 key_required_cb_(key_required_cb), | |
51 error_cb_(error_cb), | 53 error_cb_(error_cb), |
52 state_(kStopped), | 54 state_(kStopped), |
53 is_prepared_(false), | 55 is_prepared_(false), |
54 eos_enqueued_(false), | 56 eos_enqueued_(false), |
57 key_request_posted_(false), | |
55 completed_(false), | 58 completed_(false), |
56 last_frame_posted_(false), | 59 last_frame_posted_(false), |
57 is_data_request_in_progress_(false), | 60 is_data_request_in_progress_(false), |
58 is_incoming_data_invalid_(false), | 61 is_incoming_data_invalid_(false), |
59 #ifndef NDEBUG | 62 #ifndef NDEBUG |
60 verify_next_frame_is_key_(false), | 63 verify_next_frame_is_key_(false), |
61 #endif | 64 #endif |
62 weak_factory_(this) { | 65 weak_factory_(this) { |
63 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 66 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
64 | 67 |
65 DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name; | 68 DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name; |
66 | 69 |
67 internal_error_cb_ = | 70 internal_error_cb_ = |
68 base::Bind(&MediaCodecDecoder::OnCodecError, weak_factory_.GetWeakPtr()); | 71 base::Bind(&MediaCodecDecoder::OnCodecError, weak_factory_.GetWeakPtr()); |
69 internal_preroll_done_cb_ = | 72 internal_preroll_done_cb_ = |
70 base::Bind(&MediaCodecDecoder::OnPrerollDone, weak_factory_.GetWeakPtr()); | 73 base::Bind(&MediaCodecDecoder::OnPrerollDone, weak_factory_.GetWeakPtr()); |
71 request_data_cb_ = | 74 request_data_cb_ = |
72 base::Bind(&MediaCodecDecoder::RequestData, weak_factory_.GetWeakPtr()); | 75 base::Bind(&MediaCodecDecoder::RequestData, weak_factory_.GetWeakPtr()); |
73 } | 76 } |
74 | 77 |
75 MediaCodecDecoder::~MediaCodecDecoder() {} | 78 MediaCodecDecoder::~MediaCodecDecoder() {} |
76 | 79 |
77 const char* MediaCodecDecoder::class_name() const { | 80 const char* MediaCodecDecoder::class_name() const { |
78 return "Decoder"; | 81 return "Decoder"; |
79 } | 82 } |
80 | 83 |
81 void MediaCodecDecoder::Flush() { | 84 void MediaCodecDecoder::Flush() { |
qinmin
2015/09/23 05:58:26
key_request_posted needs to be reset to false here
Tima Vaisburd
2015/09/24 18:15:43
Yes, thank you. Done.
| |
82 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 85 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
83 | 86 |
84 DVLOG(1) << class_name() << "::" << __FUNCTION__; | 87 DVLOG(1) << class_name() << "::" << __FUNCTION__; |
85 | 88 |
86 DCHECK_EQ(GetState(), kStopped); | 89 DCHECK_EQ(GetState(), kStopped); |
87 | 90 |
88 // Flush() is a part of the Seek request. Whenever we request a seek we need | 91 // Flush() is a part of the Seek request. Whenever we request a seek we need |
89 // to invalidate the current data request. | 92 // to invalidate the current data request. |
90 if (is_data_request_in_progress_) | 93 if (is_data_request_in_progress_) |
91 is_incoming_data_invalid_ = true; | 94 is_incoming_data_invalid_ = true; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
172 (!is_prepared_ || preroll_timestamp_ != base::TimeDelta()); | 175 (!is_prepared_ || preroll_timestamp_ != base::TimeDelta()); |
173 } | 176 } |
174 | 177 |
175 void MediaCodecDecoder::SetPrerollTimestamp(base::TimeDelta preroll_timestamp) { | 178 void MediaCodecDecoder::SetPrerollTimestamp(base::TimeDelta preroll_timestamp) { |
176 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 179 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
177 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": " << preroll_timestamp; | 180 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": " << preroll_timestamp; |
178 | 181 |
179 preroll_timestamp_ = preroll_timestamp; | 182 preroll_timestamp_ = preroll_timestamp; |
180 } | 183 } |
181 | 184 |
182 base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() { | 185 void MediaCodecDecoder::SetNeedsReconfigure() { |
183 base::android::ScopedJavaLocalRef<jobject> media_crypto; | 186 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
184 | 187 |
185 // TODO(timav): implement DRM. | 188 DVLOG(1) << class_name() << "::" << __FUNCTION__; |
186 // drm_bridge_ is not implemented | 189 |
187 // if (drm_bridge_) | 190 needs_reconfigure_ = true; |
188 // media_crypto = drm_bridge_->GetMediaCrypto(); | |
189 return media_crypto; | |
190 } | 191 } |
191 | 192 |
192 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { | 193 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { |
193 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 194 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
194 | 195 |
195 DVLOG(1) << class_name() << "::" << __FUNCTION__; | 196 DVLOG(1) << class_name() << "::" << __FUNCTION__; |
196 | 197 |
197 DCHECK(GetState() == kStopped); | 198 DCHECK(GetState() == kStopped); |
198 | 199 |
199 prefetch_done_cb_ = prefetch_done_cb; | 200 prefetch_done_cb_ = prefetch_done_cb; |
200 | 201 |
201 SetState(kPrefetching); | 202 SetState(kPrefetching); |
202 PrefetchNextChunk(); | 203 PrefetchNextChunk(); |
203 } | 204 } |
204 | 205 |
205 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { | 206 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure( |
207 jobject media_crypto) { | |
206 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 208 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
207 | 209 |
208 DVLOG(1) << class_name() << "::" << __FUNCTION__; | 210 DVLOG(1) << class_name() << "::" << __FUNCTION__; |
209 | 211 |
210 if (GetState() == kError) { | 212 if (GetState() == kError) { |
211 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state kError"; | 213 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state kError"; |
212 return kConfigFailure; | 214 return kConfigFailure; |
213 } | 215 } |
214 | 216 |
215 if (needs_reconfigure_) { | 217 if (needs_reconfigure_) { |
(...skipping 10 matching lines...) Expand all Loading... | |
226 } | 228 } |
227 | 229 |
228 // Read all |kConfigChanged| units preceding the data one. | 230 // Read all |kConfigChanged| units preceding the data one. |
229 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); | 231 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); |
230 while (au_info.configs) { | 232 while (au_info.configs) { |
231 SetDemuxerConfigs(*au_info.configs); | 233 SetDemuxerConfigs(*au_info.configs); |
232 au_queue_.Advance(); | 234 au_queue_.Advance(); |
233 au_info = au_queue_.GetInfo(); | 235 au_info = au_queue_.GetInfo(); |
234 } | 236 } |
235 | 237 |
236 MediaCodecDecoder::ConfigStatus result = ConfigureInternal(); | 238 MediaCodecDecoder::ConfigStatus result = ConfigureInternal(media_crypto); |
237 | 239 |
238 #ifndef NDEBUG | 240 #ifndef NDEBUG |
239 // We check and reset |verify_next_frame_is_key_| on Decoder thread. | 241 // We check and reset |verify_next_frame_is_key_| on Decoder thread. |
240 // This DCHECK ensures we won't need to lock this variable. | 242 // This DCHECK ensures we won't need to lock this variable. |
241 DCHECK(!decoder_thread_.IsRunning()); | 243 DCHECK(!decoder_thread_.IsRunning()); |
242 | 244 |
243 // For video the first frame after reconfiguration must be key frame. | 245 // For video the first frame after reconfiguration must be key frame. |
244 if (result == kConfigOk) | 246 if (result == kConfigOk) |
245 verify_next_frame_is_key_ = true; | 247 verify_next_frame_is_key_ = true; |
246 #endif | 248 #endif |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
394 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 396 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
395 | 397 |
396 DVLOG(1) << class_name() << "::" << __FUNCTION__ | 398 DVLOG(1) << class_name() << "::" << __FUNCTION__ |
397 << " eos_encountered:" << eos_encountered; | 399 << " eos_encountered:" << eos_encountered; |
398 | 400 |
399 decoder_thread_.Stop(); // synchronous | 401 decoder_thread_.Stop(); // synchronous |
400 | 402 |
401 SetState(kStopped); | 403 SetState(kStopped); |
402 completed_ = (eos_encountered && !drain_decoder_); | 404 completed_ = (eos_encountered && !drain_decoder_); |
403 | 405 |
406 key_request_posted_ = false; | |
407 | |
404 // If the stream is completed during preroll we need to report it since | 408 // If the stream is completed during preroll we need to report it since |
405 // another stream might be running and the player waits for two callbacks. | 409 // another stream might be running and the player waits for two callbacks. |
406 if (completed_ && !preroll_done_cb_.is_null()) { | 410 if (completed_ && !preroll_done_cb_.is_null()) { |
407 preroll_timestamp_ = base::TimeDelta(); | 411 preroll_timestamp_ = base::TimeDelta(); |
408 media_task_runner_->PostTask(FROM_HERE, | 412 media_task_runner_->PostTask(FROM_HERE, |
409 base::ResetAndReturn(&preroll_done_cb_)); | 413 base::ResetAndReturn(&preroll_done_cb_)); |
410 } | 414 } |
411 | 415 |
412 if (eos_encountered && drain_decoder_) { | 416 if (eos_encountered && drain_decoder_) { |
413 drain_decoder_ = false; | 417 drain_decoder_ = false; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
491 DVLOG(1) << class_name() << "::" << __FUNCTION__; | 495 DVLOG(1) << class_name() << "::" << __FUNCTION__; |
492 | 496 |
493 // After this method returns, decoder thread will not be running. | 497 // After this method returns, decoder thread will not be running. |
494 | 498 |
495 // Set [kInEmergencyStop| state to block already posted ProcessNextFrame(). | 499 // Set [kInEmergencyStop| state to block already posted ProcessNextFrame(). |
496 SetState(kInEmergencyStop); | 500 SetState(kInEmergencyStop); |
497 | 501 |
498 decoder_thread_.Stop(); // synchronous | 502 decoder_thread_.Stop(); // synchronous |
499 | 503 |
500 SetState(kStopped); | 504 SetState(kStopped); |
505 | |
506 key_request_posted_ = false; | |
501 } | 507 } |
502 | 508 |
503 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, | 509 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, |
504 bool has_delayed_tasks) { | 510 bool has_delayed_tasks) { |
505 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 511 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
506 | 512 |
507 bool last_frame_when_stopping = GetState() == kStopping && !has_delayed_tasks; | 513 bool last_frame_when_stopping = GetState() == kStopping && !has_delayed_tasks; |
508 | 514 |
509 if (last_frame_when_stopping || eos_encountered) { | 515 if (last_frame_when_stopping || eos_encountered) { |
510 media_task_runner_->PostTask( | 516 media_task_runner_->PostTask( |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
611 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 617 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
612 | 618 |
613 DVLOG(2) << class_name() << "::" << __FUNCTION__; | 619 DVLOG(2) << class_name() << "::" << __FUNCTION__; |
614 | 620 |
615 if (eos_enqueued_) { | 621 if (eos_enqueued_) { |
616 DVLOG(1) << class_name() << "::" << __FUNCTION__ | 622 DVLOG(1) << class_name() << "::" << __FUNCTION__ |
617 << ": eos_enqueued, returning"; | 623 << ": eos_enqueued, returning"; |
618 return true; // Nothing to do | 624 return true; // Nothing to do |
619 } | 625 } |
620 | 626 |
627 if (key_request_posted_) { | |
628 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
629 << ": key_request_posted, returning"; | |
630 return true; // Nothing to do | |
631 } | |
632 | |
621 // Keep the number pending video frames low, ideally maintaining | 633 // Keep the number pending video frames low, ideally maintaining |
622 // the same audio and video duration after stop request | 634 // the same audio and video duration after stop request |
623 if (NumDelayedRenderTasks() > 1) { | 635 if (NumDelayedRenderTasks() > 1) { |
624 DVLOG(2) << class_name() << "::" << __FUNCTION__ << ": # delayed buffers (" | 636 DVLOG(2) << class_name() << "::" << __FUNCTION__ << ": # delayed buffers (" |
625 << NumDelayedRenderTasks() << ") exceeds 1, returning"; | 637 << NumDelayedRenderTasks() << ") exceeds 1, returning"; |
626 return true; // Nothing to do | 638 return true; // Nothing to do |
627 } | 639 } |
628 | 640 |
629 // Get the next frame from the queue. As we go, request more data and | 641 // Get the next frame from the queue. As we go, request more data and |
630 // consume |kConfigChanged| units. | 642 // consume |kConfigChanged| units. |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
688 | 700 |
689 if (drain_decoder_ || unit->is_end_of_stream) { | 701 if (drain_decoder_ || unit->is_end_of_stream) { |
690 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; | 702 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; |
691 media_codec_bridge_->QueueEOS(index); | 703 media_codec_bridge_->QueueEOS(index); |
692 eos_enqueued_ = true; | 704 eos_enqueued_ = true; |
693 return true; | 705 return true; |
694 } | 706 } |
695 | 707 |
696 DCHECK(unit); | 708 DCHECK(unit); |
697 | 709 |
698 DVLOG(2) << class_name() << "::" << __FUNCTION__ | 710 if (unit->key_id.empty() || unit->iv.empty()) { |
699 << ": QueueInputBuffer pts:" << unit->timestamp; | 711 DVLOG(2) << class_name() << "::" << __FUNCTION__ |
712 << ": QueueInputBuffer pts:" << unit->timestamp; | |
700 | 713 |
701 status = media_codec_bridge_->QueueInputBuffer( | 714 status = media_codec_bridge_->QueueInputBuffer( |
702 index, &unit->data[0], unit->data.size(), unit->timestamp); | 715 index, &unit->data[0], unit->data.size(), unit->timestamp); |
716 } else { | |
717 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
718 << ": QueueSecureInputBuffer pts:" << unit->timestamp | |
719 << " key_id size:" << unit->key_id.size() | |
720 << " iv size:" << unit->iv.size() | |
721 << " subsamples size:" << unit->subsamples.size(); | |
703 | 722 |
704 if (status == MEDIA_CODEC_ERROR) { | 723 status = media_codec_bridge_->QueueSecureInputBuffer( |
705 DVLOG(0) << class_name() << "::" << __FUNCTION__ | 724 index, &unit->data[0], unit->data.size(), |
706 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; | 725 reinterpret_cast<const uint8_t*>(&unit->key_id[0]), unit->key_id.size(), |
707 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | 726 reinterpret_cast<const uint8_t*>(&unit->iv[0]), unit->iv.size(), |
708 return false; | 727 unit->subsamples.empty() ? nullptr : &unit->subsamples[0], |
728 unit->subsamples.size(), unit->timestamp); | |
729 } | |
730 | |
731 switch (status) { | |
732 case MEDIA_CODEC_OK: | |
733 break; | |
734 | |
735 case MEDIA_CODEC_ERROR: | |
736 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
737 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; | |
738 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
739 return false; | |
740 | |
741 case MEDIA_CODEC_NO_KEY: | |
742 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
743 << ": MEDIA_CODEC_NO_KEY"; | |
744 media_task_runner_->PostTask(FROM_HERE, key_required_cb_); | |
745 | |
746 // In response to the |key_required_cb_| the player will request to stop | |
747 // decoder. We need to keep running to properly perform the stop, but | |
748 // prevent enqueuing the same frame over and over again so we won't | |
749 // generate more |key_required_cb_|. | |
750 key_request_posted_ = true; | |
751 return true; | |
752 | |
753 default: | |
754 NOTREACHED() << class_name() << "::" << __FUNCTION__ | |
755 << ": unexpected error code " << status; | |
756 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
757 return false; | |
709 } | 758 } |
710 | 759 |
711 // Have successfully queued input buffer, go to next access unit. | 760 // Have successfully queued input buffer, go to next access unit. |
712 au_queue_.Advance(); | 761 au_queue_.Advance(); |
713 return true; | 762 return true; |
714 } | 763 } |
715 | 764 |
716 AccessUnitQueue::Info MediaCodecDecoder::AdvanceAccessUnitQueue( | 765 AccessUnitQueue::Info MediaCodecDecoder::AdvanceAccessUnitQueue( |
717 bool* drain_decoder) { | 766 bool* drain_decoder) { |
718 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 767 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
896 RETURN_STRING(kStopping); | 945 RETURN_STRING(kStopping); |
897 RETURN_STRING(kInEmergencyStop); | 946 RETURN_STRING(kInEmergencyStop); |
898 RETURN_STRING(kError); | 947 RETURN_STRING(kError); |
899 } | 948 } |
900 return nullptr; // crash early | 949 return nullptr; // crash early |
901 } | 950 } |
902 | 951 |
903 #undef RETURN_STRING | 952 #undef RETURN_STRING |
904 | 953 |
905 } // namespace media | 954 } // namespace media |
OLD | NEW |