blob: 3913ed036cb34bce2513a839eeb4a6d06e8cad02 [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)),
sakalbe910462017-08-11 00:26:05 -070036 video_buffer_factory_(jni),
arsanyb75f2542016-08-31 18:50:52 -070037 is_screencast_(is_screencast) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010038 RTC_LOG(LS_INFO) << "AndroidVideoTrackSource ctor";
Sami Kalliomaki16032122016-07-20 16:13:08 +020039 camera_thread_checker_.DetachFromThread();
sakalbe910462017-08-11 00:26:05 -070040
41 jclass j_video_frame_buffer_class =
magjeda3d4f682017-08-28 16:24:06 -070042 FindClass(jni, "org/webrtc/VideoFrame$Buffer");
sakalbe910462017-08-11 00:26:05 -070043 j_crop_and_scale_id_ =
44 jni->GetMethodID(j_video_frame_buffer_class, "cropAndScale",
45 "(IIIIII)Lorg/webrtc/VideoFrame$Buffer;");
Sami Kalliomaki16032122016-07-20 16:13:08 +020046}
47
Sami Kalliomaki16032122016-07-20 16:13:08 +020048void AndroidVideoTrackSource::SetState(SourceState state) {
49 if (rtc::Thread::Current() != signaling_thread_) {
50 invoker_.AsyncInvoke<void>(
51 RTC_FROM_HERE, signaling_thread_,
52 rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
53 return;
54 }
55
56 if (state_ != state) {
57 state_ = state;
58 FireOnChanged();
59 }
60}
61
Sami Kalliomaki16032122016-07-20 16:13:08 +020062void AndroidVideoTrackSource::OnByteBufferFrameCaptured(const void* frame_data,
63 int length,
64 int width,
65 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +020066 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +020067 int64_t timestamp_ns) {
68 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +020069
nisse6f5a6c32016-09-22 01:25:59 -070070 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
71 int64_t translated_camera_time_us =
72 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
73
Sami Kalliomaki16032122016-07-20 16:13:08 +020074 int adapted_width;
75 int adapted_height;
76 int crop_width;
77 int crop_height;
78 int crop_x;
79 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +020080
sakal0ce6aaf2016-11-22 01:26:16 -080081 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
82 &adapted_height, &crop_width, &crop_height, &crop_x,
83 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +020084 return;
85 }
86
Sami Kalliomaki16032122016-07-20 16:13:08 +020087 const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
88 const uint8_t* uv_plane = y_plane + width * height;
magjed1a7ef1f2016-09-17 02:39:03 -070089 const int uv_width = (width + 1) / 2;
Sami Kalliomaki16032122016-07-20 16:13:08 +020090
91 RTC_CHECK_GE(length, width * height + 2 * uv_width * ((height + 1) / 2));
92
93 // Can only crop at even pixels.
94 crop_x &= ~1;
95 crop_y &= ~1;
magjed1a7ef1f2016-09-17 02:39:03 -070096 // Crop just by modifying pointers.
97 y_plane += width * crop_y + crop_x;
98 uv_plane += uv_width * crop_y + crop_x;
Sami Kalliomaki16032122016-07-20 16:13:08 +020099
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200100 rtc::scoped_refptr<I420Buffer> buffer =
magjed1a7ef1f2016-09-17 02:39:03 -0700101 buffer_pool_.CreateBuffer(adapted_width, adapted_height);
102
103 nv12toi420_scaler_.NV12ToI420Scale(
sakal0ce6aaf2016-11-22 01:26:16 -0800104 y_plane, width, uv_plane, uv_width * 2, crop_width, crop_height,
magjed1a7ef1f2016-09-17 02:39:03 -0700105 buffer->MutableDataY(), buffer->StrideY(),
jackychened0b0db2016-09-09 16:15:11 -0700106 // Swap U and V, since we have NV21, not NV12.
sakal0ce6aaf2016-11-22 01:26:16 -0800107 buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
108 buffer->StrideU(), buffer->width(), buffer->height());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200109
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200110 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200111}
112
113void AndroidVideoTrackSource::OnTextureFrameCaptured(
114 int width,
115 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200116 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +0200117 int64_t timestamp_ns,
magjeda3d4f682017-08-28 16:24:06 -0700118 const NativeHandleImpl& handle) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200119 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200120
nisse6f5a6c32016-09-22 01:25:59 -0700121 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
122 int64_t translated_camera_time_us =
123 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
124
Sami Kalliomaki16032122016-07-20 16:13:08 +0200125 int adapted_width;
126 int adapted_height;
127 int crop_width;
128 int crop_height;
129 int crop_x;
130 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200131
sakal0ce6aaf2016-11-22 01:26:16 -0800132 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
133 &adapted_height, &crop_width, &crop_height, &crop_x,
134 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200135 surface_texture_helper_->ReturnTextureFrame();
136 return;
137 }
138
magjeda3d4f682017-08-28 16:24:06 -0700139 Matrix matrix = handle.sampling_matrix;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200140
141 matrix.Crop(crop_width / static_cast<float>(width),
142 crop_height / static_cast<float>(height),
143 crop_x / static_cast<float>(width),
144 crop_y / static_cast<float>(height));
145
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200146 // Note that apply_rotation() may change under our feet, so we should only
147 // check once.
148 if (apply_rotation()) {
149 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200150 std::swap(adapted_width, adapted_height);
151 }
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200152 matrix.Rotate(rotation);
153 rotation = kVideoRotation_0;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200154 }
155
magjeda3d4f682017-08-28 16:24:06 -0700156 OnFrame(VideoFrame(surface_texture_helper_->CreateTextureFrame(
157 adapted_width, adapted_height,
158 NativeHandleImpl(handle.oes_texture_id, matrix)),
159 rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200160}
161
sakalbe910462017-08-11 00:26:05 -0700162void AndroidVideoTrackSource::OnFrameCaptured(JNIEnv* jni,
163 int width,
164 int height,
165 int64_t timestamp_ns,
166 VideoRotation rotation,
167 jobject j_video_frame_buffer) {
168 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
169
170 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
171 int64_t translated_camera_time_us =
172 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
173
174 int adapted_width;
175 int adapted_height;
176 int crop_width;
177 int crop_height;
178 int crop_x;
179 int crop_y;
180
181 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
182 &adapted_height, &crop_width, &crop_height, &crop_x,
183 &crop_y)) {
184 return;
185 }
186
187 jobject j_adapted_video_frame_buffer = jni->CallObjectMethod(
188 j_video_frame_buffer, j_crop_and_scale_id_, crop_x, crop_y, crop_width,
189 crop_height, adapted_width, adapted_height);
190
191 rtc::scoped_refptr<VideoFrameBuffer> buffer =
192 video_buffer_factory_.WrapBuffer(jni, j_adapted_video_frame_buffer);
193
194 // AdaptedVideoTrackSource handles applying rotation for I420 frames.
Sami Kalliomäkicbc4b1d2017-09-29 11:50:25 +0200195 if (apply_rotation() && rotation != kVideoRotation_0) {
sakalbe910462017-08-11 00:26:05 -0700196 buffer = buffer->ToI420();
197 }
198
199 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
200}
201
Sami Kalliomaki16032122016-07-20 16:13:08 +0200202void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
203 int height,
204 int fps) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200205 cricket::VideoFormat format(width, height,
206 cricket::VideoFormat::FpsToInterval(fps), 0);
nisse6f5a6c32016-09-22 01:25:59 -0700207 video_adapter()->OnOutputFormatRequest(format);
Sami Kalliomaki16032122016-07-20 16:13:08 +0200208}
209
210} // namespace webrtc
magjeda3d4f682017-08-28 16:24:06 -0700211} // namespace webrtc