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

Side by Side Diff: webrtc/sdk/android/src/java/org/webrtc/HardwareVideoDecoder.java

Issue 3002093002: Fix bugs in HardwareVideoDecoder reinitialization. (Closed)
Patch Set: Created 3 years, 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved. 2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 this.rotation = rotation; 117 this.rotation = rotation;
118 this.presentationTimestampUs = presentationTimestampUs; 118 this.presentationTimestampUs = presentationTimestampUs;
119 this.decodeTimeMs = decodeTimeMs; 119 this.decodeTimeMs = decodeTimeMs;
120 } 120 }
121 } 121 }
122 122
123 // Metadata for the last frame rendered to the texture. Only accessed on the texture helper's 123 // Metadata for the last frame rendered to the texture. Only accessed on the texture helper's
124 // thread. 124 // thread.
125 private DecodedTextureMetadata renderedTextureMetadata; 125 private DecodedTextureMetadata renderedTextureMetadata;
126 126
127 private final Object callbackLock = new Object();
magjed_webrtc 2017/08/24 10:52:24 Please get rid of the lock and use SurfaceTextureH
sakal 2017/08/24 11:24:00 Done.
127 // Decoding proceeds asynchronously. This callback returns decoded frames to the caller. 128 // Decoding proceeds asynchronously. This callback returns decoded frames to the caller.
128 private Callback callback; 129 private Callback callback;
129 130
130 private MediaCodec codec = null; 131 private MediaCodec codec = null;
131 132
132 HardwareVideoDecoder( 133 HardwareVideoDecoder(
133 String codecName, VideoCodecType codecType, int colorFormat, EglBase.Conte xt sharedContext) { 134 String codecName, VideoCodecType codecType, int colorFormat, EglBase.Conte xt sharedContext) {
134 if (!isSupportedColorFormat(colorFormat)) { 135 if (!isSupportedColorFormat(colorFormat)) {
135 throw new IllegalArgumentException("Unsupported color format: " + colorFor mat); 136 throw new IllegalArgumentException("Unsupported color format: " + colorFor mat);
136 } 137 }
137 this.codecName = codecName; 138 this.codecName = codecName;
138 this.codecType = codecType; 139 this.codecType = codecType;
139 this.colorFormat = colorFormat; 140 this.colorFormat = colorFormat;
140 this.sharedContext = sharedContext; 141 this.sharedContext = sharedContext;
141 this.frameInfos = new LinkedBlockingDeque<>(); 142 this.frameInfos = new LinkedBlockingDeque<>();
142 } 143 }
143 144
144 @Override 145 @Override
145 public VideoCodecStatus initDecode(Settings settings, Callback callback) { 146 public VideoCodecStatus initDecode(Settings settings, Callback callback) {
146 this.decoderThreadChecker = new ThreadChecker(); 147 this.decoderThreadChecker = new ThreadChecker();
147 return initDecodeInternal(settings.width, settings.height, callback); 148
149 synchronized (callbackLock) {
150 this.callback = callback;
151 }
152 if (sharedContext != null) {
153 surfaceTextureHelper = SurfaceTextureHelper.create("decoder-texture-thread ", sharedContext);
154 surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
155 surfaceTextureHelper.startListening(this);
156 }
157 return initDecodeInternal(settings.width, settings.height);
148 } 158 }
149 159
150 private VideoCodecStatus initDecodeInternal(int width, int height, Callback ca llback) { 160 private VideoCodecStatus initDecodeInternal(int width, int height) {
151 decoderThreadChecker.checkIsOnValidThread(); 161 decoderThreadChecker.checkIsOnValidThread();
162 Logging.d(TAG, "initDecodeInternal");
152 if (outputThread != null) { 163 if (outputThread != null) {
153 Logging.e(TAG, "initDecodeInternal called while the codec is already runni ng"); 164 Logging.e(TAG, "initDecodeInternal called while the codec is already runni ng");
154 return VideoCodecStatus.ERROR; 165 return VideoCodecStatus.ERROR;
155 } 166 }
156 167
157 // Note: it is not necessary to initialize dimensions under the lock, since the output thread 168 // Note: it is not necessary to initialize dimensions under the lock, since the output thread
158 // is not running. 169 // is not running.
159 this.callback = callback;
160 this.width = width; 170 this.width = width;
161 this.height = height; 171 this.height = height;
162 172
163 stride = width; 173 stride = width;
164 sliceHeight = height; 174 sliceHeight = height;
165 hasDecodedFirstFrame = false; 175 hasDecodedFirstFrame = false;
166 keyFrameRequired = true; 176 keyFrameRequired = true;
167 177
168 try { 178 try {
169 codec = MediaCodec.createByCodecName(codecName); 179 codec = MediaCodec.createByCodecName(codecName);
170 } catch (IOException | IllegalArgumentException e) { 180 } catch (IOException | IllegalArgumentException e) {
171 Logging.e(TAG, "Cannot create media decoder " + codecName); 181 Logging.e(TAG, "Cannot create media decoder " + codecName);
172 return VideoCodecStatus.ERROR; 182 return VideoCodecStatus.ERROR;
173 } 183 }
174 try { 184 try {
175 MediaFormat format = MediaFormat.createVideoFormat(codecType.mimeType(), w idth, height); 185 MediaFormat format = MediaFormat.createVideoFormat(codecType.mimeType(), w idth, height);
176 if (sharedContext == null) { 186 if (sharedContext == null) {
177 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat); 187 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
178 } else {
179 surfaceTextureHelper = SurfaceTextureHelper.create("decoder-texture-thre ad", sharedContext);
180 surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
181 surfaceTextureHelper.startListening(this);
182 } 188 }
183 codec.configure(format, surface, null, 0); 189 codec.configure(format, surface, null, 0);
184 codec.start(); 190 codec.start();
185 } catch (IllegalStateException e) { 191 } catch (IllegalStateException e) {
186 Logging.e(TAG, "initDecode failed", e); 192 Logging.e(TAG, "initDecode failed", e);
187 release(); 193 release();
188 return VideoCodecStatus.ERROR; 194 return VideoCodecStatus.ERROR;
189 } 195 }
190
191 running = true; 196 running = true;
192 outputThread = createOutputThread(); 197 outputThread = createOutputThread();
193 outputThread.start(); 198 outputThread.start();
194 199
200 Logging.d(TAG, "initDecodeInternal done");
195 return VideoCodecStatus.OK; 201 return VideoCodecStatus.OK;
196 } 202 }
197 203
198 @Override 204 @Override
199 public VideoCodecStatus decode(EncodedImage frame, DecodeInfo info) { 205 public VideoCodecStatus decode(EncodedImage frame, DecodeInfo info) {
200 decoderThreadChecker.checkIsOnValidThread(); 206 decoderThreadChecker.checkIsOnValidThread();
201 if (codec == null || callback == null) { 207 synchronized (callbackLock) {
202 return VideoCodecStatus.UNINITIALIZED; 208 if (codec == null || callback == null) {
209 Logging.d(TAG, "decode uninitalized, codec: " + codec + ", callback: " + callback);
210 return VideoCodecStatus.UNINITIALIZED;
211 }
203 } 212 }
204 213
205 if (frame.buffer == null) { 214 if (frame.buffer == null) {
206 Logging.e(TAG, "decode() - no input data"); 215 Logging.e(TAG, "decode() - no input data");
207 return VideoCodecStatus.ERR_PARAMETER; 216 return VideoCodecStatus.ERR_PARAMETER;
208 } 217 }
209 218
210 int size = frame.buffer.remaining(); 219 int size = frame.buffer.remaining();
211 if (size == 0) { 220 if (size == 0) {
212 Logging.e(TAG, "decode() - input buffer empty"); 221 Logging.e(TAG, "decode() - input buffer empty");
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 @Override 301 @Override
293 public String getImplementationName() { 302 public String getImplementationName() {
294 return "HardwareVideoDecoder: " + codecName; 303 return "HardwareVideoDecoder: " + codecName;
295 } 304 }
296 305
297 @Override 306 @Override
298 public VideoCodecStatus release() { 307 public VideoCodecStatus release() {
299 // TODO(sakal): This is not called on the correct thread but is still called synchronously. 308 // TODO(sakal): This is not called on the correct thread but is still called synchronously.
300 // Re-enable the check once this is called on the correct thread. 309 // Re-enable the check once this is called on the correct thread.
301 // decoderThreadChecker.checkIsOnValidThread(); 310 // decoderThreadChecker.checkIsOnValidThread();
311 Logging.d(TAG, "release");
312 try {
313 return releaseInternal();
314 } finally {
315 synchronized (callbackLock) {
316 callback = null;
317 }
318 frameInfos.clear();
319 if (surface != null) {
320 surface.release();
321 surface = null;
322 surfaceTextureHelper.stopListening();
323 surfaceTextureHelper.dispose();
324 surfaceTextureHelper = null;
325 }
326 }
327 }
328
329 private VideoCodecStatus releaseInternal() {
302 if (!running) { 330 if (!running) {
303 Logging.d(TAG, "release: Decoder is not running."); 331 Logging.d(TAG, "release: Decoder is not running.");
304 return VideoCodecStatus.OK; 332 return VideoCodecStatus.OK;
305 } 333 }
306 try { 334 try {
307 // The outputThread actually stops and releases the codec once running is false. 335 // The outputThread actually stops and releases the codec once running is false.
308 running = false; 336 running = false;
309 if (!ThreadUtils.joinUninterruptibly(outputThread, MEDIA_CODEC_RELEASE_TIM EOUT_MS)) { 337 if (!ThreadUtils.joinUninterruptibly(outputThread, MEDIA_CODEC_RELEASE_TIM EOUT_MS)) {
310 // Log an exception to capture the stack trace and turn it into a TIMEOU T error. 338 // Log an exception to capture the stack trace and turn it into a TIMEOU T error.
311 Logging.e(TAG, "Media decoder release timeout", new RuntimeException()); 339 Logging.e(TAG, "Media decoder release timeout", new RuntimeException());
312 return VideoCodecStatus.TIMEOUT; 340 return VideoCodecStatus.TIMEOUT;
313 } 341 }
314 if (shutdownException != null) { 342 if (shutdownException != null) {
315 // Log the exception and turn it into an error. Wrap the exception in a new exception to 343 // Log the exception and turn it into an error. Wrap the exception in a new exception to
316 // capture both the output thread's stack trace and this thread's stack trace. 344 // capture both the output thread's stack trace and this thread's stack trace.
317 Logging.e(TAG, "Media decoder release error", new RuntimeException(shutd ownException)); 345 Logging.e(TAG, "Media decoder release error", new RuntimeException(shutd ownException));
318 shutdownException = null; 346 shutdownException = null;
319 return VideoCodecStatus.ERROR; 347 return VideoCodecStatus.ERROR;
320 } 348 }
321 } finally { 349 } finally {
322 codec = null; 350 codec = null;
323 callback = null;
324 outputThread = null; 351 outputThread = null;
325 frameInfos.clear();
326 if (surface != null) {
327 surface.release();
328 surface = null;
329 surfaceTextureHelper.stopListening();
330 surfaceTextureHelper.dispose();
331 surfaceTextureHelper = null;
332 }
333 } 352 }
334 return VideoCodecStatus.OK; 353 return VideoCodecStatus.OK;
335 } 354 }
336 355
337 private VideoCodecStatus reinitDecode(int newWidth, int newHeight) { 356 private VideoCodecStatus reinitDecode(int newWidth, int newHeight) {
338 decoderThreadChecker.checkIsOnValidThread(); 357 decoderThreadChecker.checkIsOnValidThread();
339 VideoCodecStatus status = release(); 358 VideoCodecStatus status = release();
340 if (status != VideoCodecStatus.OK) { 359 if (status != VideoCodecStatus.OK) {
341 return status; 360 return status;
342 } 361 }
343 return initDecodeInternal(newWidth, newHeight, callback); 362 return initDecodeInternal(newWidth, newHeight);
344 } 363 }
345 364
346 private Thread createOutputThread() { 365 private Thread createOutputThread() {
347 return new Thread("HardwareVideoDecoder.outputThread") { 366 return new Thread("HardwareVideoDecoder.outputThread") {
348 @Override 367 @Override
349 public void run() { 368 public void run() {
350 outputThreadChecker = new ThreadChecker(); 369 outputThreadChecker = new ThreadChecker();
351 while (running) { 370 while (running) {
352 deliverDecodedFrame(); 371 deliverDecodedFrame();
353 } 372 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 } 435 }
417 436
418 @Override 437 @Override
419 public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) { 438 public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) {
420 VideoFrame.TextureBuffer oesBuffer = surfaceTextureHelper.createTextureBuffe r( 439 VideoFrame.TextureBuffer oesBuffer = surfaceTextureHelper.createTextureBuffe r(
421 renderedTextureMetadata.width, renderedTextureMetadata.height, 440 renderedTextureMetadata.width, renderedTextureMetadata.height,
422 RendererCommon.convertMatrixToAndroidGraphicsMatrix(transformMatrix)); 441 RendererCommon.convertMatrixToAndroidGraphicsMatrix(transformMatrix));
423 442
424 VideoFrame frame = new VideoFrame(oesBuffer, renderedTextureMetadata.rotatio n, 443 VideoFrame frame = new VideoFrame(oesBuffer, renderedTextureMetadata.rotatio n,
425 renderedTextureMetadata.presentationTimestampUs * 1000); 444 renderedTextureMetadata.presentationTimestampUs * 1000);
426 callback.onDecodedFrame(frame, renderedTextureMetadata.decodeTimeMs, null /* qp */); 445 synchronized (callbackLock) {
446 if (callback != null) {
447 callback.onDecodedFrame(frame, renderedTextureMetadata.decodeTimeMs, nul l /* qp */);
448 }
449 }
427 frame.release(); 450 frame.release();
428 } 451 }
429 452
430 private void deliverByteFrame( 453 private void deliverByteFrame(
431 int result, MediaCodec.BufferInfo info, int rotation, Integer decodeTimeMs ) { 454 int result, MediaCodec.BufferInfo info, int rotation, Integer decodeTimeMs ) {
432 // Load dimensions from shared memory under the dimension lock. 455 // Load dimensions from shared memory under the dimension lock.
433 int width, height, stride, sliceHeight; 456 int width, height, stride, sliceHeight;
434 synchronized (dimensionLock) { 457 synchronized (dimensionLock) {
435 width = this.width; 458 width = this.width;
436 height = this.height; 459 height = this.height;
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
640 Logging.e(TAG, "Media decoder stop failed", e); 663 Logging.e(TAG, "Media decoder stop failed", e);
641 } 664 }
642 try { 665 try {
643 codec.release(); 666 codec.release();
644 } catch (Exception e) { 667 } catch (Exception e) {
645 Logging.e(TAG, "Media decoder release failed", e); 668 Logging.e(TAG, "Media decoder release failed", e);
646 // Propagate exceptions caught during release back to the main thread. 669 // Propagate exceptions caught during release back to the main thread.
647 shutdownException = e; 670 shutdownException = e;
648 } 671 }
649 codec = null; 672 codec = null;
650 callback = null; 673 synchronized (callbackLock) {
674 callback = null;
675 }
651 outputThread = null; 676 outputThread = null;
652 frameInfos.clear(); 677 frameInfos.clear();
653 Logging.d(TAG, "Release on output thread done"); 678 Logging.d(TAG, "Release on output thread done");
654 } 679 }
655 680
656 private void waitOutputBuffersReleasedOnOutputThread() { 681 private void waitOutputBuffersReleasedOnOutputThread() {
657 outputThreadChecker.checkIsOnValidThread(); 682 outputThreadChecker.checkIsOnValidThread();
658 synchronized (activeOutputBuffersLock) { 683 synchronized (activeOutputBuffersLock) {
659 while (activeOutputBuffers > 0) { 684 while (activeOutputBuffers > 0) {
660 Logging.d(TAG, "Waiting for all frames to be released."); 685 Logging.d(TAG, "Waiting for all frames to be released.");
(...skipping 15 matching lines...) Expand all
676 701
677 private boolean isSupportedColorFormat(int colorFormat) { 702 private boolean isSupportedColorFormat(int colorFormat) {
678 for (int supported : MediaCodecUtils.DECODER_COLOR_FORMATS) { 703 for (int supported : MediaCodecUtils.DECODER_COLOR_FORMATS) {
679 if (supported == colorFormat) { 704 if (supported == colorFormat) {
680 return true; 705 return true;
681 } 706 }
682 } 707 }
683 return false; 708 return false;
684 } 709 }
685 } 710 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698