blob: f49cc30c8c975189485005cbb2ad7e4d87c44f0b [file] [log] [blame]
Sami Kalliomaki16032122016-07-20 16:13:08 +02001/*
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 "webrtc/api/androidvideotracksource.h"
12
13#include <utility>
14
15namespace webrtc {
16
17AndroidVideoTrackSource::AndroidVideoTrackSource(rtc::Thread* signaling_thread,
18 JNIEnv* jni,
arsanyb75f2542016-08-31 18:50:52 -070019 jobject j_egl_context,
20 bool is_screencast)
Sami Kalliomaki16032122016-07-20 16:13:08 +020021 : signaling_thread_(signaling_thread),
22 surface_texture_helper_(webrtc_jni::SurfaceTextureHelper::create(
23 jni,
24 "Camera SurfaceTextureHelper",
arsanyb75f2542016-08-31 18:50:52 -070025 j_egl_context)),
26 is_screencast_(is_screencast) {
Sami Kalliomaki16032122016-07-20 16:13:08 +020027 LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
28 worker_thread_checker_.DetachFromThread();
29 camera_thread_checker_.DetachFromThread();
30}
31
32bool AndroidVideoTrackSource::GetStats(AndroidVideoTrackSource::Stats* stats) {
33 rtc::CritScope lock(&stats_crit_);
34
35 if (!stats_) {
36 return false;
37 }
38
39 *stats = *stats_;
40 return true;
41}
42
43void AndroidVideoTrackSource::SetState(SourceState state) {
44 if (rtc::Thread::Current() != signaling_thread_) {
45 invoker_.AsyncInvoke<void>(
46 RTC_FROM_HERE, signaling_thread_,
47 rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
48 return;
49 }
50
51 if (state_ != state) {
52 state_ = state;
53 FireOnChanged();
54 }
55}
56
57void AndroidVideoTrackSource::AddOrUpdateSink(
58 rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
59 const rtc::VideoSinkWants& wants) {
60 RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
61
62 broadcaster_.AddOrUpdateSink(sink, wants);
63 OnSinkWantsChanged(broadcaster_.wants());
64}
65
66void AndroidVideoTrackSource::RemoveSink(
67 rtc::VideoSinkInterface<cricket::VideoFrame>* sink) {
68 RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
69
70 broadcaster_.RemoveSink(sink);
71 OnSinkWantsChanged(broadcaster_.wants());
72}
73
74void AndroidVideoTrackSource::OnSinkWantsChanged(
75 const rtc::VideoSinkWants& wants) {
76 {
77 rtc::CritScope lock(&apply_rotation_crit_);
78 apply_rotation_ = wants.rotation_applied;
79 }
80
81 video_adapter_.OnResolutionRequest(wants.max_pixel_count,
82 wants.max_pixel_count_step_up);
83}
84
85void AndroidVideoTrackSource::OnByteBufferFrameCaptured(const void* frame_data,
86 int length,
87 int width,
88 int height,
89 int rotation,
90 int64_t timestamp_ns) {
91 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
92 RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
93 rotation == 270);
94
95 int adapted_width;
96 int adapted_height;
97 int crop_width;
98 int crop_height;
99 int crop_x;
100 int crop_y;
101 int64_t translated_camera_time_us;
102
103 if (!AdaptFrame(width, height, timestamp_ns / rtc::kNumNanosecsPerMicrosec,
104 &adapted_width, &adapted_height, &crop_width, &crop_height,
105 &crop_x, &crop_y, &translated_camera_time_us)) {
106 return;
107 }
108
jackychened0b0db2016-09-09 16:15:11 -0700109 int rotated_width = crop_width;
110 int rotated_height = crop_height;
111
112 rtc::CritScope lock(&apply_rotation_crit_);
113 if (apply_rotation_ && (rotation == 90 || rotation == 270)) {
114 std::swap(adapted_width, adapted_height);
115 std::swap(rotated_width, rotated_height);
116 }
117
118 rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer =
119 pre_scale_pool_.CreateBuffer(rotated_width, rotated_height);
120
Sami Kalliomaki16032122016-07-20 16:13:08 +0200121 const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
122 const uint8_t* uv_plane = y_plane + width * height;
jackychened0b0db2016-09-09 16:15:11 -0700123 int uv_width = (width + 1) / 2;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200124
125 RTC_CHECK_GE(length, width * height + 2 * uv_width * ((height + 1) / 2));
126
127 // Can only crop at even pixels.
128 crop_x &= ~1;
129 crop_y &= ~1;
130
jackychened0b0db2016-09-09 16:15:11 -0700131 libyuv::NV12ToI420Rotate(
132 y_plane + width * crop_y + crop_x, width,
133 uv_plane + uv_width * crop_y + crop_x, width, buffer->MutableDataY(),
134 buffer->StrideY(),
135 // Swap U and V, since we have NV21, not NV12.
136 buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
137 buffer->StrideU(), crop_width, crop_height,
138 static_cast<libyuv::RotationMode>(apply_rotation_ ? rotation : 0));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200139
jackychened0b0db2016-09-09 16:15:11 -0700140 if (adapted_width != buffer->width() || adapted_height != buffer->height()) {
141 rtc::scoped_refptr<webrtc::I420Buffer> scaled_buffer(
142 post_scale_pool_.CreateBuffer(adapted_width, adapted_height));
143 scaled_buffer->ScaleFrom(buffer);
144 buffer = scaled_buffer;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200145 }
146
147 OnFrame(cricket::WebRtcVideoFrame(
148 buffer,
149 apply_rotation_ ? webrtc::kVideoRotation_0
150 : static_cast<webrtc::VideoRotation>(rotation),
Sergey Ulanov19ee1e6eb2016-08-01 13:35:55 -0700151 translated_camera_time_us, 0),
Sami Kalliomaki16032122016-07-20 16:13:08 +0200152 width, height);
153}
154
155void AndroidVideoTrackSource::OnTextureFrameCaptured(
156 int width,
157 int height,
158 int rotation,
159 int64_t timestamp_ns,
160 const webrtc_jni::NativeHandleImpl& handle) {
161 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
162 RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 ||
163 rotation == 270);
164
165 int adapted_width;
166 int adapted_height;
167 int crop_width;
168 int crop_height;
169 int crop_x;
170 int crop_y;
171 int64_t translated_camera_time_us;
172
173 if (!AdaptFrame(width, height, timestamp_ns / rtc::kNumNanosecsPerMicrosec,
174 &adapted_width, &adapted_height, &crop_width, &crop_height,
175 &crop_x, &crop_y, &translated_camera_time_us)) {
176 surface_texture_helper_->ReturnTextureFrame();
177 return;
178 }
179
180 webrtc_jni::Matrix matrix = handle.sampling_matrix;
181
182 matrix.Crop(crop_width / static_cast<float>(width),
183 crop_height / static_cast<float>(height),
184 crop_x / static_cast<float>(width),
185 crop_y / static_cast<float>(height));
186
187 rtc::CritScope lock(&apply_rotation_crit_);
188 if (apply_rotation_) {
189 if (rotation == webrtc::kVideoRotation_90 ||
190 rotation == webrtc::kVideoRotation_270) {
191 std::swap(adapted_width, adapted_height);
192 }
193 matrix.Rotate(static_cast<webrtc::VideoRotation>(rotation));
194 }
195
196 OnFrame(cricket::WebRtcVideoFrame(
197 surface_texture_helper_->CreateTextureFrame(
198 adapted_width, adapted_height,
199 webrtc_jni::NativeHandleImpl(handle.oes_texture_id, matrix)),
200 apply_rotation_ ? webrtc::kVideoRotation_0
201 : static_cast<webrtc::VideoRotation>(rotation),
Sergey Ulanov19ee1e6eb2016-08-01 13:35:55 -0700202 translated_camera_time_us, 0),
Sami Kalliomaki16032122016-07-20 16:13:08 +0200203 width, height);
204}
205
206void AndroidVideoTrackSource::OnFrame(const cricket::VideoFrame& frame,
207 int width,
208 int height) {
209 {
210 rtc::CritScope lock(&stats_crit_);
211 stats_ = rtc::Optional<AndroidVideoTrackSource::Stats>({width, height});
212 }
213
214 broadcaster_.OnFrame(frame);
215}
216
217void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
218 int height,
219 int fps) {
220 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
221
222 cricket::VideoFormat format(width, height,
223 cricket::VideoFormat::FpsToInterval(fps), 0);
224 video_adapter_.OnOutputFormatRequest(format);
225}
226
227bool AndroidVideoTrackSource::AdaptFrame(int width,
228 int height,
229 int64_t camera_time_us,
230 int* out_width,
231 int* out_height,
232 int* crop_width,
233 int* crop_height,
234 int* crop_x,
235 int* crop_y,
236 int64_t* translated_camera_time_us) {
237 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
238
239 int64_t system_time_us = rtc::TimeMicros();
nissea0758482016-09-14 00:37:00 -0700240 *translated_camera_time_us =
241 timestamp_aligner_.TranslateTimestamp(camera_time_us, system_time_us);
Sami Kalliomaki16032122016-07-20 16:13:08 +0200242
243 if (!broadcaster_.frame_wanted()) {
244 return false;
245 }
246
247 if (!video_adapter_.AdaptFrameResolution(
248 width, height, camera_time_us * rtc::kNumNanosecsPerMicrosec,
249 crop_width, crop_height, out_width, out_height)) {
250 // VideoAdapter dropped the frame.
251 return false;
252 }
253 *crop_x = (width - *crop_width) / 2;
254 *crop_y = (height - *crop_height) / 2;
255
Sami Kalliomaki16032122016-07-20 16:13:08 +0200256 return true;
257}
258
259} // namespace webrtc