1 /*
2 * Copyright (c) 2016 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 #include "sdk/android/src/jni/android_video_track_source.h"
12
13 #include <utility>
14
15 #include "rtc_base/logging.h"
16 #include "sdk/android/generated_video_jni/NativeAndroidVideoTrackSource_jni.h"
17 #include "sdk/android/src/jni/video_frame.h"
18
19 namespace webrtc {
20 namespace jni {
21
22 namespace {
23 // MediaCodec wants resolution to be divisible by 2.
24 const int kRequiredResolutionAlignment = 2;
25
jintToVideoRotation(jint rotation)26 VideoRotation jintToVideoRotation(jint rotation) {
27 RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
28 rotation == 270);
29 return static_cast<VideoRotation>(rotation);
30 }
31
OptionalAspectRatio(jint j_width,jint j_height)32 absl::optional<std::pair<int, int>> OptionalAspectRatio(jint j_width,
33 jint j_height) {
34 if (j_width > 0 && j_height > 0)
35 return std::pair<int, int>(j_width, j_height);
36 return absl::nullopt;
37 }
38
39 } // namespace
40
AndroidVideoTrackSource(rtc::Thread * signaling_thread,JNIEnv * jni,bool is_screencast,bool align_timestamps)41 AndroidVideoTrackSource::AndroidVideoTrackSource(rtc::Thread* signaling_thread,
42 JNIEnv* jni,
43 bool is_screencast,
44 bool align_timestamps)
45 : AdaptedVideoTrackSource(kRequiredResolutionAlignment),
46 signaling_thread_(signaling_thread),
47 is_screencast_(is_screencast),
48 align_timestamps_(align_timestamps) {
49 RTC_LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
50 }
51 AndroidVideoTrackSource::~AndroidVideoTrackSource() = default;
52
is_screencast() const53 bool AndroidVideoTrackSource::is_screencast() const {
54 return is_screencast_.load();
55 }
56
needs_denoising() const57 absl::optional<bool> AndroidVideoTrackSource::needs_denoising() const {
58 return false;
59 }
60
SetState(JNIEnv * env,jboolean j_is_live)61 void AndroidVideoTrackSource::SetState(JNIEnv* env,
62 jboolean j_is_live) {
63 const SourceState state = j_is_live ? kLive : kEnded;
64 if (state_.exchange(state) != state) {
65 if (rtc::Thread::Current() == signaling_thread_) {
66 FireOnChanged();
67 } else {
68 // TODO(sakal): Is this even necessary, does FireOnChanged have to be
69 // called from signaling thread?
70 signaling_thread_->PostTask([this] { FireOnChanged(); });
71 }
72 }
73 }
74
state() const75 AndroidVideoTrackSource::SourceState AndroidVideoTrackSource::state() const {
76 return state_.load();
77 }
78
remote() const79 bool AndroidVideoTrackSource::remote() const {
80 return false;
81 }
82
SetIsScreencast(JNIEnv * env,jboolean j_is_screencast)83 void AndroidVideoTrackSource::SetIsScreencast(JNIEnv* env,
84 jboolean j_is_screencast) {
85 is_screencast_.store(j_is_screencast);
86 }
87
AdaptFrame(JNIEnv * env,jint j_width,jint j_height,jint j_rotation,jlong j_timestamp_ns)88 ScopedJavaLocalRef<jobject> AndroidVideoTrackSource::AdaptFrame(
89 JNIEnv* env,
90 jint j_width,
91 jint j_height,
92 jint j_rotation,
93 jlong j_timestamp_ns) {
94 const VideoRotation rotation = jintToVideoRotation(j_rotation);
95
96 const int64_t camera_time_us = j_timestamp_ns / rtc::kNumNanosecsPerMicrosec;
97 const int64_t aligned_timestamp_ns =
98 align_timestamps_ ? rtc::kNumNanosecsPerMicrosec *
99 timestamp_aligner_.TranslateTimestamp(
100 camera_time_us, rtc::TimeMicros())
101 : j_timestamp_ns;
102
103 int adapted_width = 0;
104 int adapted_height = 0;
105 int crop_width = 0;
106 int crop_height = 0;
107 int crop_x = 0;
108 int crop_y = 0;
109 bool drop;
110
111 // TODO(magjed): Move this logic to users of NativeAndroidVideoTrackSource
112 // instead, in order to keep this native wrapping layer as thin as possible.
113 if (rotation % 180 == 0) {
114 drop = !rtc::AdaptedVideoTrackSource::AdaptFrame(
115 j_width, j_height, camera_time_us, &adapted_width, &adapted_height,
116 &crop_width, &crop_height, &crop_x, &crop_y);
117 } else {
118 // Swap all width/height and x/y.
119 drop = !rtc::AdaptedVideoTrackSource::AdaptFrame(
120 j_height, j_width, camera_time_us, &adapted_height, &adapted_width,
121 &crop_height, &crop_width, &crop_y, &crop_x);
122 }
123
124 return Java_NativeAndroidVideoTrackSource_createFrameAdaptationParameters(
125 env, crop_x, crop_y, crop_width, crop_height, adapted_width,
126 adapted_height, aligned_timestamp_ns, drop);
127 }
128
OnFrameCaptured(JNIEnv * env,jint j_rotation,jlong j_timestamp_ns,const JavaRef<jobject> & j_video_frame_buffer)129 void AndroidVideoTrackSource::OnFrameCaptured(
130 JNIEnv* env,
131 jint j_rotation,
132 jlong j_timestamp_ns,
133 const JavaRef<jobject>& j_video_frame_buffer) {
134 rtc::scoped_refptr<VideoFrameBuffer> buffer =
135 JavaToNativeFrameBuffer(env, j_video_frame_buffer);
136 const VideoRotation rotation = jintToVideoRotation(j_rotation);
137
138 // AdaptedVideoTrackSource handles applying rotation for I420 frames.
139 if (apply_rotation() && rotation != kVideoRotation_0)
140 buffer = buffer->ToI420();
141
142 OnFrame(VideoFrame::Builder()
143 .set_video_frame_buffer(buffer)
144 .set_rotation(rotation)
145 .set_timestamp_us(j_timestamp_ns / rtc::kNumNanosecsPerMicrosec)
146 .build());
147 }
148
AdaptOutputFormat(JNIEnv * env,jint j_landscape_width,jint j_landscape_height,const JavaRef<jobject> & j_max_landscape_pixel_count,jint j_portrait_width,jint j_portrait_height,const JavaRef<jobject> & j_max_portrait_pixel_count,const JavaRef<jobject> & j_max_fps)149 void AndroidVideoTrackSource::AdaptOutputFormat(
150 JNIEnv* env,
151 jint j_landscape_width,
152 jint j_landscape_height,
153 const JavaRef<jobject>& j_max_landscape_pixel_count,
154 jint j_portrait_width,
155 jint j_portrait_height,
156 const JavaRef<jobject>& j_max_portrait_pixel_count,
157 const JavaRef<jobject>& j_max_fps) {
158 video_adapter()->OnOutputFormatRequest(
159 OptionalAspectRatio(j_landscape_width, j_landscape_height),
160 JavaToNativeOptionalInt(env, j_max_landscape_pixel_count),
161 OptionalAspectRatio(j_portrait_width, j_portrait_height),
162 JavaToNativeOptionalInt(env, j_max_portrait_pixel_count),
163 JavaToNativeOptionalInt(env, j_max_fps));
164 }
165
166 } // namespace jni
167 } // namespace webrtc
168