OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 "content/common/gpu/media/android_video_decode_accelerator.h" | 5 #include "content/common/gpu/media/android_video_decode_accelerator.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/trace_event/trace_event.h" | 11 #include "base/trace_event/trace_event.h" |
12 #include "content/common/gpu/gpu_channel.h" | 12 #include "content/common/gpu/gpu_channel.h" |
13 #include "content/common/gpu/media/avda_return_on_failure.h" | 13 #include "content/common/gpu/media/avda_return_on_failure.h" |
14 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | 14 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" |
15 #include "media/base/bind_to_current_loop.h" | |
15 #include "media/base/bitstream_buffer.h" | 16 #include "media/base/bitstream_buffer.h" |
16 #include "media/base/limits.h" | 17 #include "media/base/limits.h" |
17 #include "media/base/timestamp_constants.h" | 18 #include "media/base/timestamp_constants.h" |
18 #include "media/base/video_decoder_config.h" | 19 #include "media/base/video_decoder_config.h" |
19 #include "media/video/picture.h" | 20 #include "media/video/picture.h" |
20 #include "ui/gl/android/scoped_java_surface.h" | 21 #include "ui/gl/android/scoped_java_surface.h" |
21 #include "ui/gl/android/surface_texture.h" | 22 #include "ui/gl/android/surface_texture.h" |
22 #include "ui/gl/gl_bindings.h" | 23 #include "ui/gl/gl_bindings.h" |
23 | 24 |
24 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | 25 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
79 const base::Callback<bool(void)>& make_context_current, | 80 const base::Callback<bool(void)>& make_context_current, |
80 scoped_ptr<BackingStrategy> strategy) | 81 scoped_ptr<BackingStrategy> strategy) |
81 : client_(NULL), | 82 : client_(NULL), |
82 make_context_current_(make_context_current), | 83 make_context_current_(make_context_current), |
83 codec_(media::kCodecH264), | 84 codec_(media::kCodecH264), |
84 is_encrypted_(false), | 85 is_encrypted_(false), |
85 state_(NO_ERROR), | 86 state_(NO_ERROR), |
86 picturebuffers_requested_(false), | 87 picturebuffers_requested_(false), |
87 gl_decoder_(decoder), | 88 gl_decoder_(decoder), |
88 strategy_(strategy.Pass()), | 89 strategy_(strategy.Pass()), |
90 cdm_registration_id_(0), | |
89 weak_this_factory_(this) {} | 91 weak_this_factory_(this) {} |
90 | 92 |
91 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { | 93 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { |
92 DCHECK(thread_checker_.CalledOnValidThread()); | 94 DCHECK(thread_checker_.CalledOnValidThread()); |
95 | |
96 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | |
97 if (cdm_) { | |
98 DCHECK(cdm_registration_id_); | |
99 static_cast<media::MediaDrmBridge*>(cdm_.get()) | |
100 ->UnregisterPlayer(cdm_registration_id_); | |
101 } | |
102 #endif // defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | |
93 } | 103 } |
94 | 104 |
95 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, | 105 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
96 Client* client) { | 106 Client* client) { |
97 DCHECK(!media_codec_); | 107 DCHECK(!media_codec_); |
98 DCHECK(thread_checker_.CalledOnValidThread()); | 108 DCHECK(thread_checker_.CalledOnValidThread()); |
99 TRACE_EVENT0("media", "AVDA::Initialize"); | 109 TRACE_EVENT0("media", "AVDA::Initialize"); |
100 | 110 |
101 DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile | 111 DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile |
102 << " is_encrypted:" << config.is_encrypted; | 112 << " is_encrypted:" << config.is_encrypted; |
(...skipping 28 matching lines...) Expand all Loading... | |
131 return false; | 141 return false; |
132 } | 142 } |
133 | 143 |
134 if (!gl_decoder_) { | 144 if (!gl_decoder_) { |
135 LOG(ERROR) << "Failed to get gles2 decoder instance."; | 145 LOG(ERROR) << "Failed to get gles2 decoder instance."; |
136 return false; | 146 return false; |
137 } | 147 } |
138 | 148 |
139 strategy_->Initialize(this); | 149 strategy_->Initialize(this); |
140 | 150 |
141 surface_texture_ = strategy_->CreateSurfaceTexture(); | 151 surface_texture_ = strategy_->CreateSurfaceTexture(); |
liberato (no reviews please)
2015/12/04 16:20:11
how does this fit with |needs_protected_surface|?
Tima Vaisburd
2015/12/04 20:12:01
Can the protected surface be deferred in the subse
liberato (no reviews please)
2015/12/04 21:40:13
sure, i didn't mean that protected surfaces have t
| |
142 | 152 |
143 if (!ConfigureMediaCodec()) { | 153 // For encrypted streams we postpone configuration until MediaCrypto is |
liberato (no reviews please)
2015/12/04 16:20:11
all of this would become a call to FinishInitializ
| |
144 LOG(ERROR) << "Failed to create MediaCodec instance."; | 154 // available. |
145 return false; | 155 if (is_encrypted_) |
146 } | 156 return true; |
147 | 157 |
148 return true; | 158 return ConfigureMediaCodec(); |
149 } | 159 } |
150 | 160 |
151 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { | 161 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { |
xhwang
2015/12/04 20:22:03
This should only be called after Initialize(), oth
liberato (no reviews please)
2015/12/04 21:40:13
maybe additional values for |state_| are useful he
Tima Vaisburd
2015/12/04 22:54:51
Yes, I agree. For now I DCHECKed client_, I'll try
| |
152 DVLOG(2) << __FUNCTION__ << ": " << cdm_id; | 162 DVLOG(2) << __FUNCTION__ << ": " << cdm_id; |
153 | 163 |
154 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | 164 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
155 // TODO(timav): Implement CDM setting here. See http://crbug.com/542417 | 165 using media::MediaDrmBridge; |
156 scoped_refptr<media::MediaKeys> cdm = media::MojoCdmService::GetCdm(cdm_id); | 166 |
157 DCHECK(cdm); | 167 if (cdm_) { |
158 #endif | 168 NOTREACHED() << "We do not support resetting CDM."; |
169 NotifyCdmAttached(false); | |
170 return; | |
171 } | |
172 | |
173 cdm_ = media::MojoCdmService::GetCdm(cdm_id); | |
174 DCHECK(cdm_); | |
175 | |
176 // On Android platform the MediaKeys fill be its subclass MediaDrmBridge. | |
liberato (no reviews please)
2015/12/04 16:20:11
s/fill/will
Tima Vaisburd
2015/12/04 20:12:01
Done.
| |
177 MediaDrmBridge* drm_bridge = static_cast<MediaDrmBridge*>(cdm_.get()); | |
178 | |
179 // Register CDM callbacks. The callbacks registered will be posted back to | |
180 // this thread via BindToCurrentLoop. | |
181 | |
182 // Since |this| holds a reference to the |cdm_|, by the time the CDM is | |
183 // destructed, UnregisterPlayer() must have been called and |this| has been | |
184 // destructed as well. So the |cdm_unset_cb| will never have a chance to be | |
185 // called. | |
186 // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms. | |
187 cdm_registration_id_ = drm_bridge->RegisterPlayer( | |
188 media::BindToCurrentLoop( | |
189 base::Bind(&AndroidVideoDecodeAccelerator::OnCdmKeyAdded, | |
190 weak_this_factory_.GetWeakPtr())), | |
191 base::Bind(&base::DoNothing)); | |
192 | |
193 drm_bridge->SetMediaCryptoReadyCB(media::BindToCurrentLoop( | |
194 base::Bind(&AndroidVideoDecodeAccelerator::OnCdmMediaCryptoReady, | |
195 weak_this_factory_.GetWeakPtr()))); | |
196 | |
197 // Postpone NotifyCdmAttached() call till we create the MediaCodec after | |
198 // OnCdmMediaCryptoReady(). | |
199 | |
200 #else | |
159 | 201 |
160 NOTIMPLEMENTED(); | 202 NOTIMPLEMENTED(); |
161 NotifyCdmAttached(false); | 203 NotifyCdmAttached(false); |
204 | |
205 #endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | |
162 } | 206 } |
163 | 207 |
164 void AndroidVideoDecodeAccelerator::DoIOTask() { | 208 void AndroidVideoDecodeAccelerator::DoIOTask() { |
165 DCHECK(thread_checker_.CalledOnValidThread()); | 209 DCHECK(thread_checker_.CalledOnValidThread()); |
166 TRACE_EVENT0("media", "AVDA::DoIOTask"); | 210 TRACE_EVENT0("media", "AVDA::DoIOTask"); |
167 if (state_ == ERROR) { | 211 if (state_ == ERROR) { |
168 return; | 212 return; |
169 } | 213 } |
170 | 214 |
liberato (no reviews please)
2015/12/04 16:20:11
might want to check for |media_codec_| here, since
Tima Vaisburd
2015/12/04 20:12:01
Please explain how. I've assumed that initializati
liberato (no reviews please)
2015/12/04 21:40:13
yeah, i suppose you're right. |init_cb_| doesn't
| |
171 QueueInput(); | 215 QueueInput(); |
172 while (DequeueOutput()) | 216 while (DequeueOutput()) |
173 ; | 217 ; |
174 } | 218 } |
175 | 219 |
176 void AndroidVideoDecodeAccelerator::QueueInput() { | 220 void AndroidVideoDecodeAccelerator::QueueInput() { |
177 DCHECK(thread_checker_.CalledOnValidThread()); | 221 DCHECK(thread_checker_.CalledOnValidThread()); |
178 TRACE_EVENT0("media", "AVDA::QueueInput"); | 222 TRACE_EVENT0("media", "AVDA::QueueInput"); |
179 if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance) | 223 if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance) |
180 return; | 224 return; |
181 if (pending_bitstream_buffers_.empty()) | 225 if (pending_bitstream_buffers_.empty()) |
182 return; | 226 return; |
183 | 227 |
184 int input_buf_index = 0; | 228 int input_buf_index = 0; |
185 media::MediaCodecStatus status = | 229 media::MediaCodecStatus status = |
186 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index); | 230 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index); |
187 if (status != media::MEDIA_CODEC_OK) { | 231 if (status != media::MEDIA_CODEC_OK) { |
188 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || | 232 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || |
189 status == media::MEDIA_CODEC_ERROR); | 233 status == media::MEDIA_CODEC_ERROR); |
190 return; | 234 return; |
191 } | 235 } |
192 | 236 |
193 base::Time queued_time = pending_bitstream_buffers_.front().second; | 237 base::Time queued_time = pending_bitstream_buffers_.front().second; |
194 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime", | 238 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime", |
195 base::Time::Now() - queued_time); | 239 base::Time::Now() - queued_time); |
196 media::BitstreamBuffer bitstream_buffer = | 240 media::BitstreamBuffer bitstream_buffer = |
197 pending_bitstream_buffers_.front().first; | 241 pending_bitstream_buffers_.front().first; |
198 pending_bitstream_buffers_.pop(); | |
199 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", | |
200 pending_bitstream_buffers_.size()); | |
201 | 242 |
202 if (bitstream_buffer.id() == -1) { | 243 if (bitstream_buffer.id() == -1) { |
244 pending_bitstream_buffers_.pop(); | |
245 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", | |
246 pending_bitstream_buffers_.size()); | |
247 | |
203 media_codec_->QueueEOS(input_buf_index); | 248 media_codec_->QueueEOS(input_buf_index); |
204 return; | 249 return; |
205 } | 250 } |
206 | 251 |
207 scoped_ptr<base::SharedMemory> shm( | 252 scoped_ptr<base::SharedMemory> shm( |
208 new base::SharedMemory(bitstream_buffer.handle(), true)); | 253 new base::SharedMemory(bitstream_buffer.handle(), true)); |
209 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()), | 254 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()), |
210 "Failed to SharedMemory::Map()", UNREADABLE_INPUT); | 255 "Failed to SharedMemory::Map()", UNREADABLE_INPUT); |
211 | 256 |
212 const base::TimeDelta presentation_timestamp = | 257 const base::TimeDelta presentation_timestamp = |
(...skipping 22 matching lines...) Expand all Loading... | |
235 } else { | 280 } else { |
236 status = media_codec_->QueueSecureInputBuffer( | 281 status = media_codec_->QueueSecureInputBuffer( |
237 input_buf_index, memory, bitstream_buffer.size(), key_id, iv, | 282 input_buf_index, memory, bitstream_buffer.size(), key_id, iv, |
238 subsamples, presentation_timestamp); | 283 subsamples, presentation_timestamp); |
239 } | 284 } |
240 | 285 |
241 DVLOG(2) << __FUNCTION__ | 286 DVLOG(2) << __FUNCTION__ |
242 << ": QueueInputBuffer: pts:" << presentation_timestamp | 287 << ": QueueInputBuffer: pts:" << presentation_timestamp |
243 << " status:" << status; | 288 << " status:" << status; |
244 | 289 |
290 if (status == media::MEDIA_CODEC_NO_KEY) | |
291 return; // keep trying to enqueue the front pending buffer | |
xhwang
2015/12/04 20:22:03
This is an important event. Add a log here.
Tima Vaisburd
2015/12/04 22:54:51
The log line above already prints the status, unle
xhwang
2015/12/04 23:24:09
We need a log with higher level for unexpected eve
Tima Vaisburd
2015/12/05 01:35:25
Done, but it would be better to have states and ha
| |
292 | |
293 pending_bitstream_buffers_.pop(); | |
294 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", | |
295 pending_bitstream_buffers_.size()); | |
296 | |
245 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, | 297 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, |
246 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE); | 298 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE); |
247 | 299 |
248 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output | 300 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output |
249 // will be returned from the bitstream buffer. However, MediaCodec API is | 301 // will be returned from the bitstream buffer. However, MediaCodec API is |
250 // not enough to guarantee it. | 302 // not enough to guarantee it. |
251 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to | 303 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to |
252 // keep getting more bitstreams from the client, and throttle them by using | 304 // keep getting more bitstreams from the client, and throttle them by using |
253 // |bitstreams_notified_in_advance_|. | 305 // |bitstreams_notified_in_advance_|. |
254 // TODO(dwkang): check if there is a way to remove this workaround. | 306 // TODO(dwkang): check if there is a way to remove this workaround. |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
498 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); | 550 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); |
499 } | 551 } |
500 | 552 |
501 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { | 553 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { |
502 DCHECK(thread_checker_.CalledOnValidThread()); | 554 DCHECK(thread_checker_.CalledOnValidThread()); |
503 DCHECK(surface_texture_.get()); | 555 DCHECK(surface_texture_.get()); |
504 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); | 556 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); |
505 | 557 |
506 gfx::ScopedJavaSurface surface(surface_texture_.get()); | 558 gfx::ScopedJavaSurface surface(surface_texture_.get()); |
507 | 559 |
560 jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr; | |
561 | |
508 // Pass a dummy 320x240 canvas size and let the codec signal the real size | 562 // Pass a dummy 320x240 canvas size and let the codec signal the real size |
509 // when it's known from the bitstream. | 563 // when it's known from the bitstream. |
510 media_codec_.reset(media::VideoCodecBridge::CreateDecoder( | 564 media_codec_.reset(media::VideoCodecBridge::CreateDecoder( |
511 codec_, false, gfx::Size(320, 240), surface.j_surface().obj(), NULL)); | 565 codec_, false, gfx::Size(320, 240), surface.j_surface().obj(), |
566 media_crypto)); | |
512 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); | 567 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); |
513 if (!media_codec_) | 568 if (!media_codec_) { |
569 LOG(ERROR) << "Failed to create MediaCodec instance."; | |
514 return false; | 570 return false; |
571 } | |
515 | 572 |
516 io_timer_.Start(FROM_HERE, | 573 io_timer_.Start(FROM_HERE, |
517 DecodePollDelay(), | 574 DecodePollDelay(), |
518 this, | 575 this, |
519 &AndroidVideoDecodeAccelerator::DoIOTask); | 576 &AndroidVideoDecodeAccelerator::DoIOTask); |
520 return true; | 577 return true; |
521 } | 578 } |
522 | 579 |
523 void AndroidVideoDecodeAccelerator::Reset() { | 580 void AndroidVideoDecodeAccelerator::Reset() { |
524 DCHECK(thread_checker_.CalledOnValidThread()); | 581 DCHECK(thread_checker_.CalledOnValidThread()); |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
599 | 656 |
600 void AndroidVideoDecodeAccelerator::PostError( | 657 void AndroidVideoDecodeAccelerator::PostError( |
601 const ::tracked_objects::Location& from_here, | 658 const ::tracked_objects::Location& from_here, |
602 media::VideoDecodeAccelerator::Error error) { | 659 media::VideoDecodeAccelerator::Error error) { |
603 base::MessageLoop::current()->PostTask( | 660 base::MessageLoop::current()->PostTask( |
604 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError, | 661 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError, |
605 weak_this_factory_.GetWeakPtr(), error)); | 662 weak_this_factory_.GetWeakPtr(), error)); |
606 state_ = ERROR; | 663 state_ = ERROR; |
607 } | 664 } |
608 | 665 |
666 void AndroidVideoDecodeAccelerator::OnCdmMediaCryptoReady( | |
667 media::MediaDrmBridge::JavaObjectPtr media_crypto, | |
668 bool needs_protected_surface) { | |
669 DVLOG(1) << __FUNCTION__; | |
670 | |
671 DCHECK(media_crypto); | |
xhwang
2015/12/04 20:22:03
With my CL, |media_crypto| can actually be null if
Tima Vaisburd
2015/12/04 22:54:51
Done.
| |
672 DCHECK(!media_crypto->is_null()); | |
liberato (no reviews please)
2015/12/04 16:20:11
DCHECK(!media_codec)
Tima Vaisburd
2015/12/04 20:12:01
I added this DCHECK, but ConfigureMediaCodec() doe
liberato (no reviews please)
2015/12/04 21:40:13
it depends if we've sent in any buffers.
| |
673 | |
674 media_crypto_ = media_crypto.Pass(); | |
675 | |
676 // Configure here instead of in Initialize() | |
677 const bool success = ConfigureMediaCodec(); | |
678 NotifyCdmAttached(success); | |
liberato (no reviews please)
2015/12/04 16:20:11
outside the scope of this CL, but NotifyInitializa
Tima Vaisburd
2015/12/04 20:12:01
I heard that there is a work on Version 2 which wi
liberato (no reviews please)
2015/12/04 21:40:13
yes, that's why i think it's outside the scope of
| |
679 } | |
680 | |
681 void AndroidVideoDecodeAccelerator::OnCdmKeyAdded() { | |
682 DVLOG(1) << __FUNCTION__; | |
xhwang
2015/12/04 20:22:03
NOTIMPLEMENTED()
Also add a TODO to finish this p
Tima Vaisburd
2015/12/04 22:54:51
Since in this CL we do not try to stop the decodin
xhwang
2015/12/04 23:24:09
I don't fully understand how this works. What's th
Tima Vaisburd
2015/12/05 01:35:25
Added some comments, please take a look.
| |
683 } | |
684 | |
609 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { | 685 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { |
610 client_->NotifyCdmAttached(success); | 686 client_->NotifyCdmAttached(success); |
611 } | 687 } |
612 | 688 |
613 void AndroidVideoDecodeAccelerator::NotifyPictureReady( | 689 void AndroidVideoDecodeAccelerator::NotifyPictureReady( |
614 const media::Picture& picture) { | 690 const media::Picture& picture) { |
615 client_->PictureReady(picture); | 691 client_->PictureReady(picture); |
616 } | 692 } |
617 | 693 |
618 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( | 694 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
666 // software fallback for H264 on Android anyway. | 742 // software fallback for H264 on Android anyway. |
667 profile.max_resolution.SetSize(3840, 2160); | 743 profile.max_resolution.SetSize(3840, 2160); |
668 profiles.push_back(profile); | 744 profiles.push_back(profile); |
669 } | 745 } |
670 #endif | 746 #endif |
671 | 747 |
672 return profiles; | 748 return profiles; |
673 } | 749 } |
674 | 750 |
675 } // namespace content | 751 } // namespace content |
OLD | NEW |