blob: 36b2414da8fa08f2cd6ce55f4b3250191b072ae2 [file] [log] [blame]
Adrien Béraudfb6341f2016-03-07 16:18:54 -05001/*
2 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 *
4 * Authors: Damien Riegel <damien.riegel@savoirfairelinux.com>
5 * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
6 * Ciro Santilli <ciro.santilli@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22%include <std_shared_ptr.i>
23%header %{
24#include <functional>
25#include <list>
26#include <mutex>
27
28#include "dring/dring.h"
29#include "dring/videomanager_interface.h"
30#include <android/native_window.h>
31#include <android/native_window_jni.h>
32#include <android/log.h>
33
34class VideoCallback {
35public:
36 virtual ~VideoCallback(){}
37 virtual void getCameraInfo(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates) {}
38 virtual void setParameters(const std::string, const int format, const int width, const int height, const int rate) {}
39 virtual void startCapture(const std::string& camid) {}
40 virtual void stopCapture() {}
41 virtual void decodingStarted(const std::string& id, const std::string& shm_path, int w, int h, bool is_mixer) {}
42 virtual void decodingStopped(const std::string& id, const std::string& shm_path, bool is_mixer) {}
43};
44%}
45
46%feature("director") VideoCallback;
47
48%{
49
50std::map<ANativeWindow*, std::unique_ptr<DRing::FrameBuffer>> windows {};
51std::mutex windows_mutex;
52
Adrien Béraud8d6a4832016-04-12 18:06:11 -040053std::vector<uint8_t> workspace;
54
55void rotateNV21(std::vector<uint8_t>& input, unsigned width, unsigned height, int rotation, uint8_t* output)
Adrien Béraudfb6341f2016-03-07 16:18:54 -050056{
Adrien Béraud8d6a4832016-04-12 18:06:11 -040057 if (rotation == 0) {
58 std::copy_n(input.begin(), input.size(), output);
59 return;
60 }
61 if (rotation % 90 != 0 || rotation < 0 || rotation > 270) {
62 __android_log_print(ANDROID_LOG_ERROR, "videomanager.i", "%u %u %d", width, height, rotation);
63 return;
64 }
65 unsigned frameSize = width * height;
66 bool swap = rotation % 180 != 0;
67 bool xflip = rotation % 270 != 0;
68 bool yflip = rotation >= 180;
69 unsigned wOut = swap ? height : width;
70 unsigned hOut = swap ? width : height;
71
72 for (unsigned j = 0; j < height; j++) {
73 for (unsigned i = 0; i < width; i++) {
74 unsigned yIn = j * width + i;
75 unsigned uIn = frameSize + (j >> 1) * width + (i & ~1);
76 unsigned vIn = uIn + 1;
77 unsigned iSwapped = swap ? j : i;
78 unsigned jSwapped = swap ? i : j;
79 unsigned iOut = xflip ? wOut - iSwapped - 1 : iSwapped;
80 unsigned jOut = yflip ? hOut - jSwapped - 1 : jSwapped;
81 unsigned yOut = jOut * wOut + iOut;
82 unsigned uOut = frameSize + (jOut >> 1) * wOut + (iOut & ~1);
83 unsigned vOut = uOut + 1;
84 output[yOut] = input[yIn];
85 output[uOut] = input[uIn];
86 output[vOut] = input[vIn];
87 }
88 }
Adrien Béraud86759bb2016-09-28 13:54:54 -040089 return;
Adrien Béraud8d6a4832016-04-12 18:06:11 -040090}
91
Adrien Béraud86759bb2016-09-28 13:54:54 -040092JNIEXPORT void JNICALL Java_cx_ring_service_RingserviceJNI_setVideoFrame(JNIEnv *jenv, jclass jcls, jbyteArray frame, int frame_size, jlong target, int w, int h, int rotation)
Adrien Béraud8d6a4832016-04-12 18:06:11 -040093{
94 uint8_t* f_target = (uint8_t*) ((intptr_t) target);
95 if (rotation == 0)
Adrien Béraud86759bb2016-09-28 13:54:54 -040096 jenv->GetByteArrayRegion(frame, 0, frame_size, (jbyte*)f_target);
Adrien Béraud8d6a4832016-04-12 18:06:11 -040097 else {
98 workspace.resize(frame_size);
Adrien Béraud86759bb2016-09-28 13:54:54 -040099 jenv->GetByteArrayRegion(frame, 0, frame_size, (jbyte*)workspace.data());
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400100 rotateNV21(workspace, w, h, rotation, f_target);
101 }
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500102}
103
104JNIEXPORT jlong JNICALL Java_cx_ring_service_RingserviceJNI_acquireNativeWindow(JNIEnv *jenv, jclass jcls, jobject javaSurface)
105{
106 return (jlong)(intptr_t)ANativeWindow_fromSurface(jenv, javaSurface);
107}
108
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400109JNIEXPORT void JNICALL Java_cx_ring_service_RingserviceJNI_releaseNativeWindow(JNIEnv *jenv, jclass jcls, jlong window_)
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500110{
111 std::lock_guard<std::mutex> guard(windows_mutex);
112 ANativeWindow *window = (ANativeWindow*)((intptr_t) window_);
113 ANativeWindow_release(window);
114}
115
116JNIEXPORT void JNICALL Java_cx_ring_service_RingserviceJNI_setNativeWindowGeometry(JNIEnv *jenv, jclass jcls, jlong window_, int width, int height)
117{
118 ANativeWindow *window = (ANativeWindow*)((intptr_t) window_);
119 ANativeWindow_setBuffersGeometry(window, width, height, WINDOW_FORMAT_RGBA_8888);
120}
121
122void AndroidDisplayCb(ANativeWindow *window, std::unique_ptr<DRing::FrameBuffer> frame)
123{
124 std::lock_guard<std::mutex> guard(windows_mutex);
125 try {
126 auto& i = windows.at(window);
127 ANativeWindow_Buffer buffer;
128 if (ANativeWindow_lock(window, &buffer, NULL) == 0) {
129 if (buffer.bits && frame && frame->ptr) {
130 if (buffer.stride == frame->width)
131 memcpy(buffer.bits, frame->ptr, frame->width * frame->height * 4);
132 else {
133 size_t line_size_in = frame->width * 4;
134 size_t line_size_out = buffer.stride * 4;
135 for (size_t i=0, n=frame->height; i<n; i++)
Adrien Béraud86759bb2016-09-28 13:54:54 -0400136 memcpy((uint8_t*)buffer.bits + line_size_out * i, frame->ptr + line_size_in * i, line_size_in);
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500137 }
138 }
139 else
140 __android_log_print(ANDROID_LOG_WARN, "videomanager.i", "Can't copy surface");
141 ANativeWindow_unlockAndPost(window);
142 } else {
143 __android_log_print(ANDROID_LOG_WARN, "videomanager.i", "Can't lock surface");
144 }
145 i = std::move(frame);
146 } catch (...) {
147 __android_log_print(ANDROID_LOG_WARN, "videomanager.i", "Can't copy frame: no window");
148 }
149}
150
151std::unique_ptr<DRing::FrameBuffer> sinkTargetPullCallback(ANativeWindow *window, std::size_t bytes)
152{
153 try {
154 std::unique_ptr<DRing::FrameBuffer> ret;
155 {
156 std::lock_guard<std::mutex> guard(windows_mutex);
157 ret = std::move(windows.at(window));
158 }
159 if (not ret) {
160 __android_log_print(ANDROID_LOG_WARN, "videomanager.i", "Creating new video buffer of %zu kib", bytes/1024);
161 ret.reset(new DRing::FrameBuffer());
162 }
163 ret->storage.resize(bytes);
164 ret->ptr = ret->storage.data();
165 ret->ptrSize = bytes;
166 return ret;
167 } catch (...) {
168 return {};
169 }
170}
171
172JNIEXPORT void JNICALL Java_cx_ring_service_RingserviceJNI_registerVideoCallback(JNIEnv *jenv, jclass jcls, jstring sinkId, jlong window)
173{
174 if(!sinkId) {
175 SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
Adrien Béraud86759bb2016-09-28 13:54:54 -0400176 return;
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500177 }
178 const char *arg1_pstr = (const char *)jenv->GetStringUTFChars(sinkId, 0);
179 if (!arg1_pstr)
Adrien Béraud86759bb2016-09-28 13:54:54 -0400180 return;
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500181 const std::string sink(arg1_pstr);
182 jenv->ReleaseStringUTFChars(sinkId, arg1_pstr);
183
184 ANativeWindow* nativeWindow = (ANativeWindow*)((intptr_t) window);
185 auto f_display_cb = std::bind(&AndroidDisplayCb, nativeWindow, std::placeholders::_1);
186 auto p_display_cb = std::bind(&sinkTargetPullCallback, nativeWindow, std::placeholders::_1);
187
188 std::lock_guard<std::mutex> guard(windows_mutex);
189 windows.emplace(nativeWindow, nullptr);
190 DRing::registerSinkTarget(sink, DRing::SinkTarget {.pull=p_display_cb, .push=f_display_cb});
191}
192
193JNIEXPORT void JNICALL Java_cx_ring_service_RingserviceJNI_unregisterVideoCallback(JNIEnv *jenv, jclass jcls, jstring sinkId, jlong window)
194{
195 if(!sinkId) {
196 SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
Adrien Béraud86759bb2016-09-28 13:54:54 -0400197 return;
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500198 }
199 const char *arg1_pstr = (const char *)jenv->GetStringUTFChars(sinkId, 0);
200 if (!arg1_pstr)
Adrien Béraud86759bb2016-09-28 13:54:54 -0400201 return;
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500202 const std::string sink(arg1_pstr);
203 jenv->ReleaseStringUTFChars(sinkId, arg1_pstr);
204
205 std::lock_guard<std::mutex> guard(windows_mutex);
206 DRing::registerSinkTarget(sink, DRing::SinkTarget {});
Adrien Béraud86759bb2016-09-28 13:54:54 -0400207 ANativeWindow* nativeWindow = (ANativeWindow*)((intptr_t) window);
208 windows.erase(nativeWindow);
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500209}
210
211%}
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400212%native(setVideoFrame) void setVideoFrame(void*, int, jlong, int, int, int);
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500213%native(acquireNativeWindow) jlong acquireNativeWindow(jobject);
214%native(releaseNativeWindow) void releaseNativeWindow(jlong);
215%native(setNativeWindowGeometry) void setNativeWindowGeometry(jlong, int, int);
216%native(registerVideoCallback) void registerVideoCallback(jstring, jlong);
217%native(unregisterVideoCallback) void unregisterVideoCallback(jstring, jlong);
218
219
220namespace DRing {
221
222void setDefaultDevice(const std::string& name);
223std::string getDefaultDevice();
224
225void startCamera();
226void stopCamera();
227bool hasCameraStarted();
228bool switchInput(const std::string& resource);
229bool switchToCamera();
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400230std::map<std::string, std::string> getSettings(const std::string& name);
231void applySettings(const std::string& name, const std::map<std::string, std::string>& settings);
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500232
233void addVideoDevice(const std::string &node);
234void removeVideoDevice(const std::string &node);
Adrien Béraud86759bb2016-09-28 13:54:54 -0400235uint8_t* obtainFrame(int length);
236void releaseFrame(uint8_t* frame);
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500237void registerSinkTarget(const std::string& sinkId, const DRing::SinkTarget& target);
238}
239
240class VideoCallback {
241public:
242 virtual ~VideoCallback(){}
243 virtual void getCameraInfo(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates){}
244 virtual void setParameters(const std::string, const int format, const int width, const int height, const int rate) {}
245 virtual void startCapture(const std::string& camid) {}
246 virtual void stopCapture() {}
247 virtual void decodingStarted(const std::string& id, const std::string& shm_path, int w, int h, bool is_mixer) {}
248 virtual void decodingStopped(const std::string& id, const std::string& shm_path, bool is_mixer) {}
249};