├── .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 | --------------------------------------------------------------------------------