├── .gitignore
├── app
├── .gitignore
├── CMakeLists.txt
├── CMakeLists1.txt
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── dzm
│ │ └── rtmppush
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── RtmpPush.cpp
│ │ ├── RtmpPush.h
│ │ ├── include
│ │ │ ├── faac
│ │ │ │ ├── faac.h
│ │ │ │ └── faaccfg.h
│ │ │ └── x264
│ │ │ │ ├── x264.h
│ │ │ │ └── x264_config.h
│ │ ├── lib
│ │ │ ├── libfaac.a
│ │ │ └── libx264.a
│ │ ├── librtmp
│ │ │ ├── Android.mk
│ │ │ ├── CMakeLists.txt
│ │ │ ├── amf.c
│ │ │ ├── amf.h
│ │ │ ├── bytes.h
│ │ │ ├── dh.h
│ │ │ ├── dhgroups.h
│ │ │ ├── handshake.h
│ │ │ ├── hashswf.c
│ │ │ ├── http.h
│ │ │ ├── log.c
│ │ │ ├── log.h
│ │ │ ├── parseurl.c
│ │ │ ├── rtmp.c
│ │ │ ├── rtmp.h
│ │ │ └── rtmp_sys.h
│ │ ├── native-lib.cpp
│ │ ├── queue.c
│ │ └── queue.h
│ ├── java
│ │ └── com
│ │ │ └── dzm
│ │ │ └── rtmppush
│ │ │ ├── MainActivity.java
│ │ │ ├── RtmpPush.java
│ │ │ ├── audio
│ │ │ ├── AudioController.java
│ │ │ └── AudioRunnable.java
│ │ │ └── view
│ │ │ ├── CameraSurfacevView.java
│ │ │ └── MediaSerfaceView.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── dzm
│ └── rtmppush
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | cmake_minimum_required(VERSION 3.4.1)
3 |
4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fpermissive -std=c++11 -D__STDC_CONSTANT_MACROS -DFIXED_POINT -DUSE_KISS_FFT -DEXPORT="" -UHAVE_CONFIG_H ")
5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2 -DSYS=posix -DNO_CRYPTO")
6 |
7 | set(main_path ${CMAKE_SOURCE_DIR}/src/main/cpp)
8 |
9 | file(GLOB cpp_path ${main_path}/*.cpp)
10 | file(GLOB rtmp_path ${main_path}/librtmp/*.c)
11 |
12 | find_library(log-lib log )
13 |
14 | #
15 | add_library(x264 STATIC IMPORTED)
16 | set_target_properties(x264 PROPERTIES IMPORTED_LOCATION ${main_path}/lib/libx264.a)
17 |
18 | #
19 | add_library(faac STATIC IMPORTED)
20 | set_target_properties(faac PROPERTIES IMPORTED_LOCATION ${main_path}/lib/libfaac.a)
21 |
22 | #
23 | add_library(rtmp-lib STATIC ${rtmp_path})
24 |
25 | #
26 | add_library(native-lib SHARED ${cpp_path})
27 | add_library(que-lib STATIC src/main/cpp/queue.c src/main/cpp/RtmpPush.cpp)
28 | target_link_libraries(que-lib)
29 |
30 | target_link_libraries(native-lib que-lib rtmp-lib x264 faac ${log-lib} )
31 |
32 | include_directories(${main_path})
--------------------------------------------------------------------------------
/app/CMakeLists1.txt:
--------------------------------------------------------------------------------
1 | # Sets the minimum version of CMake required to build the native
2 | # library. You should either keep the default value or only pass a
3 | # value of 3.4.0 or lower.
4 |
5 | cmake_minimum_required(VERSION 3.4.1)
6 |
7 | # Creates and names a library, sets it as either STATIC
8 | # or SHARED, and provides the relative paths to its source code.
9 | # You can define multiple libraries, and CMake builds it for you.
10 | # Gradle automatically packages shared libraries with your APK.
11 |
12 | add_library( # Sets the name of the library.
13 | native-lib
14 |
15 | # Sets the library as a shared library.
16 | SHARED
17 |
18 | # Provides a relative path to your source file(s).
19 | # Associated headers in the same location as their source
20 | # file are automatically included.
21 | src/main/cpp/native-lib.cpp )
22 |
23 | # Searches for a specified prebuilt library and stores the path as a
24 | # variable. Because system libraries are included in the search path by
25 | # default, you only need to specify the name of the public NDK library
26 | # you want to add. CMake verifies that the library exists before
27 | # completing its build.
28 |
29 | find_library( # Sets the name of the path variable.
30 | log-lib
31 |
32 | # Specifies the name of the NDK library that
33 | # you want CMake to locate.
34 | log )
35 |
36 | # Specifies libraries CMake should link to your target library. You
37 | # can link multiple libraries, such as libraries you define in the
38 | # build script, prebuilt third-party libraries, or system libraries.
39 |
40 | target_link_libraries( # Specifies the target library.
41 | native-lib
42 |
43 | # Links the target library to the log library
44 | # included in the NDK.
45 | ${log-lib} )
46 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.3"
6 | defaultConfig {
7 | applicationId "com.dzm.rtmppush"
8 | minSdkVersion 15
9 | targetSdkVersion 24
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | externalNativeBuild {
14 | cmake {
15 | cppFlags ""
16 | abiFilters "armeabi-v7a"
17 | }
18 | }
19 | }
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | externalNativeBuild {
27 | cmake {
28 | path "CMakeLists.txt"
29 | }
30 | }
31 | }
32 |
33 | dependencies {
34 | compile fileTree(dir: 'libs', include: ['*.jar'])
35 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
36 | exclude group: 'com.android.support', module: 'support-annotations'
37 | })
38 | compile 'com.android.support:appcompat-v7:24.2.1'
39 | testCompile 'junit:junit:4.12'
40 | compile 'com.mcxiaoke.ijk.media:ijkplayer-java:0.6.2'
41 | compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.6.2'
42 | compile 'com.google.android.exoplayer:exoplayer:r1.5.11'
43 | }
44 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\androidstudio\assdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/dzm/rtmppush/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | * @see Testing documentation
15 | */
16 | @RunWith(AndroidJUnit4.class)
17 | public class ExampleInstrumentedTest {
18 | @Test
19 | public void useAppContext() throws Exception {
20 | // Context of the app under test.
21 | Context appContext = InstrumentationRegistry.getTargetContext();
22 |
23 | assertEquals("com.dzm.rtmppush", appContext.getPackageName());
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/cpp/RtmpPush.cpp:
--------------------------------------------------------------------------------
1 | #include "RtmpPush.h"
2 |
3 | extern "C" {
4 |
5 | int RtmpPush::init_vedio( const char *out_url, int width, int height, int bitrate) {
6 |
7 | media_size = width * height;
8 | m_width = width;
9 | m_height = height;
10 | m_bitrate = bitrate;
11 |
12 | //x264
13 | x264_param_t param;
14 | x264_param_default_preset(¶m, "ultrafast", "zerolatency");
15 | //
16 | param.i_csp = X264_CSP_I420;
17 | param.i_width = width;
18 | param.i_height = height;
19 | param.rc.i_bitrate = bitrate / 1000;
20 | param.rc.i_rc_method = X264_RC_ABR; //参数i_rc_method表示码率控制,CQP(恒定质量),CRF(恒定码率),ABR(平均码率)
21 | param.rc.i_vbv_buffer_size = bitrate / 1000; //设置了i_vbv_max_bitrate必须设置此参数,码率控制区大小,单位kbps
22 | param.rc.i_vbv_max_bitrate = bitrate / 1000 * 1.2; //瞬时最大码率
23 | param.i_keyint_max = 10 * 2;
24 | param.i_fps_num = 10; //* 帧率分子
25 | param.i_fps_den = 1; //* 帧率分母
26 | param.i_threads = 1;
27 | param.i_timebase_den = param.i_fps_num;
28 | param.i_timebase_num = param.i_fps_den;
29 | param.b_repeat_headers = 1; // 是否复制sps和pps放在每个关键帧的前面 该参数设置是让每个关键帧(I帧)都附带sps/pps。
30 |
31 | //
32 | x264_param_apply_profile(¶m, "baseline");
33 |
34 | videoEncHandle = x264_encoder_open(¶m);
35 | pic_in = (x264_picture_t *) malloc(sizeof(x264_picture_t));
36 | pic_out = (x264_picture_t *) malloc(sizeof(x264_picture_t));
37 | x264_picture_alloc(pic_in, X264_CSP_I420, width, height);
38 | x264_picture_init(pic_out);
39 |
40 | return init_rtmp((char *) out_url);
41 | }
42 |
43 | int RtmpPush::init_rtmp(char *out_url) {
44 | //初始化rtm
45 | //创建一个RTMP会话的句柄
46 | rtmpClient = RTMP_Alloc();
47 | //初始化rtmp 句柄
48 | RTMP_Init(rtmpClient);
49 | //超时时间
50 | rtmpClient->Link.timeout = 5;
51 | rtmpClient->Link.flashVer = RTMP_DefaultFlashVer;
52 |
53 | //设置会话的参数 输出url地址
54 | if (!RTMP_SetupURL(rtmpClient, (char *) out_url)) {
55 | RTMP_Free(rtmpClient);
56 | LOGD("RTMP_SetupURL fail");
57 | return 1;
58 | }
59 |
60 | RTMP_EnableWrite(rtmpClient);
61 | //建立RTMP链接中的网络连接
62 | if (!RTMP_Connect(rtmpClient, NULL)) {
63 | RTMP_Free(rtmpClient);
64 | LOGD("RTMP_Connect fail");
65 | return 1;
66 | }
67 | //建立RTMP链接中的网络流
68 | if (!RTMP_ConnectStream(rtmpClient, 0)) {
69 | RTMP_Free(rtmpClient);
70 | LOGD("RTMP_ConnectStream fail");
71 | return 1;
72 | }
73 |
74 | //创建队列
75 | create_queue();
76 | start_time = (long) RTMP_GetTime();
77 |
78 | //开启
79 | publishing = 1;
80 | mutex = PTHREAD_MUTEX_INITIALIZER;
81 | cond = PTHREAD_COND_INITIALIZER;
82 | pthread_create(&publisher_tid, NULL, RtmpPush::push_thread, this);
83 | return 2;
84 | }
85 |
86 | int RtmpPush::init_audio(int sampleRate, int channel) {
87 | if(audioEncHandle){
88 | LOGD("音频已打开");
89 | return 2;
90 | }
91 | /**
92 | * 初始化aac句柄,同时获取最大输入样本,及编码所需最小字节
93 | * sampleRate 采样率 channel声道数 nInputSamples输入样本数 nMaxOutputBytes输出所需最大空间
94 | */
95 | audioEncHandle = faacEncOpen((unsigned long) sampleRate, (unsigned int) channel, &nInputSamples, &nMaxOutputBytes);
96 |
97 | if(!audioEncHandle){
98 | LOGD("音频打开失败");
99 | return 1;
100 | }
101 |
102 | faacEncConfigurationPtr faac_enc_config = faacEncGetCurrentConfiguration(audioEncHandle);
103 | faac_enc_config->mpegVersion = MPEG4;
104 | faac_enc_config->allowMidside = 1;
105 | //LC编码
106 | faac_enc_config->aacObjectType = LOW;
107 | //输出是否包含ADTS头
108 | faac_enc_config->outputFormat = 0;
109 | //时域噪音控制,大概就是消爆音
110 | faac_enc_config->useTns = 1;
111 | faac_enc_config->useLfe = 0;
112 | faac_enc_config->inputFormat = FAAC_INPUT_16BIT;
113 | faac_enc_config->quantqual = 100;
114 | //频宽
115 | faac_enc_config->bandWidth = 0;
116 | faac_enc_config->shortctl = SHORTCTL_NORMAL;
117 |
118 | if(!faacEncSetConfiguration(audioEncHandle,faac_enc_config)){
119 | LOGD("参数配置失败");
120 | return 1;
121 | }
122 | return init_audio_header();
123 | }
124 |
125 | int RtmpPush::init_audio_header() {
126 |
127 | unsigned char* data;
128 | //长度
129 | unsigned long len;
130 | faacEncGetDecoderSpecificInfo(audioEncHandle,&data,&len);
131 | RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
132 | if(!packet){
133 | return 1;
134 | }
135 | RTMPPacket_Reset(packet);
136 | if(!RTMPPacket_Alloc(packet,len + 2)){
137 | RTMPPacket_Free(packet);
138 | return 1;
139 | }
140 |
141 | char *body = packet->m_body;
142 | body[0] = (char) 0xaf;
143 | //faac 头为0x00
144 | body[1] = 0x00;
145 | memcpy(&body[2],data,len);
146 |
147 | packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
148 | packet->m_nBodySize = len + 2;
149 | packet->m_nChannel = 0x04;
150 | packet->m_hasAbsTimestamp = 0;
151 | packet->m_nTimeStamp = 0;
152 | packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
153 |
154 | add_rtmp_packet(packet);
155 | return 2;
156 | }
157 |
158 | void RtmpPush::audio_cllect(char *data) {
159 |
160 | if(!RTMP_IsConnected(rtmpClient)){
161 | LOGD("rtmp未开启");
162 | return;
163 | }
164 | unsigned char* bitbuf = (unsigned char *) malloc(nMaxOutputBytes * sizeof(unsigned char*));
165 | int data_len = faacEncEncode(audioEncHandle, (int32_t *) data, nInputSamples, bitbuf, nMaxOutputBytes);
166 |
167 | if(data_len<=0){
168 | if(bitbuf){
169 | free(bitbuf);
170 | }
171 | return;
172 | }
173 |
174 | int body_size = data_len + 2;
175 | RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
176 | if(!packet){
177 | return;
178 | }
179 | RTMPPacket_Reset(packet);
180 | if(!RTMPPacket_Alloc(packet, (uint32_t) body_size)){
181 | RTMPPacket_Free(packet);
182 | return;
183 | }
184 | char *body = packet->m_body;
185 | body[0] = (char) 0xaf;
186 | //faac 体为0x01
187 | body[1] = 0x01;
188 | memcpy(&body[2], bitbuf, (size_t) data_len);
189 |
190 | packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
191 | packet->m_nBodySize = (uint32_t) body_size;
192 | packet->m_nChannel = 0x04;
193 | packet->m_hasAbsTimestamp = 0;
194 | packet->m_nTimeStamp = RTMP_GetTime() - start_time;
195 | packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
196 | add_rtmp_packet(packet);
197 | }
198 |
199 | void RtmpPush::video_collect_back(char *data) {
200 | int width = m_height;
201 | int height = m_width;
202 | int uv_height = height >> 1;
203 | char *yuv = new char[media_size*3/2];
204 | int k = 0;
205 | //y
206 | /**
207 | * y4 y8 y12 y16
208 | * y3 y7 y11 y15
209 | * y2 y6 y10 y14
210 | * y1 y5 y9 y13
211 | */
212 | for (int i = 0; i < width; i++) {
213 | for (int j = height - 1; j >= 0; j--) {
214 | yuv[k++] = data[width * j + i];
215 | }
216 | }
217 | //u v
218 | for (int j = 0; j < width; j += 2) {
219 | for (int i = uv_height - 1; i >= 0; i--) {
220 | yuv[k++] = data[media_size + width * i + j];
221 | yuv[k++] = data[media_size + width * i + j + 1];
222 | }
223 | }
224 | push_video(yuv,1);
225 | }
226 |
227 | void RtmpPush::video_collect_above(char *data) {
228 | // LOGD("数据开始编码1");
229 | int width = m_height;
230 | int height = m_width;
231 | int uv_height = height >> 1;
232 | char *yuv = new char[media_size*3/2];
233 | int k = 0;
234 | //y
235 | /**
236 | * y13 y9 y5 y1
237 | * y14 y10 y6 y2
238 | * y15 y11 y7 y3
239 | * y16 y12 y8 y4
240 | */
241 | // LOGD("数据开始编码2");
242 | for (int i = 0; i < width; i++) {
243 | int n_pos = width - 1 - i;
244 | for (int j = 0; j < height; j++) {
245 | yuv[k++] = data[n_pos];
246 | n_pos+=width;
247 | }
248 | }
249 | // LOGD("数据开始编码3");
250 | //u v
251 | for (int i = 0; i < width; i += 2) {
252 | int nPos = media_size + width - 1;
253 | for (int j = 0; j < uv_height; j++) {
254 | yuv[k] = data[nPos - i - 1];
255 | yuv[k + 1] = data[nPos - i];
256 | k += 2;
257 | nPos += width;
258 | }
259 | }
260 | // LOGD("数据开始编码4");
261 | push_video(yuv,1);
262 | }
263 |
264 | void RtmpPush::video_collect_left(char *data) {
265 | push_video(data,0);
266 | }
267 |
268 |
269 | void RtmpPush::push_video(char *data,int index) {
270 | // LOGD("数据开始编码");
271 | //android 视频采集yuv420sp格式 转h264
272 | /**
273 | * android 采集的是nv21格式数据
274 | * 在nv21下是
275 | * y1 y2 y3 y4
276 | * y5 y6 y7 y8
277 | * y9 y10 y11 y12
278 | * y13 y14 y15 y16
279 | * v1 u1 v2 u2
280 | * v3 u3 v4 u4
281 | * 先全是y数据
282 | * 然后v u 交替
283 | */
284 | memcpy(pic_in->img.plane[0], data, (size_t) media_size);
285 | char* u = (char *) pic_in->img.plane[1];
286 | char* v = (char *) pic_in->img.plane[2];
287 | for (int j = 0; j < media_size / 4; j++) {
288 | //获取u数据
289 | *(u + j) = *(data + media_size + j * 2 + 1);
290 | //获取y数据
291 | *(v + j) = *(data + media_size + j * 2);
292 | }
293 | int nNal = -1;
294 | x264_nal_t *nal = NULL;
295 |
296 | if (x264_encoder_encode(videoEncHandle, &nal, &nNal, pic_in, pic_out) < 0) {
297 | LOGD("编码失败");
298 | return;
299 | }
300 |
301 | pic_in->i_pts++;
302 | int sps_len = 0, pps_len = 0;
303 | char *sps = NULL;
304 | char *pps = NULL;
305 | for (int i = 0; i < nNal; i++) {
306 | if (nal[i].i_type == NAL_SPS) {
307 | sps_len = nal[i].i_payload - 4;
308 | sps = (char *) malloc((size_t) (sps_len + 1));
309 | memcpy(sps, nal[i].p_payload + 4, (size_t) sps_len);
310 | } else if (nal[i].i_type == NAL_PPS) {
311 | pps_len = nal[i].i_payload - 4;
312 | pps = (char *) malloc((size_t) (pps_len + 1));
313 | memcpy(pps, nal[i].p_payload + 4, (size_t) pps_len);
314 | //添加头编码
315 | add_264_header(pps, sps, pps_len, sps_len);
316 | free(sps);
317 | free(pps);
318 | } else {
319 | add_264_body((char *) nal[i].p_payload, nal[i].i_payload);
320 | }
321 | }
322 | if(index){
323 | free(data);
324 | }
325 |
326 | }
327 |
328 | void RtmpPush::add_264_header(char *pps, char *sps, int pps_len, int sps_len) {
329 | int body_size = 13 + sps_len + 3 + pps_len;
330 | RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
331 | if (!packet) {
332 | return;
333 | }
334 | RTMPPacket_Reset(packet);
335 | if (!RTMPPacket_Alloc(packet, (uint32_t) body_size)) {
336 | RTMPPacket_Free(packet);
337 | return;
338 | }
339 | char *body = packet->m_body;
340 | int k = 0;
341 | body[k++] = 0x17;
342 | body[k++] = 0x00;
343 | body[k++] = 0x00;
344 | body[k++] = 0x00;
345 | body[k++] = 0x00;
346 |
347 | body[k++] = 0x01;
348 | body[k++] = sps[1];
349 | body[k++] = sps[2];
350 | body[k++] = sps[3];
351 | body[k++] = (char) 0xff;
352 |
353 | //sps
354 | body[k++] = (char) 0xe1;
355 | body[k++] = (char) ((sps_len >> 8) & 0xff);
356 | body[k++] = (char) (sps_len & 0xff);
357 | memcpy(&body[k], sps, (size_t) sps_len);
358 | k += sps_len;
359 |
360 | //pps
361 | body[k++] = 0x01;
362 | body[k++] = (char) ((pps_len >> 8) & 0xff);
363 | body[k++] = (char) (pps_len & 0xff);
364 | memcpy(&body[k], pps, (size_t) pps_len);
365 | k += pps_len;
366 |
367 | //设置RTMPPacket参数
368 | //此处为类型有两种一种是音频,一种是视频
369 | packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
370 | //包内容长度
371 | packet->m_nBodySize = (uint32_t) body_size;
372 | //块流ID为4
373 | packet->m_nChannel = 0x04;
374 | packet->m_nTimeStamp = 0;
375 | packet->m_hasAbsTimestamp = 0;
376 | packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
377 |
378 | //添加到队列中
379 | add_rtmp_packet(packet);
380 | }
381 |
382 | void RtmpPush::add_264_body(char *buf, int len) {
383 | if (buf[2] == 0x00) {
384 | buf += 4;
385 | len -= 4;
386 | } else if (buf[2] == 0x01) {
387 | buf += 3;
388 | len -= 3;
389 | }
390 | int body_size = len + 9;
391 | RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
392 | if (!packet) {
393 | return;
394 | }
395 | RTMPPacket_Reset(packet);
396 | if (!RTMPPacket_Alloc(packet, (uint32_t) body_size)) {
397 | return;
398 | }
399 | char *body = packet->m_body;
400 | int k = 0;
401 | int type = buf[0] & 0x1f;
402 | if (type == NAL_SLICE_IDR) {
403 | body[k++] = 0x17;
404 | } else {
405 | body[k++] = 0x27;
406 | }
407 | body[k++] = 0x01;
408 | body[k++] = 0x00;
409 | body[k++] = 0x00;
410 | body[k++] = 0x00;
411 |
412 | body[k++] = (char) ((len >> 24) & 0xff);
413 | body[k++] = (char) ((len >> 16) & 0xff);
414 | body[k++] = (char) ((len >> 8) & 0xff);
415 | body[k++] = (char) (len & 0xff);
416 |
417 | memcpy(&body[k++], buf, (size_t) len);
418 | packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
419 | packet->m_nBodySize = (uint32_t) body_size;
420 | packet->m_nChannel = 0x04;
421 | packet->m_nTimeStamp = RTMP_GetTime() - start_time;
422 | packet->m_hasAbsTimestamp = 0;
423 | packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
424 |
425 | //添加到队列
426 | add_rtmp_packet(packet);
427 | }
428 |
429 | void RtmpPush::add_rtmp_packet(RTMPPacket *packet) {
430 | pthread_mutex_lock(&mutex);
431 | queue_append_last(packet);
432 | pthread_cond_signal(&cond);
433 | pthread_mutex_unlock(&mutex);
434 | }
435 |
436 | void *RtmpPush::push_thread(void *args) {
437 | RtmpPush *push = (RtmpPush *) args;
438 |
439 | while (push->publishing) {
440 | pthread_mutex_lock(&push->mutex);
441 | pthread_cond_wait(&push->cond, &push->mutex);
442 | if (!push->publishing) {
443 | break;
444 | }
445 | RTMPPacket *packet = (RTMPPacket *) queue_get_first();
446 | if (packet) {
447 | queue_delete_first();
448 | }
449 | pthread_mutex_unlock(&push->mutex);
450 | if (packet) {
451 | if (RTMP_SendPacket(push->rtmpClient, packet, TRUE)) {
452 | LOGD("发送成功");
453 | } else {
454 | LOGD("发送失败");
455 | }
456 | RTMPPacket_Free(packet);
457 | }
458 | if(queue_size()>50){
459 | LOGD("队列数据过多");
460 | for(int i = 0;i<30;i++){
461 | queue_delete_first();
462 | }
463 | }
464 | }
465 |
466 | return NULL;
467 | }
468 |
469 | }
470 |
--------------------------------------------------------------------------------
/app/src/main/cpp/RtmpPush.h:
--------------------------------------------------------------------------------
1 | #include "jni.h"
2 | #include "librtmp/rtmp.h"
3 |
4 | #include "pthread.h"
5 | #include "strings.h"
6 | #include "include/x264/x264.h"
7 | #include "include/faac/faac.h"
8 | #include "android/log.h"
9 |
10 | extern "C"{
11 | #include "queue.h"
12 | #define _RTMP_Free(_rtmp) if(_rtmp) {RTMP_Free(_rtmp); _rtmp = NULL;}
13 | #define _RTMP_Close(_rtmp) if(_rtmp && RTMP_IsConnected(_rtmp)) RTMP_Close(_rtmp);
14 |
15 | #define LOGD(...) __android_log_print(3,"NDK",__VA_ARGS__)
16 |
17 | class RtmpPush{
18 | //资源
19 | private:
20 | int media_size;
21 | //x264模块
22 | x264_t *videoEncHandle;
23 | x264_picture_t* pic_in;
24 | x264_picture_t* pic_out;
25 |
26 | //rtmp模块
27 | RTMP* rtmpClient;
28 |
29 | //rtmp时间
30 | long start_time;
31 | //推流是否开启
32 | int publishing;
33 |
34 | //faac
35 | faacEncHandle audioEncHandle;
36 | unsigned long nInputSamples;
37 | unsigned long nMaxOutputBytes;
38 |
39 | //线程
40 | pthread_t publisher_tid;
41 | //线程阻塞
42 | pthread_mutex_t mutex ;
43 | pthread_cond_t cond ;
44 |
45 | //构造函数
46 | public:
47 | RtmpPush():media_size(0),videoEncHandle(NULL),pic_in(NULL),pic_out(NULL),rtmpClient(NULL),start_time(0),publishing(0),
48 | publisher_tid(NULL),audioEncHandle(NULL){};
49 | int m_width,m_height,m_bitrate;
50 |
51 |
52 | private:
53 | int init_rtmp(char* out_url);
54 |
55 | static void *push_thread(void *args);
56 |
57 | //视频编码
58 | void push_video(char* data,int index);
59 | //h264头编码
60 | void add_264_header(char* pps, char* sps, int pps_len, int sps_len);
61 | //h264体编码
62 | void add_264_body(char* buf, int len);
63 | //添加rtmp消息体
64 | void add_rtmp_packet(RTMPPacket* packet);
65 | //初始化音频头
66 | int init_audio_header();
67 | public :
68 | //初始化视频 rtmp x264
69 | int init_vedio(const char* out_url,int width,int height,int bitrate);
70 | //初始化音频 sampleRate采样率 channel 声道数
71 | int init_audio(int sampleRate, int channel);
72 | //后置摄像头 数据采集
73 | void video_collect_back(char* data);
74 | //前置摄像头 数据采集
75 | void video_collect_above(char* data);
76 | //原始头像
77 | void video_collect_left(char* data);
78 | //音频流
79 | void audio_cllect(char* data);
80 |
81 | };
82 |
83 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/include/faac/faac.h:
--------------------------------------------------------------------------------
1 | /*
2 | * FAAC - Freeware Advanced Audio Coder
3 | * Copyright (C) 2001 Menno Bakker
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 |
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 | *
19 | * $Id: faac.h,v 1.36 2009/01/25 18:50:32 menno Exp $
20 | */
21 |
22 | #ifndef _FAAC_H_
23 | #define _FAAC_H_
24 |
25 | #ifdef __cplusplus
26 | extern "C" {
27 | #endif /* __cplusplus */
28 |
29 | #if defined(_WIN32) && !defined(__MINGW32__)
30 | # ifndef FAACAPI
31 | # define FAACAPI __stdcall
32 | # endif
33 | #else
34 | # ifndef FAACAPI
35 | # define FAACAPI
36 | # endif
37 | #endif
38 |
39 | #pragma pack(push, 1)
40 |
41 | typedef struct {
42 | void *ptr;
43 | char *name;
44 | }
45 | psymodellist_t;
46 |
47 | #include "faaccfg.h"
48 |
49 |
50 | typedef void *faacEncHandle;
51 |
52 | #ifndef HAVE_INT32_T
53 | typedef signed int int32_t;
54 | #endif
55 |
56 | /*
57 | Allows an application to get FAAC version info. This is intended
58 | purely for informative purposes.
59 |
60 | Returns FAAC_CFG_VERSION.
61 | */
62 | int FAACAPI faacEncGetVersion(char **faac_id_string,
63 | char **faac_copyright_string);
64 |
65 |
66 | faacEncConfigurationPtr FAACAPI
67 | faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
68 |
69 |
70 | int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder,
71 | faacEncConfigurationPtr config);
72 |
73 |
74 | faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate,
75 | unsigned int numChannels,
76 | unsigned long *inputSamples,
77 | unsigned long *maxOutputBytes);
78 |
79 |
80 | int FAACAPI faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer,
81 | unsigned long *pSizeOfDecoderSpecificInfo);
82 |
83 |
84 | int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput,
85 | unsigned char *outputBuffer,
86 | unsigned int bufferSize);
87 |
88 |
89 | int FAACAPI faacEncClose(faacEncHandle hEncoder);
90 |
91 |
92 |
93 | #pragma pack(pop)
94 |
95 | #ifdef __cplusplus
96 | }
97 | #endif /* __cplusplus */
98 |
99 | #endif /* _FAAC_H_ */
100 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/faac/faaccfg.h:
--------------------------------------------------------------------------------
1 | /*
2 | * FAAC - Freeware Advanced Audio Coder
3 | * Copyright (C) 2001 Menno Bakker
4 | *
5 | * This library is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU Lesser General Public
7 | * License as published by the Free Software Foundation; either
8 | * version 2.1 of the License, or (at your option) any later version.
9 | *
10 | * This library is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | * Lesser General Public License for more details.
14 |
15 | * You should have received a copy of the GNU Lesser General Public
16 | * License along with this library; if not, write to the Free Software
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 | *
19 | * $Id: faaccfg.h,v 1.3 2004/07/04 12:12:05 corrados Exp $
20 | */
21 |
22 | #ifndef _FAACCFG_H_
23 | #define _FAACCFG_H_
24 |
25 | #define FAAC_CFG_VERSION 104
26 |
27 | /* MPEG ID's */
28 | #define MPEG2 1
29 | #define MPEG4 0
30 |
31 | /* AAC object types */
32 | #define MAIN 1
33 | #define LOW 2
34 | #define SSR 3
35 | #define LTP 4
36 |
37 | /* Input Formats */
38 | #define FAAC_INPUT_NULL 0
39 | #define FAAC_INPUT_16BIT 1
40 | #define FAAC_INPUT_24BIT 2
41 | #define FAAC_INPUT_32BIT 3
42 | #define FAAC_INPUT_FLOAT 4
43 |
44 | #define SHORTCTL_NORMAL 0
45 | #define SHORTCTL_NOSHORT 1
46 | #define SHORTCTL_NOLONG 2
47 |
48 | #pragma pack(push, 1)
49 | typedef struct faacEncConfiguration
50 | {
51 | /* config version */
52 | int version;
53 |
54 | /* library version */
55 | char *name;
56 |
57 | /* copyright string */
58 | char *copyright;
59 |
60 | /* MPEG version, 2 or 4 */
61 | unsigned int mpegVersion;
62 |
63 | /* AAC object type */
64 | unsigned int aacObjectType;
65 |
66 | /* Allow mid/side coding */
67 | unsigned int allowMidside;
68 |
69 | /* Use one of the channels as LFE channel */
70 | unsigned int useLfe;
71 |
72 | /* Use Temporal Noise Shaping */
73 | unsigned int useTns;
74 |
75 | /* bitrate / channel of AAC file */
76 | unsigned long bitRate;
77 |
78 | /* AAC file frequency bandwidth */
79 | unsigned int bandWidth;
80 |
81 | /* Quantizer quality */
82 | unsigned long quantqual;
83 |
84 | /* Bitstream output format (0 = Raw; 1 = ADTS) */
85 | unsigned int outputFormat;
86 |
87 | /* psychoacoustic model list */
88 | psymodellist_t *psymodellist;
89 |
90 | /* selected index in psymodellist */
91 | unsigned int psymodelidx;
92 |
93 | /*
94 | PCM Sample Input Format
95 | 0 FAAC_INPUT_NULL invalid, signifies a misconfigured config
96 | 1 FAAC_INPUT_16BIT native endian 16bit
97 | 2 FAAC_INPUT_24BIT native endian 24bit in 24 bits (not implemented)
98 | 3 FAAC_INPUT_32BIT native endian 24bit in 32 bits (DEFAULT)
99 | 4 FAAC_INPUT_FLOAT 32bit floating point
100 | */
101 | unsigned int inputFormat;
102 |
103 | /* block type enforcing (SHORTCTL_NORMAL/SHORTCTL_NOSHORT/SHORTCTL_NOLONG) */
104 | int shortctl;
105 |
106 | /*
107 | Channel Remapping
108 |
109 | Default 0, 1, 2, 3 ... 63 (64 is MAX_CHANNELS in coder.h)
110 |
111 | WAVE 4.0 2, 0, 1, 3
112 | WAVE 5.0 2, 0, 1, 3, 4
113 | WAVE 5.1 2, 0, 1, 4, 5, 3
114 | AIFF 5.1 2, 0, 3, 1, 4, 5
115 | */
116 | int channel_map[64];
117 |
118 | } faacEncConfiguration, *faacEncConfigurationPtr;
119 |
120 | #pragma pack(pop)
121 |
122 | #endif /* _FAACCFG_H_ */
123 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/x264/x264_config.h:
--------------------------------------------------------------------------------
1 | #define X264_BIT_DEPTH 8
2 | #define X264_GPL 1
3 | #define X264_INTERLACED 1
4 | #define X264_CHROMA_FORMAT 0
5 | #define X264_VERSION ""
6 | #define X264_POINTVER "0.148.x"
7 |
--------------------------------------------------------------------------------
/app/src/main/cpp/lib/libfaac.a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/cpp/lib/libfaac.a
--------------------------------------------------------------------------------
/app/src/main/cpp/lib/libx264.a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/cpp/lib/libx264.a
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | LOCAL_MODULE:= rtmp
6 |
7 | LOCAL_C_INCLUDES := $(LOCAL_PATH)
8 |
9 | #定义宏遍历jni下所有文件及子目录文件
10 | define audio-all-cpp-files
11 | $(patsubst ./%,%, \
12 | $(shell cd $(LOCAL_PATH) ;\
13 | find $(1) -name "*.c*") \
14 | )
15 | endef
16 | LOCAL_SRC_FILES := $(call audio-all-cpp-files,.)
17 | #LOCAL_SRC_FILES := \
18 | amf.c \
19 | hashswf.c \
20 | log.c \
21 | parseurl.c \
22 | rtmp.c
23 | #LOCAL_SRC_FILES := librtmp.so
24 |
25 | LOCAL_CFLAGS := -Wall -O2 -DSYS=posix -DNO_CRYPTO
26 |
27 |
28 | include $(BUILD_STATIC_LIBRARY)
29 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | #版本声明
2 | cmake_minimum_required(VERSION 3.4.1)
3 |
4 | file(GLOB my_cpp ${CMAKE_SOURCE_DIR}/src/main/cpp/librtmp/*.c)
5 | MESSAGE("我的rtmp源文件:${my_cpp}")
6 |
7 | set(CMAKE_CXX_GLAGS "${CMAKE_CXX_FLAGS} -Wall -O2 -DSYS=posix -DNO_CRYPTO")
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/amf.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2005-2008 Team XBMC
3 | * http://www.xbmc.org
4 | * Copyright (C) 2008-2009 Andrej Stepanchuk
5 | * Copyright (C) 2009-2010 Howard Chu
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | #include
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "amf.h"
32 | #include "log.h"
33 | #include "bytes.h"
34 |
35 | static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID };
36 | static const AMFObject AMFObj_Invalid = { 0, 0 };
37 | static const AVal AV_empty = { 0, 0 };
38 |
39 | /* Data is Big-Endian */
40 | unsigned short
41 | AMF_DecodeInt16(const char *data)
42 | {
43 | unsigned char *c = (unsigned char *) data;
44 | unsigned short val;
45 | val = (c[0] << 8) | c[1];
46 | return val;
47 | }
48 |
49 | unsigned int
50 | AMF_DecodeInt24(const char *data)
51 | {
52 | unsigned char *c = (unsigned char *) data;
53 | unsigned int val;
54 | val = (c[0] << 16) | (c[1] << 8) | c[2];
55 | return val;
56 | }
57 |
58 | unsigned int
59 | AMF_DecodeInt32(const char *data)
60 | {
61 | unsigned char *c = (unsigned char *)data;
62 | unsigned int val;
63 | val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
64 | return val;
65 | }
66 |
67 | void
68 | AMF_DecodeString(const char *data, AVal *bv)
69 | {
70 | bv->av_len = AMF_DecodeInt16(data);
71 | bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
72 | }
73 |
74 | void
75 | AMF_DecodeLongString(const char *data, AVal *bv)
76 | {
77 | bv->av_len = AMF_DecodeInt32(data);
78 | bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
79 | }
80 |
81 | double
82 | AMF_DecodeNumber(const char *data)
83 | {
84 | double dVal;
85 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER
86 | #if __BYTE_ORDER == __BIG_ENDIAN
87 | memcpy(&dVal, data, 8);
88 | #elif __BYTE_ORDER == __LITTLE_ENDIAN
89 | unsigned char *ci, *co;
90 | ci = (unsigned char *)data;
91 | co = (unsigned char *)&dVal;
92 | co[0] = ci[7];
93 | co[1] = ci[6];
94 | co[2] = ci[5];
95 | co[3] = ci[4];
96 | co[4] = ci[3];
97 | co[5] = ci[2];
98 | co[6] = ci[1];
99 | co[7] = ci[0];
100 | #endif
101 | #else
102 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
103 | unsigned char *ci, *co;
104 | ci = (unsigned char *)data;
105 | co = (unsigned char *)&dVal;
106 | co[0] = ci[3];
107 | co[1] = ci[2];
108 | co[2] = ci[1];
109 | co[3] = ci[0];
110 | co[4] = ci[7];
111 | co[5] = ci[6];
112 | co[6] = ci[5];
113 | co[7] = ci[4];
114 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
115 | unsigned char *ci, *co;
116 | ci = (unsigned char *)data;
117 | co = (unsigned char *)&dVal;
118 | co[0] = ci[4];
119 | co[1] = ci[5];
120 | co[2] = ci[6];
121 | co[3] = ci[7];
122 | co[4] = ci[0];
123 | co[5] = ci[1];
124 | co[6] = ci[2];
125 | co[7] = ci[3];
126 | #endif
127 | #endif
128 | return dVal;
129 | }
130 |
131 | int
132 | AMF_DecodeBoolean(const char *data)
133 | {
134 | return *data != 0;
135 | }
136 |
137 | char *
138 | AMF_EncodeInt16(char *output, char *outend, short nVal)
139 | {
140 | if (output+2 > outend)
141 | return NULL;
142 |
143 | output[1] = nVal & 0xff;
144 | output[0] = nVal >> 8;
145 | return output+2;
146 | }
147 |
148 | char *
149 | AMF_EncodeInt24(char *output, char *outend, int nVal)
150 | {
151 | if (output+3 > outend)
152 | return NULL;
153 |
154 | output[2] = nVal & 0xff;
155 | output[1] = nVal >> 8;
156 | output[0] = nVal >> 16;
157 | return output+3;
158 | }
159 |
160 | char *
161 | AMF_EncodeInt32(char *output, char *outend, int nVal)
162 | {
163 | if (output+4 > outend)
164 | return NULL;
165 |
166 | output[3] = nVal & 0xff;
167 | output[2] = nVal >> 8;
168 | output[1] = nVal >> 16;
169 | output[0] = nVal >> 24;
170 | return output+4;
171 | }
172 |
173 | char *
174 | AMF_EncodeString(char *output, char *outend, const AVal *bv)
175 | {
176 | if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) ||
177 | output + 1 + 4 + bv->av_len > outend)
178 | return NULL;
179 |
180 | if (bv->av_len < 65536)
181 | {
182 | *output++ = AMF_STRING;
183 |
184 | output = AMF_EncodeInt16(output, outend, bv->av_len);
185 | }
186 | else
187 | {
188 | *output++ = AMF_LONG_STRING;
189 |
190 | output = AMF_EncodeInt32(output, outend, bv->av_len);
191 | }
192 | memcpy(output, bv->av_val, bv->av_len);
193 | output += bv->av_len;
194 |
195 | return output;
196 | }
197 |
198 | char *
199 | AMF_EncodeNumber(char *output, char *outend, double dVal)
200 | {
201 | if (output+1+8 > outend)
202 | return NULL;
203 |
204 | *output++ = AMF_NUMBER; /* type: Number */
205 |
206 | #if __FLOAT_WORD_ORDER == __BYTE_ORDER
207 | #if __BYTE_ORDER == __BIG_ENDIAN
208 | memcpy(output, &dVal, 8);
209 | #elif __BYTE_ORDER == __LITTLE_ENDIAN
210 | {
211 | unsigned char *ci, *co;
212 | ci = (unsigned char *)&dVal;
213 | co = (unsigned char *)output;
214 | co[0] = ci[7];
215 | co[1] = ci[6];
216 | co[2] = ci[5];
217 | co[3] = ci[4];
218 | co[4] = ci[3];
219 | co[5] = ci[2];
220 | co[6] = ci[1];
221 | co[7] = ci[0];
222 | }
223 | #endif
224 | #else
225 | #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
226 | {
227 | unsigned char *ci, *co;
228 | ci = (unsigned char *)&dVal;
229 | co = (unsigned char *)output;
230 | co[0] = ci[3];
231 | co[1] = ci[2];
232 | co[2] = ci[1];
233 | co[3] = ci[0];
234 | co[4] = ci[7];
235 | co[5] = ci[6];
236 | co[6] = ci[5];
237 | co[7] = ci[4];
238 | }
239 | #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
240 | {
241 | unsigned char *ci, *co;
242 | ci = (unsigned char *)&dVal;
243 | co = (unsigned char *)output;
244 | co[0] = ci[4];
245 | co[1] = ci[5];
246 | co[2] = ci[6];
247 | co[3] = ci[7];
248 | co[4] = ci[0];
249 | co[5] = ci[1];
250 | co[6] = ci[2];
251 | co[7] = ci[3];
252 | }
253 | #endif
254 | #endif
255 |
256 | return output+8;
257 | }
258 |
259 | char *
260 | AMF_EncodeBoolean(char *output, char *outend, int bVal)
261 | {
262 | if (output+2 > outend)
263 | return NULL;
264 |
265 | *output++ = AMF_BOOLEAN;
266 |
267 | *output++ = bVal ? 0x01 : 0x00;
268 |
269 | return output;
270 | }
271 |
272 | char *
273 | AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
274 | {
275 | if (output+2+strName->av_len > outend)
276 | return NULL;
277 | output = AMF_EncodeInt16(output, outend, strName->av_len);
278 |
279 | memcpy(output, strName->av_val, strName->av_len);
280 | output += strName->av_len;
281 |
282 | return AMF_EncodeString(output, outend, strValue);
283 | }
284 |
285 | char *
286 | AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
287 | {
288 | if (output+2+strName->av_len > outend)
289 | return NULL;
290 | output = AMF_EncodeInt16(output, outend, strName->av_len);
291 |
292 | memcpy(output, strName->av_val, strName->av_len);
293 | output += strName->av_len;
294 |
295 | return AMF_EncodeNumber(output, outend, dVal);
296 | }
297 |
298 | char *
299 | AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
300 | {
301 | if (output+2+strName->av_len > outend)
302 | return NULL;
303 | output = AMF_EncodeInt16(output, outend, strName->av_len);
304 |
305 | memcpy(output, strName->av_val, strName->av_len);
306 | output += strName->av_len;
307 |
308 | return AMF_EncodeBoolean(output, outend, bVal);
309 | }
310 |
311 | void
312 | AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
313 | {
314 | *name = prop->p_name;
315 | }
316 |
317 | void
318 | AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
319 | {
320 | prop->p_name = *name;
321 | }
322 |
323 | AMFDataType
324 | AMFProp_GetType(AMFObjectProperty *prop)
325 | {
326 | return prop->p_type;
327 | }
328 |
329 | double
330 | AMFProp_GetNumber(AMFObjectProperty *prop)
331 | {
332 | return prop->p_vu.p_number;
333 | }
334 |
335 | int
336 | AMFProp_GetBoolean(AMFObjectProperty *prop)
337 | {
338 | return prop->p_vu.p_number != 0;
339 | }
340 |
341 | void
342 | AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
343 | {
344 | if (prop->p_type == AMF_STRING)
345 | *str = prop->p_vu.p_aval;
346 | else
347 | *str = AV_empty;
348 | }
349 |
350 | void
351 | AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
352 | {
353 | if (prop->p_type == AMF_OBJECT)
354 | *obj = prop->p_vu.p_object;
355 | else
356 | *obj = AMFObj_Invalid;
357 | }
358 |
359 | int
360 | AMFProp_IsValid(AMFObjectProperty *prop)
361 | {
362 | return prop->p_type != AMF_INVALID;
363 | }
364 |
365 | char *
366 | AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
367 | {
368 | if (prop->p_type == AMF_INVALID)
369 | return NULL;
370 |
371 | if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
372 | return NULL;
373 |
374 | if (prop->p_type != AMF_NULL && prop->p_name.av_len)
375 | {
376 | *pBuffer++ = prop->p_name.av_len >> 8;
377 | *pBuffer++ = prop->p_name.av_len & 0xff;
378 | memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len);
379 | pBuffer += prop->p_name.av_len;
380 | }
381 |
382 | switch (prop->p_type)
383 | {
384 | case AMF_NUMBER:
385 | pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
386 | break;
387 |
388 | case AMF_BOOLEAN:
389 | pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
390 | break;
391 |
392 | case AMF_STRING:
393 | pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
394 | break;
395 |
396 | case AMF_NULL:
397 | if (pBuffer+1 >= pBufEnd)
398 | return NULL;
399 | *pBuffer++ = AMF_NULL;
400 | break;
401 |
402 | case AMF_OBJECT:
403 | pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
404 | break;
405 |
406 | case AMF_ECMA_ARRAY:
407 | pBuffer = AMF_EncodeEcmaArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
408 | break;
409 |
410 | case AMF_STRICT_ARRAY:
411 | pBuffer = AMF_EncodeArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
412 | break;
413 |
414 | default:
415 | RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
416 | pBuffer = NULL;
417 | };
418 |
419 | return pBuffer;
420 | }
421 |
422 | #define AMF3_INTEGER_MAX 268435455
423 | #define AMF3_INTEGER_MIN -268435456
424 |
425 | int
426 | AMF3ReadInteger(const char *data, int32_t *valp)
427 | {
428 | int i = 0;
429 | int32_t val = 0;
430 |
431 | while (i <= 2)
432 | { /* handle first 3 bytes */
433 | if (data[i] & 0x80)
434 | { /* byte used */
435 | val <<= 7; /* shift up */
436 | val |= (data[i] & 0x7f); /* add bits */
437 | i++;
438 | }
439 | else
440 | {
441 | break;
442 | }
443 | }
444 |
445 | if (i > 2)
446 | { /* use 4th byte, all 8bits */
447 | val <<= 8;
448 | val |= data[3];
449 |
450 | /* range check */
451 | if (val > AMF3_INTEGER_MAX)
452 | val -= (1 << 29);
453 | }
454 | else
455 | { /* use 7bits of last unparsed byte (0xxxxxxx) */
456 | val <<= 7;
457 | val |= data[i];
458 | }
459 |
460 | *valp = val;
461 |
462 | return i > 2 ? 4 : i + 1;
463 | }
464 |
465 | int
466 | AMF3ReadString(const char *data, AVal *str)
467 | {
468 | int32_t ref = 0;
469 | int len;
470 | assert(str != 0);
471 |
472 | len = AMF3ReadInteger(data, &ref);
473 | data += len;
474 |
475 | if ((ref & 0x1) == 0)
476 | { /* reference: 0xxx */
477 | uint32_t refIndex = (ref >> 1);
478 | RTMP_Log(RTMP_LOGDEBUG,
479 | "%s, string reference, index: %d, not supported, ignoring!",
480 | __FUNCTION__, refIndex);
481 | str->av_val = NULL;
482 | str->av_len = 0;
483 | return len;
484 | }
485 | else
486 | {
487 | uint32_t nSize = (ref >> 1);
488 |
489 | str->av_val = (char *)data;
490 | str->av_len = nSize;
491 |
492 | return len + nSize;
493 | }
494 | return len;
495 | }
496 |
497 | int
498 | AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
499 | int bDecodeName)
500 | {
501 | int nOriginalSize = nSize;
502 | AMF3DataType type;
503 |
504 | prop->p_name.av_len = 0;
505 | prop->p_name.av_val = NULL;
506 |
507 | if (nSize == 0 || !pBuffer)
508 | {
509 | RTMP_Log(RTMP_LOGDEBUG, "empty buffer/no buffer pointer!");
510 | return -1;
511 | }
512 |
513 | /* decode name */
514 | if (bDecodeName)
515 | {
516 | AVal name;
517 | int nRes = AMF3ReadString(pBuffer, &name);
518 |
519 | if (name.av_len <= 0)
520 | return nRes;
521 |
522 | nSize -= nRes;
523 | if (nSize <= 0)
524 | return -1;
525 | prop->p_name = name;
526 | pBuffer += nRes;
527 | }
528 |
529 | /* decode */
530 | type = *pBuffer++;
531 | nSize--;
532 |
533 | switch (type)
534 | {
535 | case AMF3_UNDEFINED:
536 | case AMF3_NULL:
537 | prop->p_type = AMF_NULL;
538 | break;
539 | case AMF3_FALSE:
540 | prop->p_type = AMF_BOOLEAN;
541 | prop->p_vu.p_number = 0.0;
542 | break;
543 | case AMF3_TRUE:
544 | prop->p_type = AMF_BOOLEAN;
545 | prop->p_vu.p_number = 1.0;
546 | break;
547 | case AMF3_INTEGER:
548 | {
549 | int32_t res = 0;
550 | int len = AMF3ReadInteger(pBuffer, &res);
551 | prop->p_vu.p_number = (double)res;
552 | prop->p_type = AMF_NUMBER;
553 | nSize -= len;
554 | break;
555 | }
556 | case AMF3_DOUBLE:
557 | if (nSize < 8)
558 | return -1;
559 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
560 | prop->p_type = AMF_NUMBER;
561 | nSize -= 8;
562 | break;
563 | case AMF3_STRING:
564 | case AMF3_XML_DOC:
565 | case AMF3_XML:
566 | {
567 | int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval);
568 | prop->p_type = AMF_STRING;
569 | nSize -= len;
570 | break;
571 | }
572 | case AMF3_DATE:
573 | {
574 | int32_t res = 0;
575 | int len = AMF3ReadInteger(pBuffer, &res);
576 |
577 | nSize -= len;
578 | pBuffer += len;
579 |
580 | if ((res & 0x1) == 0)
581 | { /* reference */
582 | uint32_t nIndex = (res >> 1);
583 | RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex);
584 | }
585 | else
586 | {
587 | if (nSize < 8)
588 | return -1;
589 |
590 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
591 | nSize -= 8;
592 | prop->p_type = AMF_NUMBER;
593 | }
594 | break;
595 | }
596 | case AMF3_OBJECT:
597 | {
598 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
599 | if (nRes == -1)
600 | return -1;
601 | nSize -= nRes;
602 | prop->p_type = AMF_OBJECT;
603 | break;
604 | }
605 | case AMF3_ARRAY:
606 | case AMF3_BYTE_ARRAY:
607 | default:
608 | RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
609 | __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
610 | return -1;
611 | }
612 | if (nSize < 0)
613 | return -1;
614 |
615 | return nOriginalSize - nSize;
616 | }
617 |
618 | int
619 | AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
620 | int bDecodeName)
621 | {
622 | int nOriginalSize = nSize;
623 | int nRes;
624 |
625 | prop->p_name.av_len = 0;
626 | prop->p_name.av_val = NULL;
627 |
628 | if (nSize == 0 || !pBuffer)
629 | {
630 | RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
631 | return -1;
632 | }
633 |
634 | if (bDecodeName && nSize < 4)
635 | { /* at least name (length + at least 1 byte) and 1 byte of data */
636 | RTMP_Log(RTMP_LOGDEBUG,
637 | "%s: Not enough data for decoding with name, less than 4 bytes!",
638 | __FUNCTION__);
639 | return -1;
640 | }
641 |
642 | if (bDecodeName)
643 | {
644 | unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
645 | if (nNameSize > nSize - 2)
646 | {
647 | RTMP_Log(RTMP_LOGDEBUG,
648 | "%s: Name size out of range: namesize (%d) > len (%d) - 2",
649 | __FUNCTION__, nNameSize, nSize);
650 | return -1;
651 | }
652 |
653 | AMF_DecodeString(pBuffer, &prop->p_name);
654 | nSize -= 2 + nNameSize;
655 | pBuffer += 2 + nNameSize;
656 | }
657 |
658 | if (nSize == 0)
659 | {
660 | return -1;
661 | }
662 |
663 | nSize--;
664 |
665 | prop->p_type = *pBuffer++;
666 | switch (prop->p_type)
667 | {
668 | case AMF_NUMBER:
669 | if (nSize < 8)
670 | return -1;
671 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
672 | nSize -= 8;
673 | break;
674 | case AMF_BOOLEAN:
675 | if (nSize < 1)
676 | return -1;
677 | prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
678 | nSize--;
679 | break;
680 | case AMF_STRING:
681 | {
682 | unsigned short nStringSize = AMF_DecodeInt16(pBuffer);
683 |
684 | if (nSize < (long)nStringSize + 2)
685 | return -1;
686 | AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
687 | nSize -= (2 + nStringSize);
688 | break;
689 | }
690 | case AMF_OBJECT:
691 | {
692 | int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
693 | if (nRes == -1)
694 | return -1;
695 | nSize -= nRes;
696 | break;
697 | }
698 | case AMF_MOVIECLIP:
699 | {
700 | RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
701 | return -1;
702 | break;
703 | }
704 | case AMF_NULL:
705 | case AMF_UNDEFINED:
706 | case AMF_UNSUPPORTED:
707 | prop->p_type = AMF_NULL;
708 | break;
709 | case AMF_REFERENCE:
710 | {
711 | RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
712 | return -1;
713 | break;
714 | }
715 | case AMF_ECMA_ARRAY:
716 | {
717 | nSize -= 4;
718 |
719 | /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
720 | nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE);
721 | if (nRes == -1)
722 | return -1;
723 | nSize -= nRes;
724 | break;
725 | }
726 | case AMF_OBJECT_END:
727 | {
728 | return -1;
729 | break;
730 | }
731 | case AMF_STRICT_ARRAY:
732 | {
733 | unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
734 | nSize -= 4;
735 |
736 | nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
737 | nArrayLen, FALSE);
738 | if (nRes == -1)
739 | return -1;
740 | nSize -= nRes;
741 | break;
742 | }
743 | case AMF_DATE:
744 | {
745 | RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
746 |
747 | if (nSize < 10)
748 | return -1;
749 |
750 | prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
751 | prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
752 |
753 | nSize -= 10;
754 | break;
755 | }
756 | case AMF_LONG_STRING:
757 | case AMF_XML_DOC:
758 | {
759 | unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
760 | if (nSize < (long)nStringSize + 4)
761 | return -1;
762 | AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
763 | nSize -= (4 + nStringSize);
764 | if (prop->p_type == AMF_LONG_STRING)
765 | prop->p_type = AMF_STRING;
766 | break;
767 | }
768 | case AMF_RECORDSET:
769 | {
770 | RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
771 | return -1;
772 | break;
773 | }
774 | case AMF_TYPED_OBJECT:
775 | {
776 | RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
777 | return -1;
778 | break;
779 | }
780 | case AMF_AVMPLUS:
781 | {
782 | int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
783 | if (nRes == -1)
784 | return -1;
785 | nSize -= nRes;
786 | prop->p_type = AMF_OBJECT;
787 | break;
788 | }
789 | default:
790 | RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
791 | prop->p_type, pBuffer - 1);
792 | return -1;
793 | }
794 |
795 | return nOriginalSize - nSize;
796 | }
797 |
798 | void
799 | AMFProp_Dump(AMFObjectProperty *prop)
800 | {
801 | char strRes[256];
802 | char str[256];
803 | AVal name;
804 |
805 | if (prop->p_type == AMF_INVALID)
806 | {
807 | RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID");
808 | return;
809 | }
810 |
811 | if (prop->p_type == AMF_NULL)
812 | {
813 | RTMP_Log(RTMP_LOGDEBUG, "Property: NULL");
814 | return;
815 | }
816 |
817 | if (prop->p_name.av_len)
818 | {
819 | name = prop->p_name;
820 | }
821 | else
822 | {
823 | name.av_val = "no-name.";
824 | name.av_len = sizeof("no-name.") - 1;
825 | }
826 | if (name.av_len > 18)
827 | name.av_len = 18;
828 |
829 | snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val);
830 |
831 | if (prop->p_type == AMF_OBJECT)
832 | {
833 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes);
834 | AMF_Dump(&prop->p_vu.p_object);
835 | return;
836 | }
837 | else if (prop->p_type == AMF_ECMA_ARRAY)
838 | {
839 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%sECMA_ARRAY>", strRes);
840 | AMF_Dump(&prop->p_vu.p_object);
841 | return;
842 | }
843 | else if (prop->p_type == AMF_STRICT_ARRAY)
844 | {
845 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%sSTRICT_ARRAY>", strRes);
846 | AMF_Dump(&prop->p_vu.p_object);
847 | return;
848 | }
849 |
850 | switch (prop->p_type)
851 | {
852 | case AMF_NUMBER:
853 | snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
854 | break;
855 | case AMF_BOOLEAN:
856 | snprintf(str, 255, "BOOLEAN:\t%s",
857 | prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
858 | break;
859 | case AMF_STRING:
860 | snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
861 | prop->p_vu.p_aval.av_val);
862 | break;
863 | case AMF_DATE:
864 | snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
865 | prop->p_vu.p_number, prop->p_UTCoffset);
866 | break;
867 | default:
868 | snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type);
869 | }
870 |
871 | RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str);
872 | }
873 |
874 | void
875 | AMFProp_Reset(AMFObjectProperty *prop)
876 | {
877 | if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY ||
878 | prop->p_type == AMF_STRICT_ARRAY)
879 | AMF_Reset(&prop->p_vu.p_object);
880 | else
881 | {
882 | prop->p_vu.p_aval.av_len = 0;
883 | prop->p_vu.p_aval.av_val = NULL;
884 | }
885 | prop->p_type = AMF_INVALID;
886 | }
887 |
888 | /* AMFObject */
889 |
890 | char *
891 | AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
892 | {
893 | int i;
894 |
895 | if (pBuffer+4 >= pBufEnd)
896 | return NULL;
897 |
898 | *pBuffer++ = AMF_OBJECT;
899 |
900 | for (i = 0; i < obj->o_num; i++)
901 | {
902 | char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
903 | if (res == NULL)
904 | {
905 | RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
906 | i);
907 | break;
908 | }
909 | else
910 | {
911 | pBuffer = res;
912 | }
913 | }
914 |
915 | if (pBuffer + 3 >= pBufEnd)
916 | return NULL; /* no room for the end marker */
917 |
918 | pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
919 |
920 | return pBuffer;
921 | }
922 |
923 | char *
924 | AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
925 | {
926 | int i;
927 |
928 | if (pBuffer+4 >= pBufEnd)
929 | return NULL;
930 |
931 | *pBuffer++ = AMF_ECMA_ARRAY;
932 |
933 | pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
934 |
935 | for (i = 0; i < obj->o_num; i++)
936 | {
937 | char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
938 | if (res == NULL)
939 | {
940 | RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
941 | i);
942 | break;
943 | }
944 | else
945 | {
946 | pBuffer = res;
947 | }
948 | }
949 |
950 | if (pBuffer + 3 >= pBufEnd)
951 | return NULL; /* no room for the end marker */
952 |
953 | pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
954 |
955 | return pBuffer;
956 | }
957 |
958 | char *
959 | AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
960 | {
961 | int i;
962 |
963 | if (pBuffer+4 >= pBufEnd)
964 | return NULL;
965 |
966 | *pBuffer++ = AMF_STRICT_ARRAY;
967 |
968 | pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
969 |
970 | for (i = 0; i < obj->o_num; i++)
971 | {
972 | char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
973 | if (res == NULL)
974 | {
975 | RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
976 | i);
977 | break;
978 | }
979 | else
980 | {
981 | pBuffer = res;
982 | }
983 | }
984 |
985 | //if (pBuffer + 3 >= pBufEnd)
986 | // return NULL; /* no room for the end marker */
987 |
988 | //pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
989 |
990 | return pBuffer;
991 | }
992 |
993 | int
994 | AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
995 | int nArrayLen, int bDecodeName)
996 | {
997 | int nOriginalSize = nSize;
998 | int bError = FALSE;
999 |
1000 | obj->o_num = 0;
1001 | obj->o_props = NULL;
1002 | while (nArrayLen > 0)
1003 | {
1004 | AMFObjectProperty prop;
1005 | int nRes;
1006 | nArrayLen--;
1007 |
1008 | if (nSize <= 0)
1009 | {
1010 | bError = TRUE;
1011 | break;
1012 | }
1013 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1014 | if (nRes == -1)
1015 | {
1016 | bError = TRUE;
1017 | break;
1018 | }
1019 | else
1020 | {
1021 | nSize -= nRes;
1022 | pBuffer += nRes;
1023 | AMF_AddProp(obj, &prop);
1024 | }
1025 | }
1026 | if (bError)
1027 | return -1;
1028 |
1029 | return nOriginalSize - nSize;
1030 | }
1031 |
1032 | int
1033 | AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
1034 | {
1035 | int nOriginalSize = nSize;
1036 | int32_t ref;
1037 | int len;
1038 |
1039 | obj->o_num = 0;
1040 | obj->o_props = NULL;
1041 | if (bAMFData)
1042 | {
1043 | if (*pBuffer != AMF3_OBJECT)
1044 | RTMP_Log(RTMP_LOGERROR,
1045 | "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
1046 | pBuffer++;
1047 | nSize--;
1048 | }
1049 |
1050 | ref = 0;
1051 | len = AMF3ReadInteger(pBuffer, &ref);
1052 | pBuffer += len;
1053 | nSize -= len;
1054 |
1055 | if ((ref & 1) == 0)
1056 | { /* object reference, 0xxx */
1057 | uint32_t objectIndex = (ref >> 1);
1058 |
1059 | RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex);
1060 | }
1061 | else /* object instance */
1062 | {
1063 | int32_t classRef = (ref >> 1);
1064 |
1065 | AMF3ClassDef cd = { {0, 0}
1066 | };
1067 | AMFObjectProperty prop;
1068 |
1069 | if ((classRef & 0x1) == 0)
1070 | { /* class reference */
1071 | uint32_t classIndex = (classRef >> 1);
1072 | RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex);
1073 | }
1074 | else
1075 | {
1076 | int32_t classExtRef = (classRef >> 1);
1077 | int i, cdnum;
1078 |
1079 | cd.cd_externalizable = (classExtRef & 0x1) == 1;
1080 | cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
1081 |
1082 | cdnum = classExtRef >> 2;
1083 |
1084 | /* class name */
1085 |
1086 | len = AMF3ReadString(pBuffer, &cd.cd_name);
1087 | nSize -= len;
1088 | pBuffer += len;
1089 |
1090 | /*std::string str = className; */
1091 |
1092 | RTMP_Log(RTMP_LOGDEBUG,
1093 | "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
1094 | cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
1095 | cd.cd_num);
1096 |
1097 | for (i = 0; i < cdnum; i++)
1098 | {
1099 | AVal memberName;
1100 | if (nSize <=0)
1101 | {
1102 | invalid:
1103 | RTMP_Log(RTMP_LOGDEBUG, "%s, invalid class encoding!",
1104 | __FUNCTION__);
1105 | return nOriginalSize;
1106 | }
1107 | len = AMF3ReadString(pBuffer, &memberName);
1108 | RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
1109 | AMF3CD_AddProp(&cd, &memberName);
1110 | nSize -= len;
1111 | pBuffer += len;
1112 | }
1113 | }
1114 |
1115 | /* add as referencable object */
1116 |
1117 | if (cd.cd_externalizable)
1118 | {
1119 | int nRes;
1120 | AVal name = AVC("DEFAULT_ATTRIBUTE");
1121 |
1122 | RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check");
1123 |
1124 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1125 | if (nRes == -1)
1126 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1127 | __FUNCTION__);
1128 | else
1129 | {
1130 | nSize -= nRes;
1131 | pBuffer += nRes;
1132 | }
1133 |
1134 | AMFProp_SetName(&prop, &name);
1135 | AMF_AddProp(obj, &prop);
1136 | }
1137 | else
1138 | {
1139 | int nRes, i;
1140 | for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
1141 | {
1142 | if (nSize <=0)
1143 | goto invalid;
1144 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1145 | if (nRes == -1)
1146 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1147 | __FUNCTION__);
1148 |
1149 | AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
1150 | AMF_AddProp(obj, &prop);
1151 |
1152 | pBuffer += nRes;
1153 | nSize -= nRes;
1154 | }
1155 | if (cd.cd_dynamic)
1156 | {
1157 | int len = 0;
1158 |
1159 | do
1160 | {
1161 | if (nSize <=0)
1162 | goto invalid;
1163 | nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
1164 | AMF_AddProp(obj, &prop);
1165 |
1166 | pBuffer += nRes;
1167 | nSize -= nRes;
1168 |
1169 | len = prop.p_name.av_len;
1170 | }
1171 | while (len > 0);
1172 | }
1173 | }
1174 | RTMP_Log(RTMP_LOGDEBUG, "class object!");
1175 | }
1176 | return nOriginalSize - nSize;
1177 | }
1178 |
1179 | int
1180 | AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName)
1181 | {
1182 | int nOriginalSize = nSize;
1183 | int bError = FALSE; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1184 |
1185 | obj->o_num = 0;
1186 | obj->o_props = NULL;
1187 | while (nSize > 0)
1188 | {
1189 | AMFObjectProperty prop;
1190 | int nRes;
1191 |
1192 | if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
1193 | {
1194 | nSize -= 3;
1195 | bError = FALSE;
1196 | break;
1197 | }
1198 |
1199 | if (bError)
1200 | {
1201 | RTMP_Log(RTMP_LOGERROR,
1202 | "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1203 | nSize--;
1204 | pBuffer++;
1205 | continue;
1206 | }
1207 |
1208 | nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1209 | if (nRes == -1)
1210 | {
1211 | bError = TRUE;
1212 | break;
1213 | }
1214 | else
1215 | {
1216 | nSize -= nRes;
1217 | if (nSize < 0)
1218 | {
1219 | bError = TRUE;
1220 | break;
1221 | }
1222 | pBuffer += nRes;
1223 | AMF_AddProp(obj, &prop);
1224 | }
1225 | }
1226 |
1227 | if (bError)
1228 | return -1;
1229 |
1230 | return nOriginalSize - nSize;
1231 | }
1232 |
1233 | void
1234 | AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
1235 | {
1236 | if (!(obj->o_num & 0x0f))
1237 | obj->o_props =
1238 | realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
1239 | memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty));
1240 | }
1241 |
1242 | int
1243 | AMF_CountProp(AMFObject *obj)
1244 | {
1245 | return obj->o_num;
1246 | }
1247 |
1248 | AMFObjectProperty *
1249 | AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
1250 | {
1251 | if (nIndex >= 0)
1252 | {
1253 | if (nIndex < obj->o_num)
1254 | return &obj->o_props[nIndex];
1255 | }
1256 | else
1257 | {
1258 | int n;
1259 | for (n = 0; n < obj->o_num; n++)
1260 | {
1261 | if (AVMATCH(&obj->o_props[n].p_name, name))
1262 | return &obj->o_props[n];
1263 | }
1264 | }
1265 |
1266 | return (AMFObjectProperty *)&AMFProp_Invalid;
1267 | }
1268 |
1269 | void
1270 | AMF_Dump(AMFObject *obj)
1271 | {
1272 | int n;
1273 | RTMP_Log(RTMP_LOGDEBUG, "(object begin)");
1274 | for (n = 0; n < obj->o_num; n++)
1275 | {
1276 | AMFProp_Dump(&obj->o_props[n]);
1277 | }
1278 | RTMP_Log(RTMP_LOGDEBUG, "(object end)");
1279 | }
1280 |
1281 | void
1282 | AMF_Reset(AMFObject *obj)
1283 | {
1284 | int n;
1285 | for (n = 0; n < obj->o_num; n++)
1286 | {
1287 | AMFProp_Reset(&obj->o_props[n]);
1288 | }
1289 | free(obj->o_props);
1290 | obj->o_props = NULL;
1291 | obj->o_num = 0;
1292 | }
1293 |
1294 |
1295 | /* AMF3ClassDefinition */
1296 |
1297 | void
1298 | AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop)
1299 | {
1300 | if (!(cd->cd_num & 0x0f))
1301 | cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
1302 | cd->cd_props[cd->cd_num++] = *prop;
1303 | }
1304 |
1305 | AVal *
1306 | AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex)
1307 | {
1308 | if (nIndex >= cd->cd_num)
1309 | return (AVal *)&AV_empty;
1310 | return &cd->cd_props[nIndex];
1311 | }
1312 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/amf.h:
--------------------------------------------------------------------------------
1 | #ifndef __AMF_H__
2 | #define __AMF_H__
3 | /*
4 | * Copyright (C) 2005-2008 Team XBMC
5 | * http://www.xbmc.org
6 | * Copyright (C) 2008-2009 Andrej Stepanchuk
7 | * Copyright (C) 2009-2010 Howard Chu
8 | *
9 | * This file is part of librtmp.
10 | *
11 | * librtmp is free software; you can redistribute it and/or modify
12 | * it under the terms of the GNU Lesser General Public License as
13 | * published by the Free Software Foundation; either version 2.1,
14 | * or (at your option) any later version.
15 | *
16 | * librtmp is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Lesser General Public License
22 | * along with librtmp see the file COPYING. If not, write to
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 | * Boston, MA 02110-1301, USA.
25 | * http://www.gnu.org/copyleft/lgpl.html
26 | */
27 |
28 | #include
29 |
30 | #ifndef TRUE
31 | #define TRUE 1
32 | #define FALSE 0
33 | #endif
34 |
35 | #ifdef __cplusplus
36 | extern "C"
37 | {
38 | #endif
39 |
40 | typedef enum
41 | { AMF_NUMBER = 0, AMF_BOOLEAN, AMF_STRING, AMF_OBJECT,
42 | AMF_MOVIECLIP, /* reserved, not used */
43 | AMF_NULL, AMF_UNDEFINED, AMF_REFERENCE, AMF_ECMA_ARRAY, AMF_OBJECT_END,
44 | AMF_STRICT_ARRAY, AMF_DATE, AMF_LONG_STRING, AMF_UNSUPPORTED,
45 | AMF_RECORDSET, /* reserved, not used */
46 | AMF_XML_DOC, AMF_TYPED_OBJECT,
47 | AMF_AVMPLUS, /* switch to AMF3 */
48 | AMF_INVALID = 0xff
49 | } AMFDataType;
50 |
51 | typedef enum
52 | { AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE, AMF3_TRUE,
53 | AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE,
54 | AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY
55 | } AMF3DataType;
56 |
57 | typedef struct AVal
58 | {
59 | char *av_val;
60 | int av_len;
61 | } AVal;
62 | #define AVC(str) {str,sizeof(str)-1}
63 | #define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
64 |
65 | struct AMFObjectProperty;
66 |
67 | typedef struct AMFObject
68 | {
69 | int o_num;
70 | struct AMFObjectProperty *o_props;
71 | } AMFObject;
72 |
73 | typedef struct AMFObjectProperty
74 | {
75 | AVal p_name;
76 | AMFDataType p_type;
77 | union
78 | {
79 | double p_number;
80 | AVal p_aval;
81 | AMFObject p_object;
82 | } p_vu;
83 | int16_t p_UTCoffset;
84 | } AMFObjectProperty;
85 |
86 | char *AMF_EncodeString(char *output, char *outend, const AVal * str);
87 | char *AMF_EncodeNumber(char *output, char *outend, double dVal);
88 | char *AMF_EncodeInt16(char *output, char *outend, short nVal);
89 | char *AMF_EncodeInt24(char *output, char *outend, int nVal);
90 | char *AMF_EncodeInt32(char *output, char *outend, int nVal);
91 | char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
92 |
93 | /* Shortcuts for AMFProp_Encode */
94 | char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
95 | char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
96 | char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
97 |
98 | unsigned short AMF_DecodeInt16(const char *data);
99 | unsigned int AMF_DecodeInt24(const char *data);
100 | unsigned int AMF_DecodeInt32(const char *data);
101 | void AMF_DecodeString(const char *data, AVal * str);
102 | void AMF_DecodeLongString(const char *data, AVal * str);
103 | int AMF_DecodeBoolean(const char *data);
104 | double AMF_DecodeNumber(const char *data);
105 |
106 | char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
107 | char *AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd);
108 | char *AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd);
109 |
110 | int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
111 | int bDecodeName);
112 | int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
113 | int nArrayLen, int bDecodeName);
114 | int AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize,
115 | int bDecodeName);
116 | void AMF_Dump(AMFObject * obj);
117 | void AMF_Reset(AMFObject * obj);
118 |
119 | void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
120 | int AMF_CountProp(AMFObject * obj);
121 | AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name,
122 | int nIndex);
123 |
124 | AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
125 | void AMFProp_SetNumber(AMFObjectProperty * prop, double dval);
126 | void AMFProp_SetBoolean(AMFObjectProperty * prop, int bflag);
127 | void AMFProp_SetString(AMFObjectProperty * prop, AVal * str);
128 | void AMFProp_SetObject(AMFObjectProperty * prop, AMFObject * obj);
129 |
130 | void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
131 | void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
132 | double AMFProp_GetNumber(AMFObjectProperty * prop);
133 | int AMFProp_GetBoolean(AMFObjectProperty * prop);
134 | void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
135 | void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
136 |
137 | int AMFProp_IsValid(AMFObjectProperty * prop);
138 |
139 | char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
140 | int AMF3Prop_Decode(AMFObjectProperty * prop, const char *pBuffer,
141 | int nSize, int bDecodeName);
142 | int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer,
143 | int nSize, int bDecodeName);
144 |
145 | void AMFProp_Dump(AMFObjectProperty * prop);
146 | void AMFProp_Reset(AMFObjectProperty * prop);
147 |
148 | typedef struct AMF3ClassDef
149 | {
150 | AVal cd_name;
151 | char cd_externalizable;
152 | char cd_dynamic;
153 | int cd_num;
154 | AVal *cd_props;
155 | } AMF3ClassDef;
156 |
157 | void AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop);
158 | AVal *AMF3CD_GetProp(AMF3ClassDef * cd, int idx);
159 |
160 | #ifdef __cplusplus
161 | }
162 | #endif
163 |
164 | #endif /* __AMF_H__ */
165 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/bytes.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2005-2008 Team XBMC
3 | * http://www.xbmc.org
4 | * Copyright (C) 2008-2009 Andrej Stepanchuk
5 | * Copyright (C) 2009-2010 Howard Chu
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | #ifndef __BYTES_H__
27 | #define __BYTES_H__
28 |
29 | #include
30 |
31 | #ifdef _WIN32
32 | /* Windows is little endian only */
33 | #define __LITTLE_ENDIAN 1234
34 | #define __BIG_ENDIAN 4321
35 | #define __BYTE_ORDER __LITTLE_ENDIAN
36 | #define __FLOAT_WORD_ORDER __BYTE_ORDER
37 |
38 | typedef unsigned char uint8_t;
39 |
40 | #else /* !_WIN32 */
41 |
42 | #include
43 |
44 | #if defined(BYTE_ORDER) && !defined(__BYTE_ORDER)
45 | #define __BYTE_ORDER BYTE_ORDER
46 | #endif
47 |
48 | #if defined(BIG_ENDIAN) && !defined(__BIG_ENDIAN)
49 | #define __BIG_ENDIAN BIG_ENDIAN
50 | #endif
51 |
52 | #if defined(LITTLE_ENDIAN) && !defined(__LITTLE_ENDIAN)
53 | #define __LITTLE_ENDIAN LITTLE_ENDIAN
54 | #endif
55 |
56 | #endif /* !_WIN32 */
57 |
58 | /* define default endianness */
59 | #ifndef __LITTLE_ENDIAN
60 | #define __LITTLE_ENDIAN 1234
61 | #endif
62 |
63 | #ifndef __BIG_ENDIAN
64 | #define __BIG_ENDIAN 4321
65 | #endif
66 |
67 | #ifndef __BYTE_ORDER
68 | #warning "Byte order not defined on your system, assuming little endian!"
69 | #define __BYTE_ORDER __LITTLE_ENDIAN
70 | #endif
71 |
72 | /* ok, we assume to have the same float word order and byte order if float word order is not defined */
73 | #ifndef __FLOAT_WORD_ORDER
74 | #warning "Float word order not defined, assuming the same as byte order!"
75 | #define __FLOAT_WORD_ORDER __BYTE_ORDER
76 | #endif
77 |
78 | #if !defined(__BYTE_ORDER) || !defined(__FLOAT_WORD_ORDER)
79 | #error "Undefined byte or float word order!"
80 | #endif
81 |
82 | #if __FLOAT_WORD_ORDER != __BIG_ENDIAN && __FLOAT_WORD_ORDER != __LITTLE_ENDIAN
83 | #error "Unknown/unsupported float word order!"
84 | #endif
85 |
86 | #if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
87 | #error "Unknown/unsupported byte order!"
88 | #endif
89 |
90 | #endif
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/dh.h:
--------------------------------------------------------------------------------
1 | /* RTMPDump - Diffie-Hellmann Key Exchange
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #ifdef USE_POLARSSL
31 | #include
32 | typedef mpi * MP_t;
33 | #define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m)
34 | #define MP_set_w(mpi, w) mpi_lset(mpi, w)
35 | #define MP_cmp(u, v) mpi_cmp_mpi(u, v)
36 | #define MP_set(u, v) mpi_copy(u, v)
37 | #define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w)
38 | #define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1)
39 | #define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL)
40 | #define MP_free(mpi) mpi_free(mpi); free(mpi)
41 | #define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0
42 | #define MP_bytes(u) mpi_size(u)
43 | #define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len)
44 | #define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len)
45 |
46 | typedef struct MDH {
47 | MP_t p;
48 | MP_t g;
49 | MP_t pub_key;
50 | MP_t priv_key;
51 | long length;
52 | dhm_context ctx;
53 | } MDH;
54 |
55 | #define MDH_new() calloc(1,sizeof(MDH))
56 | #define MDH_free(vp) {MDH *_dh = vp; dhm_free(&_dh->ctx); MP_free(_dh->p); MP_free(_dh->g); MP_free(_dh->pub_key); MP_free(_dh->priv_key); free(_dh);}
57 |
58 | static int MDH_generate_key(MDH *dh)
59 | {
60 | unsigned char out[2];
61 | MP_set(&dh->ctx.P, dh->p);
62 | MP_set(&dh->ctx.G, dh->g);
63 | dh->ctx.len = 128;
64 | dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs);
65 | MP_new(dh->pub_key);
66 | MP_new(dh->priv_key);
67 | MP_set(dh->pub_key, &dh->ctx.GX);
68 | MP_set(dh->priv_key, &dh->ctx.X);
69 | return 1;
70 | }
71 |
72 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
73 | {
74 | MP_set(&dh->ctx.GY, pub);
75 | dhm_calc_secret(&dh->ctx, secret, &len);
76 | return 0;
77 | }
78 |
79 | #elif defined(USE_GNUTLS)
80 | #include
81 | #include
82 | #include
83 | typedef mpz_ptr MP_t;
84 | #define MP_new(m) m = malloc(sizeof(*m)); mpz_init2(m, 1)
85 | #define MP_set_w(mpi, w) mpz_set_ui(mpi, w)
86 | #define MP_cmp(u, v) mpz_cmp(u, v)
87 | #define MP_set(u, v) mpz_set(u, v)
88 | #define MP_sub_w(mpi, w) mpz_sub_ui(mpi, mpi, w)
89 | #define MP_cmp_1(mpi) mpz_cmp_ui(mpi, 1)
90 | #define MP_modexp(r, y, q, p) mpz_powm(r, y, q, p)
91 | #define MP_free(mpi) mpz_clear(mpi); free(mpi)
92 | #define MP_gethex(u, hex, res) u = malloc(sizeof(*u)); mpz_init2(u, 1); res = (mpz_set_str(u, hex, 16) == 0)
93 | #define MP_bytes(u) (mpz_sizeinbase(u, 2) + 7) / 8
94 | #define MP_setbin(u,buf,len) nettle_mpz_get_str_256(len,buf,u)
95 | #define MP_getbin(u,buf,len) u = malloc(sizeof(*u)); mpz_init2(u, 1); nettle_mpz_set_str_256_u(u,len,buf)
96 |
97 | typedef struct MDH {
98 | MP_t p;
99 | MP_t g;
100 | MP_t pub_key;
101 | MP_t priv_key;
102 | long length;
103 | } MDH;
104 |
105 | #define MDH_new() calloc(1,sizeof(MDH))
106 | #define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0)
107 |
108 | static int MDH_generate_key(MDH *dh)
109 | {
110 | int num_bytes;
111 | uint32_t seed;
112 | gmp_randstate_t rs;
113 |
114 | num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8 - 1;
115 | if (num_bytes <= 0 || num_bytes > 18000)
116 | return 0;
117 |
118 | dh->priv_key = calloc(1, sizeof(*dh->priv_key));
119 | if (!dh->priv_key)
120 | return 0;
121 | mpz_init2(dh->priv_key, 1);
122 | gnutls_rnd(GNUTLS_RND_RANDOM, &seed, sizeof(seed));
123 | gmp_randinit_mt(rs);
124 | gmp_randseed_ui(rs, seed);
125 | mpz_urandomb(dh->priv_key, rs, num_bytes);
126 | gmp_randclear(rs);
127 |
128 | dh->pub_key = calloc(1, sizeof(*dh->pub_key));
129 | if (!dh->pub_key)
130 | return 0;
131 | mpz_init2(dh->pub_key, 1);
132 | if (!dh->pub_key) {
133 | mpz_clear(dh->priv_key);
134 | free(dh->priv_key);
135 | return 0;
136 | }
137 |
138 | mpz_powm(dh->pub_key, dh->g, dh->priv_key, dh->p);
139 |
140 | return 1;
141 | }
142 |
143 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
144 | {
145 | mpz_ptr k;
146 | int num_bytes;
147 |
148 | num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8;
149 | if (num_bytes <= 0 || num_bytes > 18000)
150 | return -1;
151 |
152 | k = calloc(1, sizeof(*k));
153 | if (!k)
154 | return -1;
155 | mpz_init2(k, 1);
156 |
157 | mpz_powm(k, pub, dh->priv_key, dh->p);
158 | nettle_mpz_get_str_256(len, secret, k);
159 | mpz_clear(k);
160 | free(k);
161 |
162 | /* return the length of the shared secret key like DH_compute_key */
163 | return len;
164 | }
165 |
166 | #else /* USE_OPENSSL */
167 | #include
168 | #include
169 |
170 | typedef BIGNUM * MP_t;
171 | #define MP_new(m) m = BN_new()
172 | #define MP_set_w(mpi, w) BN_set_word(mpi, w)
173 | #define MP_cmp(u, v) BN_cmp(u, v)
174 | #define MP_set(u, v) BN_copy(u, v)
175 | #define MP_sub_w(mpi, w) BN_sub_word(mpi, w)
176 | #define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one())
177 | #define MP_modexp(r, y, q, p) do {BN_CTX *ctx = BN_CTX_new(); BN_mod_exp(r, y, q, p, ctx); BN_CTX_free(ctx);} while(0)
178 | #define MP_free(mpi) BN_free(mpi)
179 | #define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex)
180 | #define MP_bytes(u) BN_num_bytes(u)
181 | #define MP_setbin(u,buf,len) BN_bn2bin(u,buf)
182 | #define MP_getbin(u,buf,len) u = BN_bin2bn(buf,len,0)
183 |
184 | #define MDH DH
185 | #define MDH_new() DH_new()
186 | #define MDH_free(dh) DH_free(dh)
187 | #define MDH_generate_key(dh) DH_generate_key(dh)
188 | #define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh)
189 |
190 | #endif
191 |
192 | #include "log.h"
193 | #include "dhgroups.h"
194 |
195 | /* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */
196 | static int
197 | isValidPublicKey(MP_t y, MP_t p, MP_t q)
198 | {
199 | int ret = TRUE;
200 | MP_t bn;
201 | assert(y);
202 |
203 | MP_new(bn);
204 | assert(bn);
205 |
206 | /* y must lie in [2,p-1] */
207 | MP_set_w(bn, 1);
208 | if (MP_cmp(y, bn) < 0)
209 | {
210 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at least 2");
211 | ret = FALSE;
212 | goto failed;
213 | }
214 |
215 | /* bn = p-2 */
216 | MP_set(bn, p);
217 | MP_sub_w(bn, 1);
218 | if (MP_cmp(y, bn) > 0)
219 | {
220 | RTMP_Log(RTMP_LOGERROR, "DH public key must be at most p-2");
221 | ret = FALSE;
222 | goto failed;
223 | }
224 |
225 | /* Verify with Sophie-Germain prime
226 | *
227 | * This is a nice test to make sure the public key position is calculated
228 | * correctly. This test will fail in about 50% of the cases if applied to
229 | * random data.
230 | */
231 | if (q)
232 | {
233 | /* y must fulfill y^q mod p = 1 */
234 | MP_modexp(bn, y, q, p);
235 |
236 | if (MP_cmp_1(bn) != 0)
237 | {
238 | RTMP_Log(RTMP_LOGWARNING, "DH public key does not fulfill y^q mod p = 1");
239 | }
240 | }
241 |
242 | failed:
243 | MP_free(bn);
244 | return ret;
245 | }
246 |
247 | static MDH *
248 | DHInit(int nKeyBits)
249 | {
250 | size_t res;
251 | MDH *dh = MDH_new();
252 |
253 | if (!dh)
254 | goto failed;
255 |
256 | MP_new(dh->g);
257 |
258 | if (!dh->g)
259 | goto failed;
260 |
261 | MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */
262 | if (!res)
263 | {
264 | goto failed;
265 | }
266 |
267 | MP_set_w(dh->g, 2); /* base 2 */
268 |
269 | dh->length = nKeyBits;
270 | return dh;
271 |
272 | failed:
273 | if (dh)
274 | MDH_free(dh);
275 |
276 | return 0;
277 | }
278 |
279 | static int
280 | DHGenerateKey(MDH *dh)
281 | {
282 | size_t res = 0;
283 | if (!dh)
284 | return 0;
285 |
286 | while (!res)
287 | {
288 | MP_t q1 = NULL;
289 |
290 | if (!MDH_generate_key(dh))
291 | return 0;
292 |
293 | MP_gethex(q1, Q1024, res);
294 | assert(res);
295 |
296 | res = isValidPublicKey(dh->pub_key, dh->p, q1);
297 | if (!res)
298 | {
299 | MP_free(dh->pub_key);
300 | MP_free(dh->priv_key);
301 | dh->pub_key = dh->priv_key = 0;
302 | }
303 |
304 | MP_free(q1);
305 | }
306 | return 1;
307 | }
308 |
309 | /* fill pubkey with the public key in BIG ENDIAN order
310 | * 00 00 00 00 00 x1 x2 x3 .....
311 | */
312 |
313 | static int
314 | DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen)
315 | {
316 | int len;
317 | if (!dh || !dh->pub_key)
318 | return 0;
319 |
320 | len = MP_bytes(dh->pub_key);
321 | if (len <= 0 || len > (int) nPubkeyLen)
322 | return 0;
323 |
324 | memset(pubkey, 0, nPubkeyLen);
325 | MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len);
326 | return 1;
327 | }
328 |
329 | #if 0 /* unused */
330 | static int
331 | DHGetPrivateKey(MDH *dh, uint8_t *privkey, size_t nPrivkeyLen)
332 | {
333 | if (!dh || !dh->priv_key)
334 | return 0;
335 |
336 | int len = MP_bytes(dh->priv_key);
337 | if (len <= 0 || len > (int) nPrivkeyLen)
338 | return 0;
339 |
340 | memset(privkey, 0, nPrivkeyLen);
341 | MP_setbin(dh->priv_key, privkey + (nPrivkeyLen - len), len);
342 | return 1;
343 | }
344 | #endif
345 |
346 | /* computes the shared secret key from the private MDH value and the
347 | * other party's public key (pubkey)
348 | */
349 | static int
350 | DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen,
351 | uint8_t *secret)
352 | {
353 | MP_t q1 = NULL, pubkeyBn = NULL;
354 | size_t len;
355 | int res;
356 |
357 | if (!dh || !secret || nPubkeyLen >= INT_MAX)
358 | return -1;
359 |
360 | MP_getbin(pubkeyBn, pubkey, nPubkeyLen);
361 | if (!pubkeyBn)
362 | return -1;
363 |
364 | MP_gethex(q1, Q1024, len);
365 | assert(len);
366 |
367 | if (isValidPublicKey(pubkeyBn, dh->p, q1))
368 | res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh);
369 | else
370 | res = -1;
371 |
372 | MP_free(q1);
373 | MP_free(pubkeyBn);
374 |
375 | return res;
376 | }
377 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/dhgroups.h:
--------------------------------------------------------------------------------
1 | /* librtmp - Diffie-Hellmann Key Exchange
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | *
4 | * This file is part of librtmp.
5 | *
6 | * librtmp is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as
8 | * published by the Free Software Foundation; either version 2.1,
9 | * or (at your option) any later version.
10 | *
11 | * librtmp is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with librtmp see the file COPYING. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | * http://www.gnu.org/copyleft/lgpl.html
21 | */
22 |
23 | /* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */
24 |
25 | /* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */
26 | #define P768 \
27 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
28 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
29 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
30 | "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
31 |
32 | /* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */
33 | #define P1024 \
34 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
35 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
36 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
37 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
38 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
39 | "FFFFFFFFFFFFFFFF"
40 |
41 | /* Group morder largest prime factor: */
42 | #define Q1024 \
43 | "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
44 | "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
45 | "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
46 | "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
47 | "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
48 | "FFFFFFFFFFFFFFFF"
49 |
50 | /* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */
51 | #define P1536 \
52 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
53 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
54 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
55 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
56 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
57 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
58 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
59 | "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
60 |
61 | /* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */
62 | #define P2048 \
63 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
64 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
65 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
66 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
67 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
68 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
69 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
70 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
71 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
72 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
73 | "15728E5A8AACAA68FFFFFFFFFFFFFFFF"
74 |
75 | /* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */
76 | #define P3072 \
77 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
78 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
79 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
80 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
81 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
82 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
83 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
84 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
85 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
86 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
87 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
88 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
89 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
90 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
91 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
92 | "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
93 |
94 | /* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */
95 | #define P4096 \
96 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
97 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
98 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
99 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
100 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
101 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
102 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
103 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
104 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
105 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
106 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
107 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
108 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
109 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
110 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
111 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
112 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
113 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
114 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
115 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
116 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \
117 | "FFFFFFFFFFFFFFFF"
118 |
119 | /* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */
120 | #define P6144 \
121 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
122 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
123 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
124 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
125 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
126 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
127 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
128 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
129 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
130 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
131 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
132 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
133 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
134 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
135 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
136 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
137 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
138 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
139 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
140 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
141 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
142 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
143 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
144 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
145 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
146 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
147 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
148 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
149 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
150 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
151 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
152 | "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
153 |
154 | /* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */
155 | #define P8192 \
156 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
157 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
158 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
159 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
160 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
161 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
162 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
163 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
164 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
165 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
166 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
167 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
168 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
169 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
170 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
171 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
172 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
173 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
174 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
175 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
176 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
177 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
178 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
179 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
180 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
181 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
182 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
183 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
184 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
185 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
186 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
187 | "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \
188 | "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \
189 | "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \
190 | "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \
191 | "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \
192 | "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \
193 | "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \
194 | "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \
195 | "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \
196 | "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \
197 | "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \
198 | "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"
199 |
200 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/hashswf.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009-2010 Howard Chu
3 | *
4 | * This file is part of librtmp.
5 | *
6 | * librtmp is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as
8 | * published by the Free Software Foundation; either version 2.1,
9 | * or (at your option) any later version.
10 | *
11 | * librtmp is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with librtmp see the file COPYING. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | * http://www.gnu.org/copyleft/lgpl.html
21 | */
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include "rtmp_sys.h"
30 | #include "log.h"
31 | #include "http.h"
32 |
33 | #ifdef CRYPTO
34 | #ifdef USE_POLARSSL
35 | #include
36 | #ifndef SHA256_DIGEST_LENGTH
37 | #define SHA256_DIGEST_LENGTH 32
38 | #endif
39 | #define HMAC_CTX sha2_context
40 | #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0)
41 | #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len)
42 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
43 | #define HMAC_close(ctx)
44 | #elif defined(USE_GNUTLS)
45 | #include
46 | #ifndef SHA256_DIGEST_LENGTH
47 | #define SHA256_DIGEST_LENGTH 32
48 | #endif
49 | #undef HMAC_CTX
50 | #define HMAC_CTX struct hmac_sha256_ctx
51 | #define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key)
52 | #define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf)
53 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
54 | #define HMAC_close(ctx)
55 | #else /* USE_OPENSSL */
56 | #include
57 | #include
58 | #include
59 | #include
60 | #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
61 | #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len)
62 | #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
63 | #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx)
64 | #endif
65 |
66 | extern void RTMP_TLS_Init();
67 | extern TLS_CTX RTMP_TLS_ctx;
68 |
69 | #include
70 |
71 | #endif /* CRYPTO */
72 |
73 | #define AGENT "Mozilla/5.0"
74 |
75 | HTTPResult
76 | HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
77 | {
78 | char *host, *path;
79 | char *p1, *p2;
80 | char hbuf[256];
81 | int port = 80;
82 | #ifdef CRYPTO
83 | int ssl = 0;
84 | #endif
85 | int hlen, flen = 0;
86 | int rc, i;
87 | int len_known;
88 | HTTPResult ret = HTTPRES_OK;
89 | struct sockaddr_in sa;
90 | RTMPSockBuf sb = {0};
91 |
92 | http->status = -1;
93 |
94 | memset(&sa, 0, sizeof(struct sockaddr_in));
95 | sa.sin_family = AF_INET;
96 |
97 | /* we only handle http here */
98 | if (strncasecmp(url, "http", 4))
99 | return HTTPRES_BAD_REQUEST;
100 |
101 | if (url[4] == 's')
102 | {
103 | #ifdef CRYPTO
104 | ssl = 1;
105 | port = 443;
106 | if (!RTMP_TLS_ctx)
107 | RTMP_TLS_Init();
108 | #else
109 | return HTTPRES_BAD_REQUEST;
110 | #endif
111 | }
112 |
113 | p1 = strchr(url + 4, ':');
114 | if (!p1 || strncmp(p1, "://", 3))
115 | return HTTPRES_BAD_REQUEST;
116 |
117 | host = p1 + 3;
118 | path = strchr(host, '/');
119 | hlen = path - host;
120 | strncpy(hbuf, host, hlen);
121 | hbuf[hlen] = '\0';
122 | host = hbuf;
123 | p1 = strrchr(host, ':');
124 | if (p1)
125 | {
126 | *p1++ = '\0';
127 | port = atoi(p1);
128 | }
129 |
130 | sa.sin_addr.s_addr = inet_addr(host);
131 | if (sa.sin_addr.s_addr == INADDR_NONE)
132 | {
133 | struct hostent *hp = gethostbyname(host);
134 | if (!hp || !hp->h_addr)
135 | return HTTPRES_LOST_CONNECTION;
136 | sa.sin_addr = *(struct in_addr *)hp->h_addr;
137 | }
138 | sa.sin_port = htons(port);
139 | sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
140 | if (sb.sb_socket == -1)
141 | return HTTPRES_LOST_CONNECTION;
142 | i =
143 | sprintf(sb.sb_buf,
144 | "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
145 | path, AGENT, host, (int)(path - url + 1), url);
146 | if (http->date[0])
147 | i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
148 | i += sprintf(sb.sb_buf + i, "\r\n");
149 |
150 | if (connect
151 | (sb.sb_socket, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
152 | {
153 | ret = HTTPRES_LOST_CONNECTION;
154 | goto leave;
155 | }
156 | #ifdef CRYPTO
157 | if (ssl)
158 | {
159 | #ifdef NO_SSL
160 | RTMP_Log(RTMP_LOGERROR, "%s, No SSL/TLS support", __FUNCTION__);
161 | ret = HTTPRES_BAD_REQUEST;
162 | goto leave;
163 | #else
164 | TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
165 | TLS_setfd(sb.sb_ssl, sb.sb_socket);
166 | if (TLS_connect(sb.sb_ssl) < 0)
167 | {
168 | RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
169 | ret = HTTPRES_LOST_CONNECTION;
170 | goto leave;
171 | }
172 | #endif
173 | }
174 | #endif
175 | RTMPSockBuf_Send(&sb, sb.sb_buf, i);
176 |
177 | /* set timeout */
178 | #define HTTP_TIMEOUT 5
179 | {
180 | SET_RCVTIMEO(tv, HTTP_TIMEOUT);
181 | if (setsockopt
182 | (sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
183 | {
184 | RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
185 | __FUNCTION__, HTTP_TIMEOUT);
186 | }
187 | }
188 |
189 | sb.sb_size = 0;
190 | sb.sb_timedout = FALSE;
191 | if (RTMPSockBuf_Fill(&sb) < 1)
192 | {
193 | ret = HTTPRES_LOST_CONNECTION;
194 | goto leave;
195 | }
196 | if (strncmp(sb.sb_buf, "HTTP/1", 6))
197 | {
198 | ret = HTTPRES_BAD_REQUEST;
199 | goto leave;
200 | }
201 |
202 | p1 = strchr(sb.sb_buf, ' ');
203 | rc = atoi(p1 + 1);
204 | http->status = rc;
205 |
206 | if (rc >= 300)
207 | {
208 | if (rc == 304)
209 | {
210 | ret = HTTPRES_OK_NOT_MODIFIED;
211 | goto leave;
212 | }
213 | else if (rc == 404)
214 | ret = HTTPRES_NOT_FOUND;
215 | else if (rc >= 500)
216 | ret = HTTPRES_SERVER_ERROR;
217 | else if (rc >= 400)
218 | ret = HTTPRES_BAD_REQUEST;
219 | else
220 | ret = HTTPRES_REDIRECTED;
221 | }
222 |
223 | p1 = memchr(sb.sb_buf, '\n', sb.sb_size);
224 | if (!p1)
225 | {
226 | ret = HTTPRES_BAD_REQUEST;
227 | goto leave;
228 | }
229 | sb.sb_start = p1 + 1;
230 | sb.sb_size -= sb.sb_start - sb.sb_buf;
231 |
232 | while ((p2 = memchr(sb.sb_start, '\r', sb.sb_size)))
233 | {
234 | if (*sb.sb_start == '\r')
235 | {
236 | sb.sb_start += 2;
237 | sb.sb_size -= 2;
238 | break;
239 | }
240 | else
241 | if (!strncasecmp
242 | (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
243 | {
244 | flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
245 | }
246 | else
247 | if (!strncasecmp
248 | (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
249 | {
250 | *p2 = '\0';
251 | strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
252 | }
253 | p2 += 2;
254 | sb.sb_size -= p2 - sb.sb_start;
255 | sb.sb_start = p2;
256 | if (sb.sb_size < 1)
257 | {
258 | if (RTMPSockBuf_Fill(&sb) < 1)
259 | {
260 | ret = HTTPRES_LOST_CONNECTION;
261 | goto leave;
262 | }
263 | }
264 | }
265 |
266 | len_known = flen > 0;
267 | while ((!len_known || flen > 0) &&
268 | (sb.sb_size > 0 || RTMPSockBuf_Fill(&sb) > 0))
269 | {
270 | cb(sb.sb_start, 1, sb.sb_size, http->data);
271 | if (len_known)
272 | flen -= sb.sb_size;
273 | http->size += sb.sb_size;
274 | sb.sb_size = 0;
275 | }
276 |
277 | if (flen > 0)
278 | ret = HTTPRES_LOST_CONNECTION;
279 |
280 | leave:
281 | RTMPSockBuf_Close(&sb);
282 | return ret;
283 | }
284 |
285 | #ifdef CRYPTO
286 |
287 | #define CHUNK 16384
288 |
289 | struct info
290 | {
291 | z_stream *zs;
292 | HMAC_CTX ctx;
293 | int first;
294 | int zlib;
295 | int size;
296 | };
297 |
298 | static size_t
299 | swfcrunch(void *ptr, size_t size, size_t nmemb, void *stream)
300 | {
301 | struct info *i = stream;
302 | char *p = ptr;
303 | size_t len = size * nmemb;
304 |
305 | if (i->first)
306 | {
307 | i->first = 0;
308 | /* compressed? */
309 | if (!strncmp(p, "CWS", 3))
310 | {
311 | *p = 'F';
312 | i->zlib = 1;
313 | }
314 | HMAC_crunch(i->ctx, (unsigned char *)p, 8);
315 | p += 8;
316 | len -= 8;
317 | i->size = 8;
318 | }
319 |
320 | if (i->zlib)
321 | {
322 | unsigned char out[CHUNK];
323 | i->zs->next_in = (unsigned char *)p;
324 | i->zs->avail_in = len;
325 | do
326 | {
327 | i->zs->avail_out = CHUNK;
328 | i->zs->next_out = out;
329 | inflate(i->zs, Z_NO_FLUSH);
330 | len = CHUNK - i->zs->avail_out;
331 | i->size += len;
332 | HMAC_crunch(i->ctx, out, len);
333 | }
334 | while (i->zs->avail_out == 0);
335 | }
336 | else
337 | {
338 | i->size += len;
339 | HMAC_crunch(i->ctx, (unsigned char *)p, len);
340 | }
341 | return size * nmemb;
342 | }
343 |
344 | static int tzoff;
345 | static int tzchecked;
346 |
347 | #define JAN02_1980 318340800
348 |
349 | static const char *monthtab[12] = { "Jan", "Feb", "Mar",
350 | "Apr", "May", "Jun",
351 | "Jul", "Aug", "Sep",
352 | "Oct", "Nov", "Dec"
353 | };
354 | static const char *days[] =
355 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
356 |
357 | /* Parse an HTTP datestamp into Unix time */
358 | static time_t
359 | make_unix_time(char *s)
360 | {
361 | struct tm time;
362 | int i, ysub = 1900, fmt = 0;
363 | char *month;
364 | char *n;
365 | time_t res;
366 |
367 | if (s[3] != ' ')
368 | {
369 | fmt = 1;
370 | if (s[3] != ',')
371 | ysub = 0;
372 | }
373 | for (n = s; *n; ++n)
374 | if (*n == '-' || *n == ':')
375 | *n = ' ';
376 |
377 | time.tm_mon = 0;
378 | n = strchr(s, ' ');
379 | if (fmt)
380 | {
381 | /* Day, DD-MMM-YYYY HH:MM:SS GMT */
382 | time.tm_mday = strtol(n + 1, &n, 0);
383 | month = n + 1;
384 | n = strchr(month, ' ');
385 | time.tm_year = strtol(n + 1, &n, 0);
386 | time.tm_hour = strtol(n + 1, &n, 0);
387 | time.tm_min = strtol(n + 1, &n, 0);
388 | time.tm_sec = strtol(n + 1, NULL, 0);
389 | }
390 | else
391 | {
392 | /* Unix ctime() format. Does not conform to HTTP spec. */
393 | /* Day MMM DD HH:MM:SS YYYY */
394 | month = n + 1;
395 | n = strchr(month, ' ');
396 | while (isspace(*n))
397 | n++;
398 | time.tm_mday = strtol(n, &n, 0);
399 | time.tm_hour = strtol(n + 1, &n, 0);
400 | time.tm_min = strtol(n + 1, &n, 0);
401 | time.tm_sec = strtol(n + 1, &n, 0);
402 | time.tm_year = strtol(n + 1, NULL, 0);
403 | }
404 | if (time.tm_year > 100)
405 | time.tm_year -= ysub;
406 |
407 | for (i = 0; i < 12; i++)
408 | if (!strncasecmp(month, monthtab[i], 3))
409 | {
410 | time.tm_mon = i;
411 | break;
412 | }
413 | time.tm_isdst = 0; /* daylight saving is never in effect in GMT */
414 |
415 | /* this is normally the value of extern int timezone, but some
416 | * braindead C libraries don't provide it.
417 | */
418 | if (!tzchecked)
419 | {
420 | struct tm *tc;
421 | time_t then = JAN02_1980;
422 | tc = localtime(&then);
423 | tzoff = (12 - tc->tm_hour) * 3600 + tc->tm_min * 60 + tc->tm_sec;
424 | tzchecked = 1;
425 | }
426 | res = mktime(&time);
427 | /* Unfortunately, mktime() assumes the input is in local time,
428 | * not GMT, so we have to correct it here.
429 | */
430 | if (res != -1)
431 | res += tzoff;
432 | return res;
433 | }
434 |
435 | /* Convert a Unix time to a network time string
436 | * Weekday, DD-MMM-YYYY HH:MM:SS GMT
437 | */
438 | static void
439 | strtime(time_t * t, char *s)
440 | {
441 | struct tm *tm;
442 |
443 | tm = gmtime((time_t *) t);
444 | sprintf(s, "%s, %02d %s %d %02d:%02d:%02d GMT",
445 | days[tm->tm_wday], tm->tm_mday, monthtab[tm->tm_mon],
446 | tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
447 | }
448 |
449 | #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
450 |
451 | int
452 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
453 | int age)
454 | {
455 | FILE *f = NULL;
456 | char *path, date[64], cctim[64];
457 | long pos = 0;
458 | time_t ctim = -1, cnow;
459 | int i, got = 0, ret = 0;
460 | unsigned int hlen;
461 | struct info in = { 0 };
462 | struct HTTP_ctx http = { 0 };
463 | HTTPResult httpres;
464 | z_stream zs = { 0 };
465 | AVal home, hpre;
466 |
467 | date[0] = '\0';
468 | #ifdef _WIN32
469 | #ifdef XBMC4XBOX
470 | hpre.av_val = "Q:";
471 | hpre.av_len = 2;
472 | home.av_val = "\\UserData";
473 | #else
474 | hpre.av_val = getenv("HOMEDRIVE");
475 | hpre.av_len = strlen(hpre.av_val);
476 | home.av_val = getenv("HOMEPATH");
477 | #endif
478 | #define DIRSEP "\\"
479 |
480 | #else /* !_WIN32 */
481 | hpre.av_val = "";
482 | hpre.av_len = 0;
483 | home.av_val = getenv("HOME");
484 | #define DIRSEP "/"
485 | #endif
486 | if (!home.av_val)
487 | home.av_val = ".";
488 | home.av_len = strlen(home.av_val);
489 |
490 | /* SWF hash info is cached in a fixed-format file.
491 | * url:
492 | * ctim: HTTP datestamp of when we last checked it.
493 | * date: HTTP datestamp of the SWF's last modification.
494 | * size: SWF size in hex
495 | * hash: SWF hash in hex
496 | *
497 | * These fields must be present in this order. All fields
498 | * besides URL are fixed size.
499 | */
500 | path = malloc(hpre.av_len + home.av_len + sizeof(DIRSEP ".swfinfo"));
501 | sprintf(path, "%s%s" DIRSEP ".swfinfo", hpre.av_val, home.av_val);
502 |
503 | f = fopen(path, "r+");
504 | while (f)
505 | {
506 | char buf[4096], *file, *p;
507 |
508 | file = strchr(url, '/');
509 | if (!file)
510 | break;
511 | file += 2;
512 | file = strchr(file, '/');
513 | if (!file)
514 | break;
515 | file++;
516 | hlen = file - url;
517 | p = strrchr(file, '/');
518 | if (p)
519 | file = p;
520 | else
521 | file--;
522 |
523 | while (fgets(buf, sizeof(buf), f))
524 | {
525 | char *r1;
526 |
527 | got = 0;
528 |
529 | if (strncmp(buf, "url: ", 5))
530 | continue;
531 | if (strncmp(buf + 5, url, hlen))
532 | continue;
533 | r1 = strrchr(buf, '/');
534 | i = strlen(r1);
535 | r1[--i] = '\0';
536 | if (strncmp(r1, file, i))
537 | continue;
538 | pos = ftell(f);
539 | while (got < 4 && fgets(buf, sizeof(buf), f))
540 | {
541 | if (!strncmp(buf, "size: ", 6))
542 | {
543 | *size = strtol(buf + 6, NULL, 16);
544 | got++;
545 | }
546 | else if (!strncmp(buf, "hash: ", 6))
547 | {
548 | unsigned char *ptr = hash, *in = (unsigned char *)buf + 6;
549 | int l = strlen((char *)in) - 1;
550 | for (i = 0; i < l; i += 2)
551 | *ptr++ = (HEX2BIN(in[i]) << 4) | HEX2BIN(in[i + 1]);
552 | got++;
553 | }
554 | else if (!strncmp(buf, "date: ", 6))
555 | {
556 | buf[strlen(buf) - 1] = '\0';
557 | strncpy(date, buf + 6, sizeof(date));
558 | got++;
559 | }
560 | else if (!strncmp(buf, "ctim: ", 6))
561 | {
562 | buf[strlen(buf) - 1] = '\0';
563 | ctim = make_unix_time(buf + 6);
564 | got++;
565 | }
566 | else if (!strncmp(buf, "url: ", 5))
567 | break;
568 | }
569 | break;
570 | }
571 | break;
572 | }
573 |
574 | cnow = time(NULL);
575 | /* If we got a cache time, see if it's young enough to use directly */
576 | if (age && ctim > 0)
577 | {
578 | ctim = cnow - ctim;
579 | ctim /= 3600 * 24; /* seconds to days */
580 | if (ctim < age) /* ok, it's new enough */
581 | goto out;
582 | }
583 |
584 | in.first = 1;
585 | HMAC_setup(in.ctx, "Genuine Adobe Flash Player 001", 30);
586 | inflateInit(&zs);
587 | in.zs = &zs;
588 |
589 | http.date = date;
590 | http.data = ∈
591 |
592 | httpres = HTTP_get(&http, url, swfcrunch);
593 |
594 | inflateEnd(&zs);
595 |
596 | if (httpres != HTTPRES_OK && httpres != HTTPRES_OK_NOT_MODIFIED)
597 | {
598 | ret = -1;
599 | if (httpres == HTTPRES_LOST_CONNECTION)
600 | RTMP_Log(RTMP_LOGERROR, "%s: connection lost while downloading swfurl %s",
601 | __FUNCTION__, url);
602 | else if (httpres == HTTPRES_NOT_FOUND)
603 | RTMP_Log(RTMP_LOGERROR, "%s: swfurl %s not found", __FUNCTION__, url);
604 | else
605 | RTMP_Log(RTMP_LOGERROR, "%s: couldn't contact swfurl %s (HTTP error %d)",
606 | __FUNCTION__, url, http.status);
607 | }
608 | else
609 | {
610 | if (got && pos)
611 | fseek(f, pos, SEEK_SET);
612 | else
613 | {
614 | char *q;
615 | if (!f)
616 | f = fopen(path, "w");
617 | if (!f)
618 | {
619 | int err = errno;
620 | RTMP_Log(RTMP_LOGERROR,
621 | "%s: couldn't open %s for writing, errno %d (%s)",
622 | __FUNCTION__, path, err, strerror(err));
623 | ret = -1;
624 | goto out;
625 | }
626 | fseek(f, 0, SEEK_END);
627 | q = strchr(url, '?');
628 | if (q)
629 | i = q - url;
630 | else
631 | i = strlen(url);
632 |
633 | fprintf(f, "url: %.*s\n", i, url);
634 | }
635 | strtime(&cnow, cctim);
636 | fprintf(f, "ctim: %s\n", cctim);
637 |
638 | if (!in.first)
639 | {
640 | HMAC_finish(in.ctx, hash, hlen);
641 | *size = in.size;
642 |
643 | fprintf(f, "date: %s\n", date);
644 | fprintf(f, "size: %08x\n", in.size);
645 | fprintf(f, "hash: ");
646 | for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
647 | fprintf(f, "%02x", hash[i]);
648 | fprintf(f, "\n");
649 | }
650 | }
651 | HMAC_close(in.ctx);
652 | out:
653 | free(path);
654 | if (f)
655 | fclose(f);
656 | return ret;
657 | }
658 | #else
659 | int
660 | RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
661 | int age)
662 | {
663 | return -1;
664 | }
665 | #endif
666 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/http.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_HTTP_H__
2 | #define __RTMP_HTTP_H__
3 | /*
4 | * Copyright (C) 2010 Howard Chu
5 | * Copyright (C) 2010 Antti Ajanki
6 | *
7 | * This file is part of librtmp.
8 | *
9 | * librtmp is free software; you can redistribute it and/or modify
10 | * it under the terms of the GNU Lesser General Public License as
11 | * published by the Free Software Foundation; either version 2.1,
12 | * or (at your option) any later version.
13 | *
14 | * librtmp is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU Lesser General Public License
20 | * along with librtmp see the file COPYING. If not, write to
21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 | * Boston, MA 02110-1301, USA.
23 | * http://www.gnu.org/copyleft/lgpl.html
24 | */
25 |
26 | typedef enum {
27 | HTTPRES_OK, /* result OK */
28 | HTTPRES_OK_NOT_MODIFIED, /* not modified since last request */
29 | HTTPRES_NOT_FOUND, /* not found */
30 | HTTPRES_BAD_REQUEST, /* client error */
31 | HTTPRES_SERVER_ERROR, /* server reported an error */
32 | HTTPRES_REDIRECTED, /* resource has been moved */
33 | HTTPRES_LOST_CONNECTION /* connection lost while waiting for data */
34 | } HTTPResult;
35 |
36 | struct HTTP_ctx {
37 | char *date;
38 | int size;
39 | int status;
40 | void *data;
41 | };
42 |
43 | typedef size_t (HTTP_read_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
44 |
45 | HTTPResult HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb);
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/log.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008-2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "log.h"
32 |
33 | #define MAX_PRINT_LEN 2048
34 |
35 | RTMP_LogLevel RTMP_debuglevel = RTMP_LOGERROR;
36 |
37 | static int neednl;
38 |
39 | static FILE *fmsg;
40 |
41 | static RTMP_LogCallback rtmp_log_default, *cb = rtmp_log_default;
42 |
43 | static const char *levels[] = {
44 | "CRIT", "ERROR", "WARNING", "INFO",
45 | "DEBUG", "DEBUG2"
46 | };
47 |
48 | static void rtmp_log_default(int level, const char *format, va_list vl)
49 | {
50 | char str[MAX_PRINT_LEN]="";
51 |
52 | vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
53 |
54 | /* Filter out 'no-name' */
55 | if ( RTMP_debuglevel RTMP_debuglevel )
97 | return;
98 |
99 | va_start(args, format);
100 | cb(level, format, args);
101 | va_end(args);
102 | }
103 |
104 | static const char hexdig[] = "0123456789abcdef";
105 |
106 | void RTMP_LogHex(int level, const uint8_t *data, unsigned long len)
107 | {
108 | unsigned long i;
109 | char line[50], *ptr;
110 |
111 | if ( level > RTMP_debuglevel )
112 | return;
113 |
114 | ptr = line;
115 |
116 | for(i=0; i> 4)];
118 | *ptr++ = hexdig[0x0f & data[i]];
119 | if ((i & 0x0f) == 0x0f) {
120 | *ptr = '\0';
121 | ptr = line;
122 | RTMP_Log(level, "%s", line);
123 | } else {
124 | *ptr++ = ' ';
125 | }
126 | }
127 | if (i & 0x0f) {
128 | *ptr = '\0';
129 | RTMP_Log(level, "%s", line);
130 | }
131 | }
132 |
133 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len)
134 | {
135 | #define BP_OFFSET 9
136 | #define BP_GRAPH 60
137 | #define BP_LEN 80
138 | char line[BP_LEN];
139 | unsigned long i;
140 |
141 | if ( !data || level > RTMP_debuglevel )
142 | return;
143 |
144 | /* in case len is zero */
145 | line[0] = '\0';
146 |
147 | for ( i = 0 ; i < len ; i++ ) {
148 | int n = i % 16;
149 | unsigned off;
150 |
151 | if( !n ) {
152 | if( i ) RTMP_Log( level, "%s", line );
153 | memset( line, ' ', sizeof(line)-2 );
154 | line[sizeof(line)-2] = '\0';
155 |
156 | off = i % 0x0ffffU;
157 |
158 | line[2] = hexdig[0x0f & (off >> 12)];
159 | line[3] = hexdig[0x0f & (off >> 8)];
160 | line[4] = hexdig[0x0f & (off >> 4)];
161 | line[5] = hexdig[0x0f & off];
162 | line[6] = ':';
163 | }
164 |
165 | off = BP_OFFSET + n*3 + ((n >= 8)?1:0);
166 | line[off] = hexdig[0x0f & ( data[i] >> 4 )];
167 | line[off+1] = hexdig[0x0f & data[i]];
168 |
169 | off = BP_GRAPH + n + ((n >= 8)?1:0);
170 |
171 | if ( isprint( data[i] )) {
172 | line[BP_GRAPH + n] = data[i];
173 | } else {
174 | line[BP_GRAPH + n] = '.';
175 | }
176 | }
177 |
178 | RTMP_Log( level, "%s", line );
179 | }
180 |
181 | /* These should only be used by apps, never by the library itself */
182 | void RTMP_LogPrintf(const char *format, ...)
183 | {
184 | char str[MAX_PRINT_LEN]="";
185 | int len;
186 | va_list args;
187 | va_start(args, format);
188 | len = vsnprintf(str, MAX_PRINT_LEN-1, format, args);
189 | va_end(args);
190 |
191 | if ( RTMP_debuglevel==RTMP_LOGCRIT )
192 | return;
193 |
194 | if ( !fmsg ) fmsg = stderr;
195 |
196 | if (neednl) {
197 | putc('\n', fmsg);
198 | neednl = 0;
199 | }
200 |
201 | if (len > MAX_PRINT_LEN-1)
202 | len = MAX_PRINT_LEN-1;
203 | fprintf(fmsg, "%s", str);
204 | if (str[len-1] == '\n')
205 | fflush(fmsg);
206 | }
207 |
208 | void RTMP_LogStatus(const char *format, ...)
209 | {
210 | char str[MAX_PRINT_LEN]="";
211 | va_list args;
212 | va_start(args, format);
213 | vsnprintf(str, MAX_PRINT_LEN-1, format, args);
214 | va_end(args);
215 |
216 | if ( RTMP_debuglevel==RTMP_LOGCRIT )
217 | return;
218 |
219 | if ( !fmsg ) fmsg = stderr;
220 |
221 | fprintf(fmsg, "%s", str);
222 | fflush(fmsg);
223 | neednl = 1;
224 | }
225 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/log.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2008-2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #ifndef __RTMP_LOG_H__
25 | #define __RTMP_LOG_H__
26 |
27 | #include
28 | #include
29 | #include
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 | /* Enable this to get full debugging output */
35 | /* #define _DEBUG */
36 |
37 | #ifdef _DEBUG
38 | #undef NODEBUG
39 | #endif
40 |
41 | typedef enum
42 | { RTMP_LOGCRIT=0, RTMP_LOGERROR, RTMP_LOGWARNING, RTMP_LOGINFO,
43 | RTMP_LOGDEBUG, RTMP_LOGDEBUG2, RTMP_LOGALL
44 | } RTMP_LogLevel;
45 |
46 | extern RTMP_LogLevel RTMP_debuglevel;
47 |
48 | typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list);
49 | void RTMP_LogSetCallback(RTMP_LogCallback *cb);
50 | void RTMP_LogSetOutput(FILE *file);
51 | #ifdef __GNUC__
52 | void RTMP_LogPrintf(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
53 | void RTMP_LogStatus(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
54 | void RTMP_Log(int level, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
55 | #else
56 | void RTMP_LogPrintf(const char *format, ...);
57 | void RTMP_LogStatus(const char *format, ...);
58 | void RTMP_Log(int level, const char *format, ...);
59 | #endif
60 | void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
61 | void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
62 | void RTMP_LogSetLevel(RTMP_LogLevel lvl);
63 | RTMP_LogLevel RTMP_LogGetLevel(void);
64 |
65 | #ifdef __cplusplus
66 | }
67 | #endif
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/parseurl.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Andrej Stepanchuk
3 | * Copyright (C) 2009-2010 Howard Chu
4 | *
5 | * This file is part of librtmp.
6 | *
7 | * librtmp is free software; you can redistribute it and/or modify
8 | * it under the terms of the GNU Lesser General Public License as
9 | * published by the Free Software Foundation; either version 2.1,
10 | * or (at your option) any later version.
11 | *
12 | * librtmp is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with librtmp see the file COPYING. If not, write to
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 | * Boston, MA 02110-1301, USA.
21 | * http://www.gnu.org/copyleft/lgpl.html
22 | */
23 |
24 | #include
25 | #include
26 |
27 | #include
28 | #include
29 |
30 | #include "rtmp_sys.h"
31 | #include "log.h"
32 |
33 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
34 | AVal *playpath, AVal *app)
35 | {
36 | char *p, *end, *col, *ques, *slash;
37 |
38 | RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
39 |
40 | *protocol = RTMP_PROTOCOL_RTMP;
41 | *port = 0;
42 | playpath->av_len = 0;
43 | playpath->av_val = NULL;
44 | app->av_len = 0;
45 | app->av_val = NULL;
46 |
47 | /* Old School Parsing */
48 |
49 | /* look for usual :// pattern */
50 | p = strstr(url, "://");
51 | if(!p) {
52 | RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
53 | return FALSE;
54 | }
55 | {
56 | int len = (int)(p-url);
57 |
58 | if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
59 | *protocol = RTMP_PROTOCOL_RTMP;
60 | else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
61 | *protocol = RTMP_PROTOCOL_RTMPT;
62 | else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
63 | *protocol = RTMP_PROTOCOL_RTMPS;
64 | else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
65 | *protocol = RTMP_PROTOCOL_RTMPE;
66 | else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
67 | *protocol = RTMP_PROTOCOL_RTMFP;
68 | else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
69 | *protocol = RTMP_PROTOCOL_RTMPTE;
70 | else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
71 | *protocol = RTMP_PROTOCOL_RTMPTS;
72 | else {
73 | RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
74 | goto parsehost;
75 | }
76 | }
77 |
78 | RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
79 |
80 | parsehost:
81 | /* let's get the hostname */
82 | p+=3;
83 |
84 | /* check for sudden death */
85 | if(*p==0) {
86 | RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
87 | return FALSE;
88 | }
89 |
90 | end = p + strlen(p);
91 | col = strchr(p, ':');
92 | ques = strchr(p, '?');
93 | slash = strchr(p, '/');
94 |
95 | {
96 | int hostlen;
97 | if(slash)
98 | hostlen = slash - p;
99 | else
100 | hostlen = end - p;
101 | if(col && col -p < hostlen)
102 | hostlen = col - p;
103 |
104 | if(hostlen < 256) {
105 | host->av_val = p;
106 | host->av_len = hostlen;
107 | RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host->av_val);
108 | } else {
109 | RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
110 | }
111 |
112 | p+=hostlen;
113 | }
114 |
115 | /* get the port number if available */
116 | if(*p == ':') {
117 | unsigned int p2;
118 | p++;
119 | p2 = atoi(p);
120 | if(p2 > 65535) {
121 | RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
122 | } else {
123 | *port = p2;
124 | }
125 | }
126 |
127 | if(!slash) {
128 | RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
129 | return TRUE;
130 | }
131 | p = slash+1;
132 |
133 | {
134 | /* parse application
135 | *
136 | * rtmp://host[:port]/app[/appinstance][/...]
137 | * application = app[/appinstance]
138 | */
139 |
140 | char *slash2, *slash3 = NULL, *slash4 = NULL;
141 | int applen, appnamelen;
142 |
143 | slash2 = strchr(p, '/');
144 | if(slash2)
145 | slash3 = strchr(slash2+1, '/');
146 | if(slash3)
147 | slash4 = strchr(slash3+1, '/');
148 |
149 | applen = end-p; /* ondemand, pass all parameters as app */
150 | appnamelen = applen; /* ondemand length */
151 |
152 | if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
153 | appnamelen = ques-p;
154 | }
155 | else if(strncmp(p, "ondemand/", 9)==0) {
156 | /* app = ondemand/foobar, only pass app=ondemand */
157 | applen = 8;
158 | appnamelen = 8;
159 | }
160 | else { /* app!=ondemand, so app is app[/appinstance] */
161 | if(slash4)
162 | appnamelen = slash4-p;
163 | else if(slash3)
164 | appnamelen = slash3-p;
165 | else if(slash2)
166 | appnamelen = slash2-p;
167 |
168 | applen = appnamelen;
169 | }
170 |
171 | app->av_val = p;
172 | app->av_len = applen;
173 | RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
174 |
175 | p += appnamelen;
176 | }
177 |
178 | if (*p == '/')
179 | p++;
180 |
181 | if (end-p) {
182 | AVal av = {p, end-p};
183 | RTMP_ParsePlaypath(&av, playpath);
184 | }
185 |
186 | return TRUE;
187 | }
188 |
189 | /*
190 | * Extracts playpath from RTMP URL. playpath is the file part of the
191 | * URL, i.e. the part that comes after rtmp://host:port/app/
192 | *
193 | * Returns the stream name in a format understood by FMS. The name is
194 | * the playpath part of the URL with formatting depending on the stream
195 | * type:
196 | *
197 | * mp4 streams: prepend "mp4:", remove extension
198 | * mp3 streams: prepend "mp3:", remove extension
199 | * flv streams: remove extension
200 | */
201 | void RTMP_ParsePlaypath(AVal *in, AVal *out) {
202 | int addMP4 = 0;
203 | int addMP3 = 0;
204 | int subExt = 0;
205 | const char *playpath = in->av_val;
206 | const char *temp, *q, *ext = NULL;
207 | const char *ppstart = playpath;
208 | char *streamname, *destptr, *p;
209 |
210 | int pplen = in->av_len;
211 |
212 | out->av_val = NULL;
213 | out->av_len = 0;
214 |
215 | if ((*ppstart == '?') &&
216 | (temp=strstr(ppstart, "slist=")) != 0) {
217 | ppstart = temp+6;
218 | pplen = strlen(ppstart);
219 |
220 | temp = strchr(ppstart, '&');
221 | if (temp) {
222 | pplen = temp-ppstart;
223 | }
224 | }
225 |
226 | q = strchr(ppstart, '?');
227 | if (pplen >= 4) {
228 | if (q)
229 | ext = q-4;
230 | else
231 | ext = &ppstart[pplen-4];
232 | if ((strncmp(ext, ".f4v", 4) == 0) ||
233 | (strncmp(ext, ".mp4", 4) == 0)) {
234 | addMP4 = 1;
235 | subExt = 1;
236 | /* Only remove .flv from rtmp URL, not slist params */
237 | } else if ((ppstart == playpath) &&
238 | (strncmp(ext, ".flv", 4) == 0)) {
239 | subExt = 1;
240 | } else if (strncmp(ext, ".mp3", 4) == 0) {
241 | addMP3 = 1;
242 | subExt = 1;
243 | }
244 | }
245 |
246 | streamname = (char *)malloc((pplen+4+1)*sizeof(char));
247 | if (!streamname)
248 | return;
249 |
250 | destptr = streamname;
251 | if (addMP4) {
252 | if (strncmp(ppstart, "mp4:", 4)) {
253 | strcpy(destptr, "mp4:");
254 | destptr += 4;
255 | } else {
256 | subExt = 0;
257 | }
258 | } else if (addMP3) {
259 | if (strncmp(ppstart, "mp3:", 4)) {
260 | strcpy(destptr, "mp3:");
261 | destptr += 4;
262 | } else {
263 | subExt = 0;
264 | }
265 | }
266 |
267 | for (p=(char *)ppstart; pplen >0;) {
268 | /* skip extension */
269 | if (subExt && p == ext) {
270 | p += 4;
271 | pplen -= 4;
272 | continue;
273 | }
274 | if (*p == '%') {
275 | unsigned int c;
276 | sscanf(p+1, "%02x", &c);
277 | *destptr++ = c;
278 | pplen -= 3;
279 | p += 3;
280 | } else {
281 | *destptr++ = *p++;
282 | pplen--;
283 | }
284 | }
285 | *destptr = '\0';
286 |
287 | out->av_val = streamname;
288 | out->av_len = destptr - streamname;
289 | }
290 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/rtmp.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_H__
2 | #define __RTMP_H__
3 | /*
4 | * Copyright (C) 2005-2008 Team XBMC
5 | * http://www.xbmc.org
6 | * Copyright (C) 2008-2009 Andrej Stepanchuk
7 | * Copyright (C) 2009-2010 Howard Chu
8 | *
9 | * This file is part of librtmp.
10 | *
11 | * librtmp is free software; you can redistribute it and/or modify
12 | * it under the terms of the GNU Lesser General Public License as
13 | * published by the Free Software Foundation; either version 2.1,
14 | * or (at your option) any later version.
15 | *
16 | * librtmp is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | * GNU General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Lesser General Public License
22 | * along with librtmp see the file COPYING. If not, write to
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 | * Boston, MA 02110-1301, USA.
25 | * http://www.gnu.org/copyleft/lgpl.html
26 | */
27 |
28 | #if !defined(NO_CRYPTO) && !defined(CRYPTO)
29 | #define CRYPTO
30 | #endif
31 |
32 | #include
33 | #include
34 | #include
35 |
36 | #include "amf.h"
37 |
38 | #ifdef __cplusplus
39 | extern "C"
40 | {
41 | #endif
42 |
43 | #define RTMP_LIB_VERSION 0x020300 /* 2.3 */
44 |
45 | #define RTMP_FEATURE_HTTP 0x01
46 | #define RTMP_FEATURE_ENC 0x02
47 | #define RTMP_FEATURE_SSL 0x04
48 | #define RTMP_FEATURE_MFP 0x08 /* not yet supported */
49 | #define RTMP_FEATURE_WRITE 0x10 /* publish, not play */
50 | #define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */
51 |
52 | #define RTMP_PROTOCOL_UNDEFINED -1
53 | #define RTMP_PROTOCOL_RTMP 0
54 | #define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC
55 | #define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP
56 | #define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL
57 | #define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC)
58 | #define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL)
59 | #define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP
60 |
61 | #define RTMP_DEFAULT_CHUNKSIZE 128
62 |
63 | /* needs to fit largest number of bytes recv() may return */
64 | #define RTMP_BUFFER_CACHE_SIZE (16*1024)
65 |
66 | #define RTMP_CHANNELS 65600
67 |
68 | extern const char RTMPProtocolStringsLower[][7];
69 | extern const AVal RTMP_DefaultFlashVer;
70 | extern int RTMP_ctrlC;
71 |
72 | uint32_t RTMP_GetTime(void);
73 |
74 | /* RTMP_PACKET_TYPE_... 0x00 */
75 | #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01
76 | /* RTMP_PACKET_TYPE_... 0x02 */
77 | #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03
78 | #define RTMP_PACKET_TYPE_CONTROL 0x04
79 | #define RTMP_PACKET_TYPE_SERVER_BW 0x05
80 | #define RTMP_PACKET_TYPE_CLIENT_BW 0x06
81 | /* RTMP_PACKET_TYPE_... 0x07 */
82 | #define RTMP_PACKET_TYPE_AUDIO 0x08
83 | #define RTMP_PACKET_TYPE_VIDEO 0x09
84 | /* RTMP_PACKET_TYPE_... 0x0A */
85 | /* RTMP_PACKET_TYPE_... 0x0B */
86 | /* RTMP_PACKET_TYPE_... 0x0C */
87 | /* RTMP_PACKET_TYPE_... 0x0D */
88 | /* RTMP_PACKET_TYPE_... 0x0E */
89 | #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F
90 | #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10
91 | #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11
92 | #define RTMP_PACKET_TYPE_INFO 0x12
93 | #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13
94 | #define RTMP_PACKET_TYPE_INVOKE 0x14
95 | /* RTMP_PACKET_TYPE_... 0x15 */
96 | #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16
97 |
98 | #define RTMP_MAX_HEADER_SIZE 18
99 |
100 | #define RTMP_PACKET_SIZE_LARGE 0
101 | #define RTMP_PACKET_SIZE_MEDIUM 1
102 | #define RTMP_PACKET_SIZE_SMALL 2
103 | #define RTMP_PACKET_SIZE_MINIMUM 3
104 |
105 | typedef struct RTMPChunk
106 | {
107 | int c_headerSize;
108 | int c_chunkSize;
109 | char *c_chunk;
110 | char c_header[RTMP_MAX_HEADER_SIZE];
111 | } RTMPChunk;
112 |
113 | typedef struct RTMPPacket
114 | {
115 | uint8_t m_headerType;
116 | uint8_t m_packetType;
117 | uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */
118 | int m_nChannel;
119 | uint32_t m_nTimeStamp; /* timestamp */
120 | int32_t m_nInfoField2; /* last 4 bytes in a long header */
121 | uint32_t m_nBodySize;
122 | uint32_t m_nBytesRead;
123 | RTMPChunk *m_chunk;
124 | char *m_body;
125 | } RTMPPacket;
126 |
127 | typedef struct RTMPSockBuf
128 | {
129 | int sb_socket;
130 | int sb_size; /* number of unprocessed bytes in buffer */
131 | char *sb_start; /* pointer into sb_pBuffer of next byte to process */
132 | char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */
133 | int sb_timedout;
134 | void *sb_ssl;
135 | } RTMPSockBuf;
136 |
137 | void RTMPPacket_Reset(RTMPPacket *p);
138 | void RTMPPacket_Dump(RTMPPacket *p);
139 | int RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize);
140 | void RTMPPacket_Free(RTMPPacket *p);
141 |
142 | #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize)
143 |
144 | typedef struct RTMP_LNK
145 | {
146 | AVal hostname;
147 | AVal sockshost;
148 |
149 | AVal playpath0; /* parsed from URL */
150 | AVal playpath; /* passed in explicitly */
151 | AVal tcUrl;
152 | AVal swfUrl;
153 | AVal pageUrl;
154 | AVal app;
155 | AVal auth;
156 | AVal flashVer;
157 | AVal subscribepath;
158 | AVal usherToken;
159 | AVal token;
160 | AVal pubUser;
161 | AVal pubPasswd;
162 | AMFObject extras;
163 | int edepth;
164 |
165 | int seekTime;
166 | int stopTime;
167 |
168 | #define RTMP_LF_AUTH 0x0001 /* using auth param */
169 | #define RTMP_LF_LIVE 0x0002 /* stream is live */
170 | #define RTMP_LF_SWFV 0x0004 /* do SWF verification */
171 | #define RTMP_LF_PLST 0x0008 /* send playlist before play */
172 | #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */
173 | #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */
174 | #define RTMP_LF_FAPU 0x0040 /* free app on close */
175 | int lFlags;
176 |
177 | int swfAge;
178 |
179 | int protocol;
180 | int timeout; /* connection timeout in seconds */
181 |
182 | int pFlags; /* unused, but kept to avoid breaking ABI */
183 |
184 | unsigned short socksport;
185 | unsigned short port;
186 |
187 | #ifdef CRYPTO
188 | #define RTMP_SWF_HASHLEN 32
189 | void *dh; /* for encryption */
190 | void *rc4keyIn;
191 | void *rc4keyOut;
192 |
193 | uint32_t SWFSize;
194 | uint8_t SWFHash[RTMP_SWF_HASHLEN];
195 | char SWFVerificationResponse[RTMP_SWF_HASHLEN+10];
196 | #endif
197 | } RTMP_LNK;
198 |
199 | /* state for read() wrapper */
200 | typedef struct RTMP_READ
201 | {
202 | char *buf;
203 | char *bufpos;
204 | unsigned int buflen;
205 | uint32_t timestamp;
206 | uint8_t dataType;
207 | uint8_t flags;
208 | #define RTMP_READ_HEADER 0x01
209 | #define RTMP_READ_RESUME 0x02
210 | #define RTMP_READ_NO_IGNORE 0x04
211 | #define RTMP_READ_GOTKF 0x08
212 | #define RTMP_READ_GOTFLVK 0x10
213 | #define RTMP_READ_SEEKING 0x20
214 | int8_t status;
215 | #define RTMP_READ_COMPLETE -3
216 | #define RTMP_READ_ERROR -2
217 | #define RTMP_READ_EOF -1
218 | #define RTMP_READ_IGNORE 0
219 |
220 | /* if bResume == TRUE */
221 | uint8_t initialFrameType;
222 | uint32_t nResumeTS;
223 | char *metaHeader;
224 | char *initialFrame;
225 | uint32_t nMetaHeaderSize;
226 | uint32_t nInitialFrameSize;
227 | uint32_t nIgnoredFrameCounter;
228 | uint32_t nIgnoredFlvFrameCounter;
229 | } RTMP_READ;
230 |
231 | typedef struct RTMP_METHOD
232 | {
233 | AVal name;
234 | int num;
235 | } RTMP_METHOD;
236 |
237 | typedef struct RTMP
238 | {
239 | int m_inChunkSize;
240 | int m_outChunkSize;
241 | int m_nBWCheckCounter;
242 | int m_nBytesIn;
243 | int m_nBytesInSent;
244 | int m_nBufferMS;
245 | int m_stream_id; /* returned in _result from createStream */
246 | int m_mediaChannel;
247 | uint32_t m_mediaStamp;
248 | uint32_t m_pauseStamp;
249 | int m_pausing;
250 | int m_nServerBW;
251 | int m_nClientBW;
252 | uint8_t m_nClientBW2;
253 | uint8_t m_bPlaying;
254 | uint8_t m_bSendEncoding;
255 | uint8_t m_bSendCounter;
256 |
257 | int m_numInvokes;
258 | int m_numCalls;
259 | RTMP_METHOD *m_methodCalls; /* remote method calls queue */
260 |
261 | int m_channelsAllocatedIn;
262 | int m_channelsAllocatedOut;
263 | RTMPPacket **m_vecChannelsIn;
264 | RTMPPacket **m_vecChannelsOut;
265 | int *m_channelTimestamp; /* abs timestamp of last packet */
266 |
267 | double m_fAudioCodecs; /* audioCodecs for the connect packet */
268 | double m_fVideoCodecs; /* videoCodecs for the connect packet */
269 | double m_fEncoding; /* AMF0 or AMF3 */
270 |
271 | double m_fDuration; /* duration of stream in seconds */
272 |
273 | int m_msgCounter; /* RTMPT stuff */
274 | int m_polling;
275 | int m_resplen;
276 | int m_unackd;
277 | AVal m_clientID;
278 |
279 | RTMP_READ m_read;
280 | RTMPPacket m_write;
281 | RTMPSockBuf m_sb;
282 | RTMP_LNK Link;
283 | } RTMP;
284 |
285 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host,
286 | unsigned int *port, AVal *playpath, AVal *app);
287 |
288 | void RTMP_ParsePlaypath(AVal *in, AVal *out);
289 | void RTMP_SetBufferMS(RTMP *r, int size);
290 | void RTMP_UpdateBufferMS(RTMP *r);
291 |
292 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg);
293 | int RTMP_SetupURL(RTMP *r, char *url);
294 | void RTMP_SetupStream(RTMP *r, int protocol,
295 | AVal *hostname,
296 | unsigned int port,
297 | AVal *sockshost,
298 | AVal *playpath,
299 | AVal *tcUrl,
300 | AVal *swfUrl,
301 | AVal *pageUrl,
302 | AVal *app,
303 | AVal *auth,
304 | AVal *swfSHA256Hash,
305 | uint32_t swfSize,
306 | AVal *flashVer,
307 | AVal *subscribepath,
308 | AVal *usherToken,
309 | int dStart,
310 | int dStop, int bLiveStream, long int timeout);
311 |
312 | int RTMP_Connect(RTMP *r, RTMPPacket *cp);
313 | struct sockaddr;
314 | int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
315 | int RTMP_Connect1(RTMP *r, RTMPPacket *cp);
316 | int RTMP_Serve(RTMP *r);
317 | int RTMP_TLS_Accept(RTMP *r, void *ctx);
318 |
319 | int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
320 | int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
321 | int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk);
322 | int RTMP_IsConnected(RTMP *r);
323 | int RTMP_Socket(RTMP *r);
324 | int RTMP_IsTimedout(RTMP *r);
325 | double RTMP_GetDuration(RTMP *r);
326 | int RTMP_ToggleStream(RTMP *r);
327 |
328 | int RTMP_ConnectStream(RTMP *r, int seekTime);
329 | int RTMP_ReconnectStream(RTMP *r, int seekTime);
330 | void RTMP_DeleteStream(RTMP *r);
331 | int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet);
332 | int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet);
333 |
334 | void RTMP_Init(RTMP *r);
335 | void RTMP_Close(RTMP *r);
336 | RTMP *RTMP_Alloc(void);
337 | void RTMP_Free(RTMP *r);
338 | void RTMP_EnableWrite(RTMP *r);
339 |
340 | void *RTMP_TLS_AllocServerContext(const char* cert, const char* key);
341 | void RTMP_TLS_FreeServerContext(void *ctx);
342 |
343 | int RTMP_LibVersion(void);
344 | void RTMP_UserInterrupt(void); /* user typed Ctrl-C */
345 |
346 | int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject,
347 | unsigned int nTime);
348 |
349 | /* caller probably doesn't know current timestamp, should
350 | * just use RTMP_Pause instead
351 | */
352 | int RTMP_SendPause(RTMP *r, int DoPause, int dTime);
353 | int RTMP_Pause(RTMP *r, int DoPause);
354 |
355 | int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
356 | AMFObjectProperty * p);
357 |
358 | int RTMPSockBuf_Fill(RTMPSockBuf *sb);
359 | int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len);
360 | int RTMPSockBuf_Close(RTMPSockBuf *sb);
361 |
362 | int RTMP_SendCreateStream(RTMP *r);
363 | int RTMP_SendSeek(RTMP *r, int dTime);
364 | int RTMP_SendServerBW(RTMP *r);
365 | int RTMP_SendClientBW(RTMP *r);
366 | void RTMP_DropRequest(RTMP *r, int i, int freeit);
367 | int RTMP_Read(RTMP *r, char *buf, int size);
368 | int RTMP_Write(RTMP *r, const char *buf, int size);
369 |
370 | /* hashswf.c */
371 | int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
372 | int age);
373 |
374 | #ifdef __cplusplus
375 | };
376 | #endif
377 |
378 | #endif
379 |
--------------------------------------------------------------------------------
/app/src/main/cpp/librtmp/rtmp_sys.h:
--------------------------------------------------------------------------------
1 | #ifndef __RTMP_SYS_H__
2 | #define __RTMP_SYS_H__
3 | /*
4 | * Copyright (C) 2010 Howard Chu
5 | *
6 | * This file is part of librtmp.
7 | *
8 | * librtmp is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU Lesser General Public License as
10 | * published by the Free Software Foundation; either version 2.1,
11 | * or (at your option) any later version.
12 | *
13 | * librtmp 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 Lesser General Public License
19 | * along with librtmp see the file COPYING. If not, write to
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 | * Boston, MA 02110-1301, USA.
22 | * http://www.gnu.org/copyleft/lgpl.html
23 | */
24 |
25 | #ifdef _WIN32
26 |
27 | #include
28 | #include
29 |
30 | #ifdef _MSC_VER /* MSVC */
31 | #define snprintf _snprintf
32 | #define strcasecmp stricmp
33 | #define strncasecmp strnicmp
34 | #define vsnprintf _vsnprintf
35 | #endif
36 |
37 | #define GetSockError() WSAGetLastError()
38 | #define SetSockError(e) WSASetLastError(e)
39 | #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
40 | #define EWOULDBLOCK WSAETIMEDOUT /* we don't use nonblocking, but we do use timeouts */
41 | #define sleep(n) Sleep(n*1000)
42 | #define msleep(n) Sleep(n)
43 | #define SET_RCVTIMEO(tv,s) int tv = s*1000
44 | #else /* !_WIN32 */
45 | #include
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #define GetSockError() errno
54 | #define SetSockError(e) errno = e
55 | #undef closesocket
56 | #define closesocket(s) close(s)
57 | #define msleep(n) usleep(n*1000)
58 | #define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0}
59 | #endif
60 |
61 | #include "rtmp.h"
62 |
63 | #ifdef USE_POLARSSL
64 | #include
65 | #include
66 | #include
67 | #include
68 | #if POLARSSL_VERSION_NUMBER < 0x01010000
69 | #define havege_random havege_rand
70 | #endif
71 | #if POLARSSL_VERSION_NUMBER >= 0x01020000
72 | #define SSL_SET_SESSION(S,resume,timeout,ctx) ssl_set_session(S,ctx)
73 | #else
74 | #define SSL_SET_SESSION(S,resume,timeout,ctx) ssl_set_session(S,resume,timeout,ctx)
75 | #endif
76 | typedef struct tls_ctx {
77 | havege_state hs;
78 | ssl_session ssn;
79 | } tls_ctx;
80 | typedef struct tls_server_ctx {
81 | havege_state *hs;
82 | x509_cert cert;
83 | rsa_context key;
84 | ssl_session ssn;
85 | const char *dhm_P, *dhm_G;
86 | } tls_server_ctx;
87 |
88 | #define TLS_CTX tls_ctx *
89 | #define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\
90 | ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
91 | ssl_set_rng(s, havege_random, &ctx->hs);\
92 | ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
93 | SSL_SET_SESSION(s, 1, 600, &ctx->ssn)
94 | #define TLS_server(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\
95 | ssl_set_endpoint(s, SSL_IS_SERVER); ssl_set_authmode(s, SSL_VERIFY_NONE);\
96 | ssl_set_rng(s, havege_random, ((tls_server_ctx*)ctx)->hs);\
97 | ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
98 | SSL_SET_SESSION(s, 1, 600, &((tls_server_ctx*)ctx)->ssn);\
99 | ssl_set_own_cert(s, &((tls_server_ctx*)ctx)->cert, &((tls_server_ctx*)ctx)->key);\
100 | ssl_set_dh_param(s, ((tls_server_ctx*)ctx)->dhm_P, ((tls_server_ctx*)ctx)->dhm_G)
101 | #define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd)
102 | #define TLS_connect(s) ssl_handshake(s)
103 | #define TLS_accept(s) ssl_handshake(s)
104 | #define TLS_read(s,b,l) ssl_read(s,(unsigned char *)b,l)
105 | #define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l)
106 | #define TLS_shutdown(s) ssl_close_notify(s)
107 | #define TLS_close(s) ssl_free(s); free(s)
108 |
109 | #elif defined(USE_GNUTLS)
110 | #include
111 | typedef struct tls_ctx {
112 | gnutls_certificate_credentials_t cred;
113 | gnutls_priority_t prios;
114 | } tls_ctx;
115 | #define TLS_CTX tls_ctx *
116 | #define TLS_client(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred)
117 | #define TLS_server(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_SERVER); gnutls_priority_set_direct(s, "NORMAL", NULL); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx)
118 | #define TLS_setfd(s,fd) gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd)
119 | #define TLS_connect(s) gnutls_handshake(s)
120 | #define TLS_accept(s) gnutls_handshake(s)
121 | #define TLS_read(s,b,l) gnutls_record_recv(s,b,l)
122 | #define TLS_write(s,b,l) gnutls_record_send(s,b,l)
123 | #define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR)
124 | #define TLS_close(s) gnutls_deinit(s)
125 |
126 | #else /* USE_OPENSSL */
127 | #define TLS_CTX SSL_CTX *
128 | #define TLS_client(ctx,s) s = SSL_new(ctx)
129 | #define TLS_server(ctx,s) s = SSL_new(ctx)
130 | #define TLS_setfd(s,fd) SSL_set_fd(s,fd)
131 | #define TLS_connect(s) SSL_connect(s)
132 | #define TLS_accept(s) SSL_accept(s)
133 | #define TLS_read(s,b,l) SSL_read(s,b,l)
134 | #define TLS_write(s,b,l) SSL_write(s,b,l)
135 | #define TLS_shutdown(s) SSL_shutdown(s)
136 | #define TLS_close(s) SSL_free(s)
137 |
138 | #endif
139 | #endif
140 |
--------------------------------------------------------------------------------
/app/src/main/cpp/native-lib.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 |
5 | extern "C" {
6 | #include "RtmpPush.h"
7 |
8 | RtmpPush *rtmp_push;
9 | int start_vedio;
10 | int start_audio;
11 |
12 | static int VIDEO_VERTIAL_BACK = 1;
13 |
14 | static int VIDEO_VERTIAL_AFTER = 2;
15 |
16 | static int VIDEO_LEFT = 3;
17 |
18 | void
19 | Java_com_dzm_rtmppush_RtmpPush_initVideo(JNIEnv *env, jobject /* this */, jstring out_url, jint width,
20 | jint height, jint bitrate) {
21 | LOGD("rtmp 初始化");
22 | if(!rtmp_push){
23 | rtmp_push = new RtmpPush();
24 | }
25 | const char *out_url_char = env->GetStringUTFChars(out_url,0);
26 | start_vedio = rtmp_push->init_vedio(out_url_char,width,height,bitrate);
27 | env->ReleaseStringUTFChars(out_url,out_url_char);
28 | }
29 |
30 | void
31 | Java_com_dzm_rtmppush_RtmpPush_pushVideo(JNIEnv *env,jobject /* this */,jbyteArray data,jint index){
32 | if(start_vedio != 2){
33 | return;
34 | }
35 | // LOGD("数据进入jni");
36 | char* data_char = (char *) env->GetByteArrayElements(data, 0);
37 | if(index == VIDEO_VERTIAL_BACK){
38 | rtmp_push->video_collect_back(data_char);
39 | }else if(index == VIDEO_VERTIAL_AFTER){
40 | rtmp_push->video_collect_above(data_char);
41 | }else if(index == VIDEO_LEFT){
42 | rtmp_push->video_collect_left(data_char);
43 | }
44 | env->ReleaseByteArrayElements(data, (jbyte *) data_char, 0);
45 | }
46 |
47 | void
48 | Java_com_dzm_rtmppush_RtmpPush_initAudio(JNIEnv *env,jobject /* this */,jint sampleRate,jint channel){
49 | if(!rtmp_push){
50 | rtmp_push = new RtmpPush();
51 | }
52 | start_audio = rtmp_push->init_audio(sampleRate,channel);
53 | }
54 |
55 | void
56 | Java_com_dzm_rtmppush_RtmpPush_pushAudio(JNIEnv *env,jobject /* this */,jbyteArray data){
57 | if(start_audio != 2){
58 | return;
59 | }
60 | char* data_char = (char *) env->GetByteArrayElements(data, 0);
61 | rtmp_push->audio_cllect(data_char);
62 | env->ReleaseByteArrayElements(data, (jbyte *) data_char, 0);
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/queue.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | typedef struct queue_node {
5 | struct queue_node* prev;
6 | struct queue_node* next;
7 | void *p;
8 | } node;
9 |
10 | // 表头。注意,表头不存放元素值!!!
11 | static node *phead = NULL;
12 | static int count = 0;
13 |
14 | static node* create_node(void *pval) {
15 | node *pnode = NULL;
16 | pnode = (node *) malloc(sizeof(node));
17 | if (pnode) {
18 | // 默认的,pnode的前一节点和后一节点都指向它自身
19 | pnode->prev = pnode->next = pnode;
20 | // 节点的值为pval
21 | pnode->p = pval;
22 | }
23 | return pnode;
24 | }
25 | // 新建“双向链表”。成功,返回0;否则,返回-1。
26 | int create_queue() {
27 | phead = create_node(NULL);
28 | if (!phead) {
29 | return -1;
30 | }
31 | // 设置“节点个数”为0
32 | count = 0;
33 | return 0;
34 | }
35 |
36 | // “双向链表是否为空”
37 | int queue_is_empty() {
38 | return count == 0;
39 | }
40 |
41 | // 返回“双向链表的大小”
42 | int queue_size() {
43 | return count;
44 | }
45 |
46 | // 获取“双向链表中第index位置的节点”
47 | static node* get_node(int index) {
48 | if (index < 0 || index >= count) {
49 | return NULL;
50 | }
51 | if (index <= (count / 2)) {
52 | int i = 0;
53 | node *pnode = phead->next;
54 | while ((i++) < index)
55 | pnode = pnode->next;
56 | return pnode;
57 | }
58 | int j = 0;
59 | int rindex = count - index - 1;
60 | node *rnode = phead->prev;
61 | while ((j++) < rindex)
62 | rnode = rnode->prev;
63 | return rnode;
64 | }
65 |
66 | // 获取“第一个节点”
67 | static node* get_first_node() {
68 | return get_node(0);
69 | }
70 | // 获取“最后一个节点”
71 | static node* get_last_node() {
72 | return get_node(count - 1);
73 | }
74 | // 获取“双向链表中第index位置的元素”。成功,返回节点值;否则,返回-1。
75 | void* queue_get(int index) {
76 | node *pindex = get_node(index);
77 | if (!pindex) {
78 | return NULL;
79 | }
80 | return pindex->p;
81 | }
82 |
83 | // 获取“双向链表中第1个元素的值”
84 | void* queue_get_first() {
85 | return queue_get(0);
86 | }
87 |
88 | void* queue_get_last() {
89 | return queue_get(count - 1);
90 | }
91 |
92 | // 将“pval”插入到index位置。成功,返回0;否则,返回-1。
93 | int queue_insert(int index, void* pval) {
94 | // 插入表头
95 | if (index == 0)
96 | return queue_insert_first(pval);
97 | // 获取要插入的位置对应的节点
98 | node *pindex = get_node(index);
99 | if (!pindex)
100 | return -1;
101 | // 创建“节点”
102 | node *pnode = create_node(pval);
103 | if (!pnode)
104 | return -1;
105 | pnode->prev = pindex->prev;
106 | pnode->next = pindex;
107 | pindex->prev->next = pnode;
108 | pindex->prev = pnode;
109 | // 节点个数+1
110 | count++;
111 | return 0;
112 | }
113 |
114 | // 将“pval”插入到表头位置
115 | int queue_insert_first(void *pval) {
116 | node *pnode = create_node(pval);
117 | if (!pnode)
118 | return -1;
119 | pnode->prev = phead;
120 | pnode->next = phead->next;
121 | phead->next->prev = pnode;
122 | phead->next = pnode;
123 | count++;
124 | return 0;
125 | }
126 | // 将“pval”插入到末尾位置
127 | int queue_append_last(void *pval) {
128 | node *pnode = create_node(pval);
129 | if (!pnode)
130 | return -1;
131 | pnode->next = phead;
132 | pnode->prev = phead->prev;
133 | phead->prev->next = pnode;
134 | phead->prev = pnode;
135 | count++;
136 | return 0;
137 | }
138 | // 删除“双向链表中index位置的节点”。成功,返回0;否则,返回-1。
139 | int queue_delete(int index) {
140 | node *pindex = get_node(index);
141 | if (!pindex) {
142 | return -1;
143 | }
144 | pindex->next->prev = pindex->prev;
145 | pindex->prev->next = pindex->next;
146 | free(pindex);
147 | count--;
148 | return 0;
149 | }
150 | // 删除第一个节点
151 | int queue_delete_first() {
152 | return queue_delete(0);
153 | }
154 | // 删除组后一个节点
155 | int queue_delete_last() {
156 | return queue_delete(count - 1);
157 | }
158 | // 撤销“双向链表”。成功,返回0;否则,返回-1。
159 | int destroy_queue() {
160 | if (!phead) {
161 | return -1;
162 | }
163 | node *pnode = phead->next;
164 | node *ptmp = NULL;
165 | while (pnode != phead) {
166 | ptmp = pnode;
167 | pnode = pnode->next;
168 | free(ptmp);
169 | }
170 | free(phead);
171 | phead = NULL;
172 | count = 0;
173 | return 0;
174 | }
175 |
--------------------------------------------------------------------------------
/app/src/main/cpp/queue.h:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #ifndef _QUQEUE_H
5 | #define _QUQEUE_H
6 | // 新建“双向链表”。成功,返回表头;否则,返回NULL
7 | extern int create_queue();
8 | // 撤销“双向链表”。成功,返回0;否则,返回-1
9 | extern int destroy_queue();
10 | // “双向链表是否为空”。为空的话返回1;否则,返回0。
11 | extern int queue_is_empty();
12 | // 返回“双向链表的大小”
13 | extern int queue_size();
14 | // 获取“双向链表中第index位置的元素”。成功,返回节点指针;否则,返回NULL。
15 | extern void* queue_get(int index);
16 | // 获取“双向链表中第1个元素”。成功,返回节点指针;否则,返回NULL。
17 | extern void* queue_get_first();
18 | // 获取“双向链表中最后1个元素”。成功,返回节点指针;否则,返回NULL。
19 | extern void* queue_get_last();
20 | // 将“value”插入到index位置。成功,返回0;否则,返回-1。
21 | extern int queue_insert(int index, void *pval);
22 | // 将“value”插入到表头位置。成功,返回0;否则,返回-1。
23 | extern int queue_insert_first(void *pval);
24 | // 将“value”插入到末尾位置。成功,返回0;否则,返回-1。
25 | extern int queue_append_last(void *pval);
26 | // 删除“双向链表中index位置的节点”。成功,返回0;否则,返回-1
27 | extern int queue_delete(int index);
28 | // 删除第一个节点。成功,返回0;否则,返回-1
29 | extern int queue_delete_first();
30 | // 删除组后一个节点。成功,返回0;否则,返回-1
31 | extern int queue_delete_last();
32 | #endif//_QUQEUE_H
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.dzm.rtmppush.audio.AudioController;
7 | import com.dzm.rtmppush.view.CameraSurfacevView;
8 | import com.dzm.rtmppush.view.MediaSerfaceView;
9 |
10 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
11 |
12 | public class MainActivity extends AppCompatActivity {
13 |
14 | private RtmpPush rtmpPush;
15 |
16 |
17 | private MediaSerfaceView mediaSurface;
18 |
19 | private AudioController audioController;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_main);
25 |
26 | initPlayer();
27 | initRtmp();
28 | initAudio();
29 | CameraSurfacevView cameraSurface = (CameraSurfacevView) findViewById(R.id.cameraSurface);
30 | cameraSurface.setmRtmpPush(rtmpPush);
31 |
32 | mediaSurface = (MediaSerfaceView) findViewById(R.id.mediaSurface);
33 | mediaSurface.setZOrderOnTop(true);
34 | mediaSurface.play("rtmp://192.168.1.125/live");
35 |
36 | }
37 |
38 | private void initAudio() {
39 | audioController = new AudioController();
40 | audioController.start(rtmpPush);
41 | }
42 |
43 | private void initPlayer(){
44 | IjkMediaPlayer.loadLibrariesOnce(null);
45 | IjkMediaPlayer.native_profileBegin("libijkplayer.so");
46 | }
47 |
48 | private void initRtmp(){
49 | rtmpPush = new RtmpPush();
50 | rtmpPush.initVideo("rtmp://192.168.1.125/live",480,640,750_000);
51 | }
52 |
53 | @Override
54 | protected void onResume() {
55 | mediaSurface.onResume();
56 | super.onResume();
57 | }
58 |
59 | @Override
60 | protected void onPause() {
61 | mediaSurface.onPause();
62 | super.onPause();
63 | }
64 |
65 | @Override
66 | protected void onDestroy() {
67 | mediaSurface.onDestroy();
68 | audioController.destroy();
69 | super.onDestroy();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/RtmpPush.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush;
2 |
3 | /**
4 | *
5 | * @author 邓治民
6 | * date 2017/2/22 16:43
7 | */
8 |
9 | public class RtmpPush {
10 |
11 | static {
12 | System.loadLibrary("native-lib");
13 | }
14 |
15 |
16 | public native void pushVideo(byte[] data,int index);
17 |
18 | public native void initVideo(String url,int width,int height,int bitrate);
19 |
20 | public native void initAudio(int sampleRate,int channel);
21 |
22 | public native void pushAudio(byte[] data);
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/audio/AudioController.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush.audio;
2 |
3 | import com.dzm.rtmppush.RtmpPush;
4 |
5 |
6 | /**
7 | *
8 | * @author 邓治民
9 | * date 2016/12/28 20:17
10 | * audio 控制器
11 | */
12 |
13 | public class AudioController {
14 |
15 | static final int sampleRate = 44100;
16 |
17 | private static final int channel = 1;
18 |
19 | private AudioRunnable runnable;
20 |
21 | public void start(RtmpPush rtmpPush_){
22 | rtmpPush_.initAudio(sampleRate,channel);
23 | new Thread(runnable = new AudioRunnable(rtmpPush_)).start();
24 | }
25 |
26 | public void destroy() {
27 | runnable.destroy();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/audio/AudioRunnable.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush.audio;
2 |
3 | import android.media.AudioFormat;
4 | import android.media.AudioRecord;
5 | import android.media.MediaRecorder;
6 |
7 | import com.dzm.rtmppush.RtmpPush;
8 |
9 | /**
10 | * @author 邓治民
11 | * date 2017/2/23 15:20
12 | */
13 |
14 | class AudioRunnable implements Runnable {
15 |
16 | private int audio_size;
17 |
18 | private RtmpPush rtmpPush;
19 |
20 | private boolean isAudio;
21 |
22 | AudioRunnable(RtmpPush rtmpPush){
23 | this.rtmpPush = rtmpPush;
24 | isAudio = true;
25 | audio_size = AudioRecord.getMinBufferSize(AudioController.sampleRate, AudioFormat.CHANNEL_IN_MONO,
26 | AudioFormat.ENCODING_PCM_16BIT);
27 |
28 | }
29 |
30 | @Override
31 | public void run() {
32 | AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
33 | AudioController.sampleRate, AudioFormat.CHANNEL_IN_MONO,
34 | AudioFormat.ENCODING_PCM_16BIT, audio_size);
35 | try {
36 | audioRecord.startRecording();
37 | } catch (Exception e) {
38 | e.printStackTrace();
39 | audioRecord.release();
40 | return;
41 | }
42 |
43 | while (isAudio){
44 | byte[] bytes = new byte[2048];
45 | int len = audioRecord.read(bytes, 0, bytes.length);
46 | if(null != rtmpPush && len >0)
47 | rtmpPush.pushAudio(bytes);
48 | }
49 | audioRecord.stop();
50 | audioRecord.release();
51 | }
52 |
53 | void destroy() {
54 | isAudio = false;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/view/CameraSurfacevView.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush.view;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.hardware.Camera;
6 | import android.util.AttributeSet;
7 | import android.view.Surface;
8 | import android.view.SurfaceHolder;
9 | import android.view.SurfaceView;
10 |
11 | import com.dzm.rtmppush.RtmpPush;
12 |
13 | import java.io.IOException;
14 |
15 | /**
16 | * @author 邓治民
17 | * date 2016/12/28 10:52
18 | * 获取摄像头 头像 承载surfaceview
19 | */
20 |
21 | public class CameraSurfacevView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
22 |
23 | /** 相机 */
24 | private Camera mCamera;
25 |
26 | /** 是否打开camera */
27 | private boolean mPreviewRunning;
28 |
29 | /** rtmp native */
30 | private RtmpPush mRtmpPush;
31 |
32 | /** 0代表前置摄像头,1代表后置摄像头 */
33 | private int cameraPosition = 1;
34 |
35 | /** 分辨率宽度 */
36 | private static final int CAMERAWIDTH = 640;
37 | /** 分辨率 */
38 | private static final int CAMERAHEIGHT = 480;
39 |
40 | private final static int SCREEN_PORTRAIT = 0;
41 | private final static int SCREEN_LANDSCAPE_LEFT = 90;
42 | private final static int SCREEN_LANDSCAPE_RIGHT = 270;
43 | private int screen;
44 | private byte[] raw;
45 |
46 | private int cameraId;
47 |
48 | public CameraSurfacevView(Context context) {
49 | this(context, null);
50 | }
51 |
52 | public CameraSurfacevView(Context context, AttributeSet attrs) {
53 | this(context, attrs, 0);
54 | }
55 |
56 | public CameraSurfacevView(Context context, AttributeSet attrs, int defStyleAttr) {
57 | super(context, attrs, defStyleAttr);
58 | init();
59 | }
60 |
61 | private void init() {
62 | getHolder().addCallback(this);
63 | getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
64 | raw = new byte[CAMERAWIDTH * CAMERAHEIGHT * 3 / 2];
65 | }
66 |
67 | /**
68 | * 设置rtmp native
69 | * @param mRtmpPush native
70 | */
71 | public void setmRtmpPush(RtmpPush mRtmpPush) {
72 | this.mRtmpPush = mRtmpPush;
73 | }
74 |
75 | @Override
76 | public void surfaceCreated(SurfaceHolder surfaceHolder) {
77 | mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);//代表摄像头的方位,CAMERA_FACING_FRONT前置
78 | setCameraDisplayOrientation((Activity) getContext(), Camera.CameraInfo.CAMERA_FACING_FRONT, mCamera);
79 | cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
80 | cameraPosition = 0;
81 |
82 | }
83 |
84 | @Override
85 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
86 | if (mPreviewRunning) {
87 | mCamera.stopPreview();
88 | }
89 |
90 |
91 | Camera.Parameters p = mCamera.getParameters();
92 | // List sizeList = p.getSupportedPreviewSizes();
93 | // for(Camera.Size ss:sizeList){
94 | // Log.d("size", ss.width+"::"+ss.height);
95 | // }
96 | p.setPreviewSize(CAMERAWIDTH, CAMERAHEIGHT);
97 | // p.setPreviewSize(720, 480);
98 | // p.setRotation(90);
99 | // mCamera.setDisplayOrientation();
100 | mCamera.setPreviewCallback(this);
101 | mCamera.setParameters(p);
102 | try {
103 | mCamera.setPreviewDisplay(getHolder());
104 | } catch (IOException e) {
105 | e.printStackTrace();
106 | }
107 | mCamera.startPreview();
108 | mPreviewRunning = true;
109 | }
110 |
111 | @Override
112 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
113 | if (null != mCamera) {
114 | mCamera.setPreviewCallback(null);
115 | mCamera.stopPreview();
116 | mCamera.release();
117 | mCamera = null;
118 | }
119 | }
120 |
121 | @Override
122 | public void onPreviewFrame(byte[] data, Camera camera) {
123 | if(null == data){
124 | return;
125 | }
126 | switch (screen) {
127 | case SCREEN_PORTRAIT:
128 | if(cameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
129 | mRtmpPush.pushVideo(data,1);
130 | }else{
131 | mRtmpPush.pushVideo(data,2);
132 | }
133 | break;
134 | case SCREEN_LANDSCAPE_LEFT:
135 | mRtmpPush.pushVideo(data,3);
136 | break;
137 | case SCREEN_LANDSCAPE_RIGHT:
138 | landscapeData2Raw(data);
139 | mRtmpPush.pushVideo(raw,3);
140 | break;
141 | }
142 | }
143 |
144 | /**
145 | * 前后屏切换
146 | */
147 | public void change() {
148 | //切换前后摄像头
149 | int cameraCount = 0;
150 | Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
151 | cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数
152 |
153 | for (int i = 0; i < cameraCount; i++) {
154 | Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息
155 | if (cameraPosition == 1) {
156 | //现在是后置,变更为前置
157 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
158 | mCamera.stopPreview();//停掉原来摄像头的预览
159 | mCamera.release();//释放资源
160 | mCamera = null;//取消原来摄像头
161 | mCamera = Camera.open(i);//打开当前选中的摄像头
162 | try {
163 | Camera.Parameters p = mCamera.getParameters();
164 | p.setPreviewSize(CAMERAWIDTH, CAMERAHEIGHT);
165 | mCamera.setPreviewCallback(this);
166 | mCamera.setParameters(p);
167 | mCamera.setPreviewDisplay(getHolder());//通过surfaceview显示取景画面
168 | } catch (IOException e) {
169 | // TODO Auto-generated catch block
170 | e.printStackTrace();
171 | }
172 | mCamera.startPreview();//开始预览
173 | cameraPosition = 0;
174 | break;
175 | }
176 | } else {
177 | //现在是前置, 变更为后置
178 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
179 | mCamera.stopPreview();//停掉原来摄像头的预览
180 | mCamera.release();//释放资源
181 | mCamera = null;//取消原来摄像头
182 | mCamera = Camera.open(i);//打开当前选中的摄像头
183 | try {
184 | Camera.Parameters p = mCamera.getParameters();
185 | p.setPreviewSize(CAMERAWIDTH, CAMERAHEIGHT);
186 | mCamera.setPreviewCallback(this);
187 | mCamera.setParameters(p);
188 | mCamera.setPreviewDisplay(getHolder());//通过surfaceview显示取景画面
189 | } catch (IOException e) {
190 | // TODO Auto-generated catch block
191 | e.printStackTrace();
192 | }
193 | mCamera.startPreview();//开始预览
194 | cameraPosition = 1;
195 | break;
196 | }
197 | }
198 |
199 | }
200 | }
201 |
202 | private void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
203 | android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
204 | android.hardware.Camera.getCameraInfo(cameraId, info);//得到每一个摄像头的信息
205 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
206 | int degrees = 0;
207 | switch (rotation) {
208 | case Surface.ROTATION_0:
209 | screen = SCREEN_PORTRAIT;
210 | degrees = 0;
211 | break;
212 | case Surface.ROTATION_90:// 横屏 左边是头部(home键在右边)
213 | screen = SCREEN_LANDSCAPE_LEFT;
214 | degrees = 90;
215 | break;
216 | case Surface.ROTATION_180:
217 | screen = 180;
218 | degrees = 180;
219 | break;
220 | case Surface.ROTATION_270:// 横屏 头部在右边
221 | screen = SCREEN_LANDSCAPE_RIGHT;
222 | degrees = 270;
223 | break;
224 | }
225 | int result;
226 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
227 | result = (info.orientation + degrees) % 360;
228 | result = (360 - result) % 360; // compensate the mirror
229 | } else {
230 | // back-facing
231 | result = (info.orientation - degrees + 360) % 360;
232 | }
233 | camera.setDisplayOrientation(result);
234 | }
235 |
236 | private void landscapeData2Raw(byte[] data) {
237 | int width = CAMERAWIDTH, height = CAMERAHEIGHT;
238 | int y_len = width * height;
239 | int k = 0;
240 | // y数据倒叙插入raw中
241 | for (int i = y_len - 1; i > -1; i--) {
242 | raw[k] = data[i];
243 | k++;
244 | }
245 | // System.arraycopy(data, y_len, raw, y_len, uv_len);
246 | // v1 u1 v2 u2
247 | // v3 u3 v4 u4
248 | // 需要转换为:
249 | // v4 u4 v3 u3
250 | // v2 u2 v1 u1
251 | int maxpos = data.length - 1;
252 | int uv_len = y_len >> 2; // 4:1:1
253 | for (int i = 0; i < uv_len; i++) {
254 | int pos = i << 1;
255 | raw[y_len + i * 2] = data[maxpos - pos - 1];
256 | raw[y_len + i * 2 + 1] = data[maxpos - pos];
257 | }
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/app/src/main/java/com/dzm/rtmppush/view/MediaSerfaceView.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetFileDescriptor;
5 | import android.media.AudioManager;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.SurfaceHolder;
10 | import android.view.SurfaceView;
11 |
12 | import java.io.IOException;
13 |
14 | import tv.danmaku.ijk.media.player.IMediaPlayer;
15 | import tv.danmaku.ijk.media.player.IjkMediaPlayer;
16 |
17 | /**
18 | *
19 | * @author 邓治民
20 | * date 2016/12/28 11:30
21 | * 播放rtmp 流
22 | */
23 | public class MediaSerfaceView extends SurfaceView implements SurfaceHolder.Callback, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnPreparedListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnVideoSizeChangedListener{
24 |
25 | private IjkMediaPlayer mediaPlayer;
26 |
27 | // private List fileAddress;
28 | private String mPath;
29 | // private int listPositon;
30 |
31 | // private Object mutex = new Object();
32 |
33 | private SurfaceHolder surfaceHolder;
34 |
35 | private int streamVolume;
36 | private AudioManager am;
37 |
38 | private Context context;
39 |
40 | public MediaSerfaceView(Context context) {
41 | this(context,null);
42 | }
43 |
44 | public MediaSerfaceView(Context context, AttributeSet attrs) {
45 | this(context, attrs,0);
46 | }
47 |
48 | public MediaSerfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
49 | super(context, attrs, defStyleAttr);
50 | this.context = context;
51 | am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
52 | // fileAddress = new ArrayList<>();
53 | // fileAddress.add("rtmp://live.hkstv.hk.lxdns.com/live/hks");
54 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "cadey.mp4");
55 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "shell.mp4");
56 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "test.mp4");
57 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "a1.mp4");
58 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "a2.mp4");
59 | // fileAddress.add(FTPFileUtil.SDCARDPATH + "a3.mp4");
60 | initPlayer();
61 | init();
62 | }
63 |
64 | public void init() {
65 | getHolder().addCallback(this);
66 | getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
67 | // if (fileAddress.size() > 0) {
68 | // listPositon = 0;
69 | // play(fileCheck());
70 | // }
71 | }
72 |
73 | public void initPlayer(){
74 | mediaPlayer = new IjkMediaPlayer();
75 | // mediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
76 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
77 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
78 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
79 |
80 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
81 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
82 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
83 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
84 |
85 | mediaPlayer.setOnCompletionListener(this);
86 | mediaPlayer.setOnErrorListener(this);
87 | mediaPlayer.setOnInfoListener(this);
88 | mediaPlayer.setOnPreparedListener(this);
89 | mediaPlayer.setOnSeekCompleteListener(this);
90 | mediaPlayer.setOnVideoSizeChangedListener(this);
91 | }
92 |
93 | public void play(String path) {
94 | try {
95 | if (null == mediaPlayer)
96 | return;
97 | if (TextUtils.isEmpty(path)) {
98 | return;
99 | }
100 | this.mPath = path;
101 | mediaPlayer.reset();
102 | if (null != surfaceHolder)
103 | mediaPlayer.setDisplay(surfaceHolder);
104 | mediaPlayer.setDataSource(path);
105 | // mediaPlayer.setDataSource("rtmp://192.168.1.120/live");
106 | mediaPlayer.prepareAsync();
107 | } catch (IOException e) {
108 | e.printStackTrace();
109 | }
110 | }
111 |
112 | public void play(AssetFileDescriptor path) {
113 | try {
114 | if (null == mediaPlayer)
115 | return;
116 | if (null == path) {
117 | return;
118 | }
119 | mediaPlayer.reset();
120 | if (null != surfaceHolder)
121 | mediaPlayer.setDisplay(surfaceHolder);
122 | mediaPlayer.setDataSource(path.getFileDescriptor());
123 | // mediaPlayer.setDataSource("rtmp://192.168.1.120/live");
124 | mediaPlayer.prepareAsync();
125 | } catch (IOException e) {
126 | e.printStackTrace();
127 | }
128 | }
129 |
130 | public void onPause() {
131 | streamVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
132 | try {
133 | am.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_MUTE, 0);
134 | } catch (Exception e) {
135 | e.printStackTrace();
136 | am.setStreamMute(AudioManager.STREAM_MUSIC, true);
137 | // if (streamVolume != 0)
138 | // am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
139 | }
140 | // if (null != mediaPlayer && mediaPlayer.isPlaying()) {
141 | // mediaPlayer.pause();
142 | // }
143 | }
144 |
145 | public void onResume() {
146 | try {
147 | am.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_UNMUTE, 0);
148 | } catch (Exception e) {
149 | e.printStackTrace();
150 | am.setStreamMute(AudioManager.STREAM_MUSIC, false);
151 | // if (streamVolume != 0)
152 | // am.setStreamVolume(AudioManager.STREAM_MUSIC, streamVolume, 0);
153 | }
154 | // if (null != mediaPlayer && !mediaPlayer.isPlaying()) {
155 | // mediaPlayer.start();
156 | // }
157 | }
158 |
159 | public void stop() {
160 | if (null != mediaPlayer && mediaPlayer.isPlaying())
161 | mediaPlayer.pause();
162 | }
163 |
164 | // public void exit() {
165 | //
166 | // if (fileAddress != null) {
167 | // fileAddress.clear();
168 | // }
169 | // }
170 |
171 | public void playErer(){
172 | if(null != mediaPlayer){
173 | mediaPlayer.release();
174 | mediaPlayer = null;
175 | }
176 | initPlayer();
177 | play(mPath);
178 | }
179 |
180 | public void onDestroy(){
181 | if (null != mediaPlayer && mediaPlayer.isPlaying()){
182 | mediaPlayer.stop();
183 | mediaPlayer.pause();
184 | }
185 | mediaPlayer = null;
186 |
187 | }
188 |
189 | // public void add(String fileName) {
190 | // fileAddress.add(fileName);
191 | // }
192 |
193 | // public void addAll(JSONArray jsonArray) {
194 | // try {
195 | // synchronized (mutex) {
196 | // clearList();
197 | // for (int i = 0; i < jsonArray.length(); i++) {
198 | // JSONObject jsonObject = jsonArray.getJSONObject(i);
199 | // add(FTPFileUtil.SDCARDPATH + jsonObject.getString("name"));
200 | // }
201 | // }
202 | // play();
203 | // } catch (JSONException e) {
204 | // e.printStackTrace();
205 | // }
206 | //
207 | // }
208 |
209 | // public void addAll(List list) {
210 | // synchronized (mutex) {
211 | // clearList();
212 | // fileAddress.addAll(list);
213 | // }
214 | // play();
215 | // }
216 |
217 | public boolean isCurrentPlaying(String path) {
218 | if(TextUtils.isEmpty(mPath) || TextUtils.isEmpty(path))
219 | return false;
220 | if(null == mediaPlayer)
221 | return false;
222 | if(TextUtils.equals(mPath,path) && null != mediaPlayer && mediaPlayer.isPlaying())
223 | return false;
224 | return true;
225 | }
226 |
227 | public long getCurrentPosition(){
228 | return mediaPlayer!=null?mediaPlayer.getCurrentPosition():0;
229 | }
230 |
231 |
232 | // long seekto = 0;
233 | // public void seekTo(String name,long l){
234 | // seekto = l;
235 | // if(TextUtils.equals(FTPFileUtil.SDCARDPATH+name,getCurrent())&&null != mediaPlayer){
236 | // mediaPlayer.seekTo(l);
237 | // }else{
238 | // String fineName = FTPFileUtil.SDCARDPATH+name;
239 | // if(null == mediaPlayer){
240 | // initPlayer();
241 | // }
242 | // play(fineName);
243 | // for(int i = 0;i 0) {
327 | // if (listPositon<0 || listPositon >= fileAddress.size()) {
328 | // listPositon = 0;
329 | // }
330 | // play(fileCheck());
331 | // }
332 | }
333 |
334 | @Override
335 | public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
336 | return false;
337 | }
338 |
339 | @Override
340 | public void onPrepared(IMediaPlayer iMediaPlayer) {
341 | iMediaPlayer.start();
342 | // if(seekto > 0){
343 | // iMediaPlayer.seekTo(seekto);
344 | // seekto = 0;
345 | // }
346 | }
347 |
348 | @Override
349 | public void onSeekComplete(IMediaPlayer iMediaPlayer) {
350 |
351 | }
352 |
353 | @Override
354 | public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
355 |
356 | }
357 |
358 | @Override
359 | public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
360 | playErer();
361 | Log.e("","");
362 | return false;
363 | }
364 |
365 |
366 | }
367 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Rtmp_Push
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/dzm/rtmppush/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.dzm.rtmppush;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | * @see Testing documentation
10 | */
11 | public class ExampleUnitTest {
12 | @Test
13 | public void addition_isCorrect() throws Exception {
14 | assertEquals(4, 2 + 2);
15 | }
16 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dengzhi00/Rtmp_Push/be441c1fc8cd9b57f8419f576f2fc3bd47c80a51/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------