blob: ba31f7e6de545dcd354ef0030bc5b30de33a2ed7 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "sdk/android/src/jni/androidvideotracksource.h"
Sami Kalliomaki16032122016-07-20 16:13:08 +020012
13#include <utility>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "rtc_base/logging.h"
16#include "sdk/android/src/jni/classreferenceholder.h"
zhihuang38ede132017-06-15 12:52:32 -070017
kthelgasonc8474172016-12-08 08:04:51 -080018namespace {
19// MediaCodec wants resolution to be divisible by 2.
20const int kRequiredResolutionAlignment = 2;
21}
22
Sami Kalliomaki16032122016-07-20 16:13:08 +020023namespace webrtc {
magjeda3d4f682017-08-28 16:24:06 -070024namespace jni {
Sami Kalliomaki16032122016-07-20 16:13:08 +020025
Sami Kalliomäki58c742c2017-06-02 09:51:39 +020026AndroidVideoTrackSource::AndroidVideoTrackSource(
27 rtc::Thread* signaling_thread,
28 JNIEnv* jni,
29 jobject j_surface_texture_helper,
30 bool is_screencast)
kthelgasonc8474172016-12-08 08:04:51 -080031 : AdaptedVideoTrackSource(kRequiredResolutionAlignment),
32 signaling_thread_(signaling_thread),
magjeda3d4f682017-08-28 16:24:06 -070033 surface_texture_helper_(new rtc::RefCountedObject<SurfaceTextureHelper>(
34 jni,
35 j_surface_texture_helper)),
arsanyb75f2542016-08-31 18:50:52 -070036 is_screencast_(is_screencast) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010037 RTC_LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
Sami Kalliomaki16032122016-07-20 16:13:08 +020038 camera_thread_checker_.DetachFromThread();
sakalbe910462017-08-11 00:26:05 -070039
40 jclass j_video_frame_buffer_class =
magjeda3d4f682017-08-28 16:24:06 -070041 FindClass(jni, "org/webrtc/VideoFrame$Buffer");
sakalbe910462017-08-11 00:26:05 -070042 j_crop_and_scale_id_ =
43 jni->GetMethodID(j_video_frame_buffer_class, "cropAndScale",
44 "(IIIIII)Lorg/webrtc/VideoFrame$Buffer;");
Sami Kalliomaki16032122016-07-20 16:13:08 +020045}
46
Sami Kalliomaki16032122016-07-20 16:13:08 +020047void AndroidVideoTrackSource::SetState(SourceState state) {
48 if (rtc::Thread::Current() != signaling_thread_) {
49 invoker_.AsyncInvoke<void>(
50 RTC_FROM_HERE, signaling_thread_,
51 rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
52 return;
53 }
54
55 if (state_ != state) {
56 state_ = state;
57 FireOnChanged();
58 }
59}
60
Sami Kalliomaki16032122016-07-20 16:13:08 +020061void AndroidVideoTrackSource::OnByteBufferFrameCaptured(const void* frame_data,
62 int length,
63 int width,
64 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +020065 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +020066 int64_t timestamp_ns) {
67 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +020068
nisse6f5a6c32016-09-22 01:25:59 -070069 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
70 int64_t translated_camera_time_us =
71 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
72
Sami Kalliomaki16032122016-07-20 16:13:08 +020073 int adapted_width;
74 int adapted_height;
75 int crop_width;
76 int crop_height;
77 int crop_x;
78 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +020079
sakal0ce6aaf2016-11-22 01:26:16 -080080 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
81 &adapted_height, &crop_width, &crop_height, &crop_x,
82 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +020083 return;
84 }
85
Sami Kalliomaki16032122016-07-20 16:13:08 +020086 const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
87 const uint8_t* uv_plane = y_plane + width * height;
magjed1a7ef1f2016-09-17 02:39:03 -070088 const int uv_width = (width + 1) / 2;
Sami Kalliomaki16032122016-07-20 16:13:08 +020089
90 RTC_CHECK_GE(length, width * height + 2 * uv_width * ((height + 1) / 2));
91
92 // Can only crop at even pixels.
93 crop_x &= ~1;
94 crop_y &= ~1;
magjed1a7ef1f2016-09-17 02:39:03 -070095 // Crop just by modifying pointers.
96 y_plane += width * crop_y + crop_x;
97 uv_plane += uv_width * crop_y + crop_x;
Sami Kalliomaki16032122016-07-20 16:13:08 +020098
Magnus Jedvert3093ef12017-06-19 15:04:19 +020099 rtc::scoped_refptr<I420Buffer> buffer =
magjed1a7ef1f2016-09-17 02:39:03 -0700100 buffer_pool_.CreateBuffer(adapted_width, adapted_height);
101
102 nv12toi420_scaler_.NV12ToI420Scale(
sakal0ce6aaf2016-11-22 01:26:16 -0800103 y_plane, width, uv_plane, uv_width * 2, crop_width, crop_height,
magjed1a7ef1f2016-09-17 02:39:03 -0700104 buffer->MutableDataY(), buffer->StrideY(),
jackychened0b0db2016-09-09 16:15:11 -0700105 // Swap U and V, since we have NV21, not NV12.
sakal0ce6aaf2016-11-22 01:26:16 -0800106 buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
107 buffer->StrideU(), buffer->width(), buffer->height());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200108
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200109 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200110}
111
112void AndroidVideoTrackSource::OnTextureFrameCaptured(
113 int width,
114 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200115 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +0200116 int64_t timestamp_ns,
magjeda3d4f682017-08-28 16:24:06 -0700117 const NativeHandleImpl& handle) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200118 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200119
nisse6f5a6c32016-09-22 01:25:59 -0700120 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
121 int64_t translated_camera_time_us =
122 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
123
Sami Kalliomaki16032122016-07-20 16:13:08 +0200124 int adapted_width;
125 int adapted_height;
126 int crop_width;
127 int crop_height;
128 int crop_x;
129 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200130
sakal0ce6aaf2016-11-22 01:26:16 -0800131 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
132 &adapted_height, &crop_width, &crop_height, &crop_x,
133 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200134 surface_texture_helper_->ReturnTextureFrame();
135 return;
136 }
137
magjeda3d4f682017-08-28 16:24:06 -0700138 Matrix matrix = handle.sampling_matrix;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200139
140 matrix.Crop(crop_width / static_cast<float>(width),
141 crop_height / static_cast<float>(height),
142 crop_x / static_cast<float>(width),
143 crop_y / static_cast<float>(height));
144
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200145 // Note that apply_rotation() may change under our feet, so we should only
146 // check once.
147 if (apply_rotation()) {
148 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200149 std::swap(adapted_width, adapted_height);
150 }
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200151 matrix.Rotate(rotation);
152 rotation = kVideoRotation_0;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200153 }
154
magjeda3d4f682017-08-28 16:24:06 -0700155 OnFrame(VideoFrame(surface_texture_helper_->CreateTextureFrame(
156 adapted_width, adapted_height,
157 NativeHandleImpl(handle.oes_texture_id, matrix)),
158 rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200159}
160
sakalbe910462017-08-11 00:26:05 -0700161void AndroidVideoTrackSource::OnFrameCaptured(JNIEnv* jni,
162 int width,
163 int height,
164 int64_t timestamp_ns,
165 VideoRotation rotation,
166 jobject j_video_frame_buffer) {
167 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
168
169 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
170 int64_t translated_camera_time_us =
171 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
172
173 int adapted_width;
174 int adapted_height;
175 int crop_width;
176 int crop_height;
177 int crop_x;
178 int crop_y;
179
180 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
181 &adapted_height, &crop_width, &crop_height, &crop_x,
182 &crop_y)) {
183 return;
184 }
185
186 jobject j_adapted_video_frame_buffer = jni->CallObjectMethod(
187 j_video_frame_buffer, j_crop_and_scale_id_, crop_x, crop_y, crop_width,
188 crop_height, adapted_width, adapted_height);
189
190 rtc::scoped_refptr<VideoFrameBuffer> buffer =
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100191 AndroidVideoBuffer::Adopt(jni, j_adapted_video_frame_buffer);
sakalbe910462017-08-11 00:26:05 -0700192
193 // AdaptedVideoTrackSource handles applying rotation for I420 frames.
Sami Kalliomäkicbc4b1d2017-09-29 11:50:25 +0200194 if (apply_rotation() && rotation != kVideoRotation_0) {
sakalbe910462017-08-11 00:26:05 -0700195 buffer = buffer->ToI420();
196 }
197
198 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
199}
200
Sami Kalliomaki16032122016-07-20 16:13:08 +0200201void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
202 int height,
203 int fps) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200204 cricket::VideoFormat format(width, height,
205 cricket::VideoFormat::FpsToInterval(fps), 0);
nisse6f5a6c32016-09-22 01:25:59 -0700206 video_adapter()->OnOutputFormatRequest(format);
Sami Kalliomaki16032122016-07-20 16:13:08 +0200207}
208
209} // namespace webrtc
magjeda3d4f682017-08-28 16:24:06 -0700210} // namespace webrtc