1 /* 2 * Copyright 2013 The WebRTC project authors. All Rights Reserved. 3 * 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 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import android.support.annotation.Nullable; 14 15 /** 16 * Java wrapper of native AndroidVideoTrackSource. 17 */ 18 public class VideoSource extends MediaSource { 19 /** Simple aspect ratio clas for use in constraining output format. */ 20 public static class AspectRatio { 21 public static final AspectRatio UNDEFINED = new AspectRatio(/* width= */ 0, /* height= */ 0); 22 23 public final int width; 24 public final int height; 25 AspectRatio(int width, int height)26 public AspectRatio(int width, int height) { 27 this.width = width; 28 this.height = height; 29 } 30 } 31 32 private final NativeAndroidVideoTrackSource nativeAndroidVideoTrackSource; 33 private final Object videoProcessorLock = new Object(); 34 @Nullable private VideoProcessor videoProcessor; 35 private boolean isCapturerRunning; 36 37 private final CapturerObserver capturerObserver = new CapturerObserver() { 38 @Override 39 public void onCapturerStarted(boolean success) { 40 nativeAndroidVideoTrackSource.setState(success); 41 synchronized (videoProcessorLock) { 42 isCapturerRunning = success; 43 if (videoProcessor != null) { 44 videoProcessor.onCapturerStarted(success); 45 } 46 } 47 } 48 49 @Override 50 public void onCapturerStopped() { 51 nativeAndroidVideoTrackSource.setState(/* isLive= */ false); 52 synchronized (videoProcessorLock) { 53 isCapturerRunning = false; 54 if (videoProcessor != null) { 55 videoProcessor.onCapturerStopped(); 56 } 57 } 58 } 59 60 @Override 61 public void onFrameCaptured(VideoFrame frame) { 62 final VideoProcessor.FrameAdaptationParameters parameters = 63 nativeAndroidVideoTrackSource.adaptFrame(frame); 64 synchronized (videoProcessorLock) { 65 if (videoProcessor != null) { 66 videoProcessor.onFrameCaptured(frame, parameters); 67 return; 68 } 69 } 70 71 VideoFrame adaptedFrame = VideoProcessor.applyFrameAdaptationParameters(frame, parameters); 72 if (adaptedFrame != null) { 73 nativeAndroidVideoTrackSource.onFrameCaptured(adaptedFrame); 74 adaptedFrame.release(); 75 } 76 } 77 }; 78 VideoSource(long nativeSource)79 public VideoSource(long nativeSource) { 80 super(nativeSource); 81 this.nativeAndroidVideoTrackSource = new NativeAndroidVideoTrackSource(nativeSource); 82 } 83 84 /** 85 * Calling this function will cause frames to be scaled down to the requested resolution. Also, 86 * frames will be cropped to match the requested aspect ratio, and frames will be dropped to match 87 * the requested fps. The requested aspect ratio is orientation agnostic and will be adjusted to 88 * maintain the input orientation, so it doesn't matter if e.g. 1280x720 or 720x1280 is requested. 89 */ adaptOutputFormat(int width, int height, int fps)90 public void adaptOutputFormat(int width, int height, int fps) { 91 final int maxSide = Math.max(width, height); 92 final int minSide = Math.min(width, height); 93 adaptOutputFormat(maxSide, minSide, minSide, maxSide, fps); 94 } 95 96 /** 97 * Same as above, but allows setting two different target resolutions depending on incoming 98 * frame orientation. This gives more fine-grained control and can e.g. be used to force landscape 99 * video to be cropped to portrait video. 100 */ adaptOutputFormat( int landscapeWidth, int landscapeHeight, int portraitWidth, int portraitHeight, int fps)101 public void adaptOutputFormat( 102 int landscapeWidth, int landscapeHeight, int portraitWidth, int portraitHeight, int fps) { 103 adaptOutputFormat(new AspectRatio(landscapeWidth, landscapeHeight), 104 /* maxLandscapePixelCount= */ landscapeWidth * landscapeHeight, 105 new AspectRatio(portraitWidth, portraitHeight), 106 /* maxPortraitPixelCount= */ portraitWidth * portraitHeight, fps); 107 } 108 109 /** Same as above, with even more control as each constraint is optional. */ adaptOutputFormat(AspectRatio targetLandscapeAspectRatio, @Nullable Integer maxLandscapePixelCount, AspectRatio targetPortraitAspectRatio, @Nullable Integer maxPortraitPixelCount, @Nullable Integer maxFps)110 public void adaptOutputFormat(AspectRatio targetLandscapeAspectRatio, 111 @Nullable Integer maxLandscapePixelCount, AspectRatio targetPortraitAspectRatio, 112 @Nullable Integer maxPortraitPixelCount, @Nullable Integer maxFps) { 113 nativeAndroidVideoTrackSource.adaptOutputFormat(targetLandscapeAspectRatio, 114 maxLandscapePixelCount, targetPortraitAspectRatio, maxPortraitPixelCount, maxFps); 115 } 116 setIsScreencast(boolean isScreencast)117 public void setIsScreencast(boolean isScreencast) { 118 nativeAndroidVideoTrackSource.setIsScreencast(isScreencast); 119 } 120 121 /** 122 * Hook for injecting a custom video processor before frames are passed onto WebRTC. The frames 123 * will be cropped and scaled depending on CPU and network conditions before they are passed to 124 * the video processor. Frames will be delivered to the video processor on the same thread they 125 * are passed to this object. The video processor is allowed to deliver the processed frames 126 * back on any thread. 127 */ setVideoProcessor(@ullable VideoProcessor newVideoProcessor)128 public void setVideoProcessor(@Nullable VideoProcessor newVideoProcessor) { 129 synchronized (videoProcessorLock) { 130 if (videoProcessor != null) { 131 videoProcessor.setSink(/* sink= */ null); 132 if (isCapturerRunning) { 133 videoProcessor.onCapturerStopped(); 134 } 135 } 136 videoProcessor = newVideoProcessor; 137 if (newVideoProcessor != null) { 138 newVideoProcessor.setSink( 139 (frame) 140 -> runWithReference(() -> nativeAndroidVideoTrackSource.onFrameCaptured(frame))); 141 if (isCapturerRunning) { 142 newVideoProcessor.onCapturerStarted(/* success= */ true); 143 } 144 } 145 } 146 } 147 getCapturerObserver()148 public CapturerObserver getCapturerObserver() { 149 return capturerObserver; 150 } 151 152 /** Returns a pointer to webrtc::VideoTrackSourceInterface. */ getNativeVideoTrackSource()153 long getNativeVideoTrackSource() { 154 return getNativeMediaSource(); 155 } 156 157 @Override dispose()158 public void dispose() { 159 setVideoProcessor(/* newVideoProcessor= */ null); 160 super.dispose(); 161 } 162 } 163