-
Notifications
You must be signed in to change notification settings - Fork 0
/
api_hw_jni.cpp
334 lines (298 loc) · 10.3 KB
/
api_hw_jni.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
//
// Created by HWilliam on 2021/5/16.
//
/**
* jni函数,面向Java
*
* 初始化
* RTMP_init
*
* 连接
* RTMP_JNI_Connect
*
* 发送数据
* RTMP_JNI_SendData
*
* 销毁
* RTMP_destroy
*/
#include <jni.h>
#include "rtmp_wrap.h"
#include "x264_wrap.h"
#include <cassert>
#include "log_abs.h"
#include "x264.h"
#include "VideoEncoder.h"
#include "safe_queue.h"
#include "hwutil.h"
#include "pthread.h"
#include "malloc.h"
// <editor-fold defaultstate="collapsed" desc="static 函数预声明">
static void X264Jni_encodeCallback(char *encodedData, int size);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="全局变量定义">
namespace {
/**** 字符串常量定义 ****/
const char *TAG = "LiveRtmp";
const char *CLASS_RMP_JNI = "com/hwilliamgo/livertmp/jni/RTMPJni";
const char *CLASS_X264_JNI = "com/hwilliamgo/livertmp/jni/X264Jni";
const char *CLASS_RTMP_X264_JNI = "com/hwilliamgo/livertmp/jni/RTMPX264Jni";
/**** java类定义 ****/
jclass jni_class_rtmp = nullptr;
jclass jni_class_x264 = nullptr;
jclass jni_class_rtmp_x264 = nullptr;
/**** 引擎定义 ****/
VideoEncoder *videoEncoder = nullptr;
JNIEnv *jniEnv = nullptr;
/**
* Rtmp状态
*/
enum class RtmpStatus : int {
INIT,
START,
STOP,
DESTROY
};
RtmpStatus rtmpStatus = RtmpStatus::DESTROY;
// 工作线程id
pthread_t pid;
//阻塞式队列
SafeQueue<RTMPPacket *> *globalPackets = nullptr;
}
// </editor-fold>
static void createBlockingQueue(SafeQueue<RTMPPacket *> **packets) {
*packets = new SafeQueue<RTMPPacket *>;
(*packets)->setWork(1);
}
// <editor-fold defaultstate="collapsed" desc="JNI函数定义-RTMP">
static void RTMP_init(JNIEnv *env, jclass clazz) {
MyLog::init_log(LogType::SimpleLog, TAG);
MyLog::v(__func__);
}
static jboolean RTMP_JNI_Connect(JNIEnv *env, jclass clazz, jstring url_) {
const char *url = env->GetStringUTFChars(url_, nullptr);
if (url) {
MyLog::v("Java_com_hwilliamgo_livertmp_ScreenLive_connect, url=%s", url);
}
// 链接 服务器 重试几次
int ret = RtmpWrap::connect(url);
env->ReleaseStringUTFChars(url_, url);
return ret;
}
static jboolean RTMP_JNI_SendData(JNIEnv *env, jclass clazz, jbyteArray data_,
jint len,
jlong tms) {
int ret;
jbyte *data = env->GetByteArrayElements(data_, nullptr);
ret = RtmpWrap::sendVideo(data, len, tms);
env->ReleaseByteArrayElements(data_, data, 0);
return ret;
}
static void RTMP_destroy(JNIEnv *env, jclass clazz) {
MyLog::destroy_log();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="JNI函数定义-RTMP_X264">
static void onVideoEncoderCallback(char *encodedData, int size) {
if (globalPackets) {
RTMPPacket *packet = nullptr;
RtmpWrap::createRtmpPacket(&packet, reinterpret_cast<int8_t *>(encodedData), size);
// fixme 此处不注释的话将会引发崩溃,崩溃原因是多线程访问了JNIEnv变量,这是虚拟机不允许的。需要修复
// X264Jni_encodeCallback(encodedData, size);
if (packet) {
globalPackets->push(packet);
}
}
}
static void onVideoEncoderLog(const char *msg) {
MyLog::dTag("x264log", msg);
}
/**
* RTMPX264的队列线程
* @param args 参数
* @return nullptr
*/
static void *RTMPX264_thread(void *args) {
const char *url = static_cast<const char *>(args);
int ret = RtmpWrap::connect(url);
free((void *) url);
url = nullptr;
if (!ret) {
// 连接失败
MyLog::d("RtmpWrap::connect failed");
return nullptr;
}
// 初始化包队列
createBlockingQueue(&globalPackets);
// 循环取出队列中的包
while (rtmpStatus == RtmpStatus::START) {
RTMPPacket *packet = nullptr;
if (globalPackets) {
globalPackets->pop(packet);
}
if (!packet) {
continue;
}
// 给每个包设置流Id
packet->m_nInfoField2 = RtmpWrap::getRtmpStreamId();
// 加入rtmp_dump自带的发送队列发送
ret = RtmpWrap::sendVideo(*packet);
if (!ret) {
MyLog::e("%s %s", __func__, "sendVideo failed");
break;
}
}
return nullptr;
}
static void RTMPX264Jni_native_init(JNIEnv *env, jclass clazz) {
rtmpStatus = RtmpStatus::INIT;
MyLog::init_log(LogType::SimpleLog, TAG);
MyLog::v(__func__);
videoEncoder = new VideoEncoder();
videoEncoder->setVideoEncodeCallback(&onVideoEncoderCallback);
VideoEncoder::setLogger(onVideoEncoderLog);
}
static void RTMPX264Jni_native_setVideoEncoderInfo
(JNIEnv *env, jclass clazz, jint width, jint heigth, jint fps, jint bitrate) {
if (videoEncoder) {
videoEncoder->setVideoEncInfo(width, heigth, fps, bitrate, 1);
}
}
static void RTMPX264Jni_native_start
(JNIEnv *env, jclass clazz, jstring path) {
// 设置状态
rtmpStatus = RtmpStatus::START;
// 建立连接
jsize length = env->GetStringLength(path);
char *url = static_cast<char *>(calloc(length + 1, sizeof(char)));
env->GetStringUTFRegion(path, 0, length, url);
// 创建工作线程
pthread_create(&pid, nullptr, RTMPX264_thread, (void *) url);
}
static void RTMPX264Jni_native_pushVideo
(JNIEnv *env, jclass clazz, jbyteArray yuvData) {
int8_t *byte = env->GetByteArrayElements(yuvData, nullptr);
if (!byte) {
env->ReleaseByteArrayElements(yuvData, byte, JNI_ABORT);
return;
}
if (videoEncoder) {
videoEncoder->encodeData(byte);
}
env->ReleaseByteArrayElements(yuvData, byte, JNI_ABORT);
}
static void RTMPX264Jni_native_stop(JNIEnv *env, jclass clazz) {
rtmpStatus = RtmpStatus::STOP;
// 销毁工作线程
if (globalPackets) {
// setWork函数可以释放锁,否则线程是阻塞的
globalPackets->setWork(0);
globalPackets->clear();
}
pthread_join(pid, nullptr);
// 销毁队列
if (globalPackets) {
delete globalPackets;
globalPackets = nullptr;
}
RtmpWrap::destroy();
}
static void RTMPX264Jni_native_release
(JNIEnv *env, jclass clazz) {
rtmpStatus = RtmpStatus::DESTROY;
if (videoEncoder) {
delete videoEncoder;
videoEncoder = nullptr;
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="JNI函数定义-X264">
/**
* x264编码回调到java层
* @param encodedData 编码数据
* @param size 长度
*/
static void X264Jni_encodeCallback(char *encodedData, int size) {
jclass x264_jni_class = jniEnv->FindClass(CLASS_X264_JNI);
if (!x264_jni_class) {
return;
}
jmethodID onEncodeMethodId = jniEnv->GetStaticMethodID(x264_jni_class, "onEncode", "([B)V");
if (!onEncodeMethodId) {
return;
}
jbyteArray ret = jniEnv->NewByteArray(size);
if (!ret) {
return;
}
jniEnv->SetByteArrayRegion(ret, 0, size, reinterpret_cast<const signed char *>(encodedData));
jniEnv->CallStaticVoidMethod(x264_jni_class, onEncodeMethodId, ret);
jniEnv->DeleteLocalRef(ret);
}
static void X264Jni_init(JNIEnv *env, jclass clazz) {
MyLog::init_log(LogType::SimpleLog, TAG);
X264Wrap::init();
X264Wrap::setVideoEncodeCallback(X264Jni_encodeCallback);
}
static void
X264Jni_setVideoCodecInfo(JNIEnv *env, jclass clazz, int width, int height, int fps, int bitrate) {
X264Wrap::setVideoCodecInfo(width, height, fps, bitrate);
}
static void X264Jni_encode(JNIEnv *env, jclass clazz, jbyteArray yuvData) {
int8_t *data = env->GetByteArrayElements(yuvData, nullptr);
X264Wrap::encode(data);
env->ReleaseByteArrayElements(yuvData, data, JNI_ABORT);
}
static void X264Jni_destroy(JNIEnv *env, jclass clazz) {
X264Wrap::destroy();
MyLog::destroy_log();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="动态加载jni函数">
static JNINativeMethod g_methods_rtmp[] = {
{"init", "()V", (void *) RTMP_init},
{"sendData", "([BIJ)Z", (void *) RTMP_JNI_SendData},
{"connect", "(Ljava/lang/String;)Z", (void *) RTMP_JNI_Connect},
{"destroy", "()V", (void *) RTMP_destroy}
};
static JNINativeMethod g_methods_x264[] = {
{"native_init", "()V", (void *) X264Jni_init},
{"native_setVideoCodecInfo", "(IIII)V", (void *) X264Jni_setVideoCodecInfo},
{"native_encode", "([B)V", (void *) X264Jni_encode},
{"native_destroy", "()V", (void *) X264Jni_destroy}
};
static JNINativeMethod g_methods_rtmp_x264[] = {
{"native_init", "()V", (void *) RTMPX264Jni_native_init},
{"native_setVideoEncoderInfo", "(IIII)V", (void *) RTMPX264Jni_native_setVideoEncoderInfo},
{"native_start", "(Ljava/lang/String;)V", (void *) RTMPX264Jni_native_start},
{"native_pushVideo", "([B)V", (void *) RTMPX264Jni_native_pushVideo},
{"native_stop", "()V", (void *) RTMPX264Jni_native_stop},
{"native_release", "()V", (void *) RTMPX264Jni_native_release}
};
static void registerJniMethod(JNIEnv *env,
jclass *jniClass,
const char *classFullName,
JNINativeMethod *allMethods,
int methodLength) {
*jniClass = env->FindClass(classFullName);
env->RegisterNatives(*jniClass, allMethods, methodLength);
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != nullptr);
registerJniMethod(env, &jni_class_rtmp, CLASS_RMP_JNI,
g_methods_rtmp, NELEM(g_methods_rtmp));
registerJniMethod(env, &jni_class_x264, CLASS_X264_JNI,
g_methods_x264, NELEM(g_methods_x264));
registerJniMethod(env, &jni_class_rtmp_x264, CLASS_RTMP_X264_JNI,
g_methods_rtmp_x264, NELEM(g_methods_rtmp_x264));
jniEnv = env;
return JNI_VERSION_1_4;
}
JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved) {
jniEnv = nullptr;
}
// </editor-fold>