OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2013 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 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
447 format.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrateBps); | 447 format.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrateBps); |
448 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); | 448 format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); |
449 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); | 449 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat); |
450 format.setInteger(MediaFormat.KEY_FRAME_RATE, targetFps); | 450 format.setInteger(MediaFormat.KEY_FRAME_RATE, targetFps); |
451 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); | 451 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameIntervalSec); |
452 Logging.d(TAG, " Format: " + format); | 452 Logging.d(TAG, " Format: " + format); |
453 mediaCodec = createByCodecName(properties.codecName); | 453 mediaCodec = createByCodecName(properties.codecName); |
454 this.type = type; | 454 this.type = type; |
455 if (mediaCodec == null) { | 455 if (mediaCodec == null) { |
456 Logging.e(TAG, "Can not create media encoder"); | 456 Logging.e(TAG, "Can not create media encoder"); |
| 457 release(); |
457 return false; | 458 return false; |
458 } | 459 } |
459 mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
; | 460 mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
; |
460 | 461 |
461 if (useSurface) { | 462 if (useSurface) { |
462 eglBase = new EglBase14(sharedContext, EglBase.CONFIG_RECORDABLE); | 463 eglBase = new EglBase14(sharedContext, EglBase.CONFIG_RECORDABLE); |
463 // Create an input surface and keep a reference since we must release th
e surface when done. | 464 // Create an input surface and keep a reference since we must release th
e surface when done. |
464 inputSurface = mediaCodec.createInputSurface(); | 465 inputSurface = mediaCodec.createInputSurface(); |
465 eglBase.createSurface(inputSurface); | 466 eglBase.createSurface(inputSurface); |
466 drawer = new GlRectDrawer(); | 467 drawer = new GlRectDrawer(); |
467 } | 468 } |
468 mediaCodec.start(); | 469 mediaCodec.start(); |
469 outputBuffers = mediaCodec.getOutputBuffers(); | 470 outputBuffers = mediaCodec.getOutputBuffers(); |
470 Logging.d(TAG, "Output buffers: " + outputBuffers.length); | 471 Logging.d(TAG, "Output buffers: " + outputBuffers.length); |
471 | 472 |
472 } catch (IllegalStateException e) { | 473 } catch (IllegalStateException e) { |
473 Logging.e(TAG, "initEncode failed", e); | 474 Logging.e(TAG, "initEncode failed", e); |
| 475 release(); |
474 return false; | 476 return false; |
475 } | 477 } |
476 return true; | 478 return true; |
477 } | 479 } |
478 | 480 |
479 ByteBuffer[] getInputBuffers() { | 481 ByteBuffer[] getInputBuffers() { |
480 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | 482 ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); |
481 Logging.d(TAG, "Input buffers: " + inputBuffers.length); | 483 Logging.d(TAG, "Input buffers: " + inputBuffers.length); |
482 return inputBuffers; | 484 return inputBuffers; |
483 } | 485 } |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 } catch (RuntimeException e) { | 539 } catch (RuntimeException e) { |
538 Logging.e(TAG, "encodeTexture failed", e); | 540 Logging.e(TAG, "encodeTexture failed", e); |
539 return false; | 541 return false; |
540 } | 542 } |
541 } | 543 } |
542 | 544 |
543 void release() { | 545 void release() { |
544 Logging.d(TAG, "Java releaseEncoder"); | 546 Logging.d(TAG, "Java releaseEncoder"); |
545 checkOnMediaCodecThread(); | 547 checkOnMediaCodecThread(); |
546 | 548 |
547 // Run Mediacodec stop() and release() on separate thread since sometime | 549 class CaughtException { |
548 // Mediacodec.stop() may hang. | 550 Exception e; |
549 final CountDownLatch releaseDone = new CountDownLatch(1); | 551 } |
| 552 final CaughtException caughtException = new CaughtException(); |
| 553 boolean stopHung = false; |
550 | 554 |
551 Runnable runMediaCodecRelease = new Runnable() { | 555 if (mediaCodec != null) { |
552 @Override | 556 // Run Mediacodec stop() and release() on separate thread since sometime |
553 public void run() { | 557 // Mediacodec.stop() may hang. |
554 try { | 558 final CountDownLatch releaseDone = new CountDownLatch(1); |
| 559 |
| 560 Runnable runMediaCodecRelease = new Runnable() { |
| 561 @Override |
| 562 public void run() { |
555 Logging.d(TAG, "Java releaseEncoder on release thread"); | 563 Logging.d(TAG, "Java releaseEncoder on release thread"); |
556 mediaCodec.stop(); | 564 try { |
557 mediaCodec.release(); | 565 mediaCodec.stop(); |
| 566 } catch (Exception e) { |
| 567 Logging.e(TAG, "Media encoder stop failed", e); |
| 568 } |
| 569 try { |
| 570 mediaCodec.release(); |
| 571 } catch (Exception e) { |
| 572 Logging.e(TAG, "Media encoder release failed", e); |
| 573 caughtException.e = e; |
| 574 } |
558 Logging.d(TAG, "Java releaseEncoder on release thread done"); | 575 Logging.d(TAG, "Java releaseEncoder on release thread done"); |
559 } catch (Exception e) { | 576 |
560 Logging.e(TAG, "Media encoder release failed", e); | 577 releaseDone.countDown(); |
561 } | 578 } |
562 releaseDone.countDown(); | 579 }; |
| 580 new Thread(runMediaCodecRelease).start(); |
| 581 |
| 582 if (!ThreadUtils.awaitUninterruptibly(releaseDone, MEDIA_CODEC_RELEASE_TIM
EOUT_MS)) { |
| 583 Logging.e(TAG, "Media encoder release timeout"); |
| 584 stopHung = true; |
563 } | 585 } |
564 }; | |
565 new Thread(runMediaCodecRelease).start(); | |
566 | 586 |
567 if (!ThreadUtils.awaitUninterruptibly(releaseDone, MEDIA_CODEC_RELEASE_TIMEO
UT_MS)) { | 587 mediaCodec = null; |
568 Logging.e(TAG, "Media encoder release timeout"); | |
569 codecErrors++; | |
570 if (errorCallback != null) { | |
571 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); | |
572 errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors); | |
573 } | |
574 } | 588 } |
575 | 589 |
576 mediaCodec = null; | |
577 mediaCodecThread = null; | 590 mediaCodecThread = null; |
578 if (drawer != null) { | 591 if (drawer != null) { |
579 drawer.release(); | 592 drawer.release(); |
580 drawer = null; | 593 drawer = null; |
581 } | 594 } |
582 if (eglBase != null) { | 595 if (eglBase != null) { |
583 eglBase.release(); | 596 eglBase.release(); |
584 eglBase = null; | 597 eglBase = null; |
585 } | 598 } |
586 if (inputSurface != null) { | 599 if (inputSurface != null) { |
587 inputSurface.release(); | 600 inputSurface.release(); |
588 inputSurface = null; | 601 inputSurface = null; |
589 } | 602 } |
590 runningInstance = null; | 603 runningInstance = null; |
| 604 |
| 605 if (stopHung) { |
| 606 codecErrors++; |
| 607 if (errorCallback != null) { |
| 608 Logging.e(TAG, "Invoke codec error callback. Errors: " + codecErrors); |
| 609 errorCallback.onMediaCodecVideoEncoderCriticalError(codecErrors); |
| 610 } |
| 611 throw new RuntimeException("Media encoder release timeout."); |
| 612 } |
| 613 |
| 614 // Re-throw any runtime exception caught inside the other thread. Since this
is an invoke, add |
| 615 // stack trace for the waiting thread as well. |
| 616 if (caughtException.e != null) { |
| 617 final RuntimeException runtimeException = new RuntimeException(caughtExcep
tion.e); |
| 618 runtimeException.setStackTrace(ThreadUtils.concatStackTraces( |
| 619 caughtException.e.getStackTrace(), runtimeException.getStackTrace())); |
| 620 throw runtimeException; |
| 621 } |
| 622 |
591 Logging.d(TAG, "Java releaseEncoder done"); | 623 Logging.d(TAG, "Java releaseEncoder done"); |
592 } | 624 } |
593 | 625 |
594 private boolean setRates(int kbps, int frameRate) { | 626 private boolean setRates(int kbps, int frameRate) { |
595 checkOnMediaCodecThread(); | 627 checkOnMediaCodecThread(); |
596 | 628 |
597 int codecBitrateBps = 1000 * kbps; | 629 int codecBitrateBps = 1000 * kbps; |
598 if (bitrateAdjustmentType == BitrateAdjustmentType.DYNAMIC_ADJUSTMENT) { | 630 if (bitrateAdjustmentType == BitrateAdjustmentType.DYNAMIC_ADJUSTMENT) { |
599 bitrateAccumulatorMax = codecBitrateBps / 8.0; | 631 bitrateAccumulatorMax = codecBitrateBps / 8.0; |
600 if (targetBitrateBps > 0 && codecBitrateBps < targetBitrateBps) { | 632 if (targetBitrateBps > 0 && codecBitrateBps < targetBitrateBps) { |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
781 checkOnMediaCodecThread(); | 813 checkOnMediaCodecThread(); |
782 try { | 814 try { |
783 mediaCodec.releaseOutputBuffer(index, false); | 815 mediaCodec.releaseOutputBuffer(index, false); |
784 return true; | 816 return true; |
785 } catch (IllegalStateException e) { | 817 } catch (IllegalStateException e) { |
786 Logging.e(TAG, "releaseOutputBuffer failed", e); | 818 Logging.e(TAG, "releaseOutputBuffer failed", e); |
787 return false; | 819 return false; |
788 } | 820 } |
789 } | 821 } |
790 } | 822 } |
OLD | NEW |