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