blob: 42430c2804a56acf2455cfff66e19d6291d32f49 [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();
39}
40
Sami Kalliomaki16032122016-07-20 16:13:08 +020041void AndroidVideoTrackSource::SetState(SourceState state) {
42 if (rtc::Thread::Current() != signaling_thread_) {
43 invoker_.AsyncInvoke<void>(
44 RTC_FROM_HERE, signaling_thread_,
45 rtc::Bind(&AndroidVideoTrackSource::SetState, this, state));
46 return;
47 }
48
49 if (state_ != state) {
50 state_ = state;
51 FireOnChanged();
52 }
53}
54
Sami Kalliomaki16032122016-07-20 16:13:08 +020055void AndroidVideoTrackSource::OnByteBufferFrameCaptured(const void* frame_data,
56 int length,
57 int width,
58 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +020059 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +020060 int64_t timestamp_ns) {
61 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +020062
nisse6f5a6c32016-09-22 01:25:59 -070063 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
64 int64_t translated_camera_time_us =
65 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
66
Sami Kalliomaki16032122016-07-20 16:13:08 +020067 int adapted_width;
68 int adapted_height;
69 int crop_width;
70 int crop_height;
71 int crop_x;
72 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +020073
sakal0ce6aaf2016-11-22 01:26:16 -080074 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
75 &adapted_height, &crop_width, &crop_height, &crop_x,
76 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +020077 return;
78 }
79
Sami Kalliomaki16032122016-07-20 16:13:08 +020080 const uint8_t* y_plane = static_cast<const uint8_t*>(frame_data);
81 const uint8_t* uv_plane = y_plane + width * height;
magjed1a7ef1f2016-09-17 02:39:03 -070082 const int uv_width = (width + 1) / 2;
Sami Kalliomaki16032122016-07-20 16:13:08 +020083
84 RTC_CHECK_GE(length, width * height + 2 * uv_width * ((height + 1) / 2));
85
86 // Can only crop at even pixels.
87 crop_x &= ~1;
88 crop_y &= ~1;
magjed1a7ef1f2016-09-17 02:39:03 -070089 // Crop just by modifying pointers.
90 y_plane += width * crop_y + crop_x;
91 uv_plane += uv_width * crop_y + crop_x;
Sami Kalliomaki16032122016-07-20 16:13:08 +020092
Magnus Jedvert3093ef12017-06-19 15:04:19 +020093 rtc::scoped_refptr<I420Buffer> buffer =
magjed1a7ef1f2016-09-17 02:39:03 -070094 buffer_pool_.CreateBuffer(adapted_width, adapted_height);
95
96 nv12toi420_scaler_.NV12ToI420Scale(
sakal0ce6aaf2016-11-22 01:26:16 -080097 y_plane, width, uv_plane, uv_width * 2, crop_width, crop_height,
magjed1a7ef1f2016-09-17 02:39:03 -070098 buffer->MutableDataY(), buffer->StrideY(),
jackychened0b0db2016-09-09 16:15:11 -070099 // Swap U and V, since we have NV21, not NV12.
sakal0ce6aaf2016-11-22 01:26:16 -0800100 buffer->MutableDataV(), buffer->StrideV(), buffer->MutableDataU(),
101 buffer->StrideU(), buffer->width(), buffer->height());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200102
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200103 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200104}
105
106void AndroidVideoTrackSource::OnTextureFrameCaptured(
107 int width,
108 int height,
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200109 VideoRotation rotation,
Sami Kalliomaki16032122016-07-20 16:13:08 +0200110 int64_t timestamp_ns,
magjeda3d4f682017-08-28 16:24:06 -0700111 const NativeHandleImpl& handle) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200112 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
Sami Kalliomaki16032122016-07-20 16:13:08 +0200113
nisse6f5a6c32016-09-22 01:25:59 -0700114 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
115 int64_t translated_camera_time_us =
116 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
117
Sami Kalliomaki16032122016-07-20 16:13:08 +0200118 int adapted_width;
119 int adapted_height;
120 int crop_width;
121 int crop_height;
122 int crop_x;
123 int crop_y;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200124
sakal0ce6aaf2016-11-22 01:26:16 -0800125 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
126 &adapted_height, &crop_width, &crop_height, &crop_x,
127 &crop_y)) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200128 surface_texture_helper_->ReturnTextureFrame();
129 return;
130 }
131
magjeda3d4f682017-08-28 16:24:06 -0700132 Matrix matrix = handle.sampling_matrix;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200133
134 matrix.Crop(crop_width / static_cast<float>(width),
135 crop_height / static_cast<float>(height),
136 crop_x / static_cast<float>(width),
137 crop_y / static_cast<float>(height));
138
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200139 // Note that apply_rotation() may change under our feet, so we should only
140 // check once.
141 if (apply_rotation()) {
142 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200143 std::swap(adapted_width, adapted_height);
144 }
Magnus Jedvert3093ef12017-06-19 15:04:19 +0200145 matrix.Rotate(rotation);
146 rotation = kVideoRotation_0;
Sami Kalliomaki16032122016-07-20 16:13:08 +0200147 }
148
magjeda3d4f682017-08-28 16:24:06 -0700149 OnFrame(VideoFrame(surface_texture_helper_->CreateTextureFrame(
150 adapted_width, adapted_height,
151 NativeHandleImpl(handle.oes_texture_id, matrix)),
152 rotation, translated_camera_time_us));
Sami Kalliomaki16032122016-07-20 16:13:08 +0200153}
154
sakalbe910462017-08-11 00:26:05 -0700155void AndroidVideoTrackSource::OnFrameCaptured(JNIEnv* jni,
156 int width,
157 int height,
158 int64_t timestamp_ns,
159 VideoRotation rotation,
160 jobject j_video_frame_buffer) {
161 RTC_DCHECK(camera_thread_checker_.CalledOnValidThread());
162
163 int64_t camera_time_us = timestamp_ns / rtc::kNumNanosecsPerMicrosec;
164 int64_t translated_camera_time_us =
165 timestamp_aligner_.TranslateTimestamp(camera_time_us, rtc::TimeMicros());
166
167 int adapted_width;
168 int adapted_height;
169 int crop_width;
170 int crop_height;
171 int crop_x;
172 int crop_y;
173
174 if (!AdaptFrame(width, height, camera_time_us, &adapted_width,
175 &adapted_height, &crop_width, &crop_height, &crop_x,
176 &crop_y)) {
177 return;
178 }
179
sakalbe910462017-08-11 00:26:05 -0700180 rtc::scoped_refptr<VideoFrameBuffer> buffer =
Magnus Jedvert202be392017-11-18 16:09:17 +0100181 AndroidVideoBuffer::Create(jni, j_video_frame_buffer)
182 ->CropAndScale(jni, crop_x, crop_y, crop_width, crop_height,
183 adapted_width, adapted_height);
sakalbe910462017-08-11 00:26:05 -0700184
185 // AdaptedVideoTrackSource handles applying rotation for I420 frames.
Sami Kalliomäkicbc4b1d2017-09-29 11:50:25 +0200186 if (apply_rotation() && rotation != kVideoRotation_0) {
sakalbe910462017-08-11 00:26:05 -0700187 buffer = buffer->ToI420();
188 }
189
190 OnFrame(VideoFrame(buffer, rotation, translated_camera_time_us));
191}
192
Sami Kalliomaki16032122016-07-20 16:13:08 +0200193void AndroidVideoTrackSource::OnOutputFormatRequest(int width,
194 int height,
195 int fps) {
Sami Kalliomaki16032122016-07-20 16:13:08 +0200196 cricket::VideoFormat format(width, height,
197 cricket::VideoFormat::FpsToInterval(fps), 0);
nisse6f5a6c32016-09-22 01:25:59 -0700198 video_adapter()->OnOutputFormatRequest(format);
Sami Kalliomaki16032122016-07-20 16:13:08 +0200199}
200
201} // namespace webrtc
magjeda3d4f682017-08-28 16:24:06 -0700202} // namespace webrtc