├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yxt │ │ └── xiaozhenkeji │ │ └── androidlivepusher │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ └── native-lib.cpp │ ├── java │ │ └── com │ │ │ └── yxt │ │ │ └── xiaozhenkeji │ │ │ └── androidlivepusher │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── yxt │ └── xiaozhenkeji │ └── androidlivepusher │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── livepusher ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── yxt │ │ └── livepusher │ │ └── livepusher │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── AndroidLog.h │ │ ├── CallJava.cpp │ │ ├── CallJava.h │ │ ├── MessageQueue.cpp │ │ ├── MessageQueue.h │ │ ├── RtmpPush.cpp │ │ ├── RtmpPush.h │ │ ├── librtmp │ │ │ ├── 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 │ │ └── push.cpp │ ├── java │ │ └── com │ │ │ └── yxt │ │ │ └── livepusher │ │ │ ├── camera │ │ │ ├── CameraFboRender.java │ │ │ ├── CameraRender.java │ │ │ ├── CameraView.java │ │ │ └── YUCamera.java │ │ │ ├── egl │ │ │ ├── BaseRendeer.java │ │ │ ├── EglHelper.java │ │ │ └── YUEGLSurfaceView.java │ │ │ ├── encodec │ │ │ ├── BasePushEncoder.java │ │ │ ├── BaseVideoEncoder.java │ │ │ ├── EncodecRender.java │ │ │ ├── MP4VideoExtractor.java │ │ │ ├── PushEncoder.java │ │ │ └── RecordEncoder.java │ │ │ ├── network │ │ │ ├── NetSocket.java │ │ │ └── rtmp │ │ │ │ ├── ConnectListenr.java │ │ │ │ ├── RtmpPush.java │ │ │ │ └── RtmpPushBack.java │ │ │ ├── test │ │ │ ├── CameraGLRender.java │ │ │ ├── EGLSurface.java │ │ │ └── YxtStream.java │ │ │ ├── utils │ │ │ ├── AudioRecordUitl.java │ │ │ ├── FLV.java │ │ │ ├── G711Code.java │ │ │ ├── MessageConversionUtils.java │ │ │ └── ShaderUtils.java │ │ │ └── view │ │ │ └── AutoFitTextureView.java │ └── res │ │ ├── raw │ │ ├── fragment_shader.glsl │ │ ├── fragment_shader_screen.glsl │ │ ├── vertex_shader.glsl │ │ └── vertex_shader_screen.glsl │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── yxt │ └── livepusher │ └── livepusher │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | # Uncomment the following line if you do not want to check your keystore files in. 45 | #*.jks 46 | 47 | # External native build folder generated in Android Studio 2.2 and later 48 | .externalNativeBuild 49 | 50 | # Google Services (e.g. APIs or Firebase) 51 | google-services.json 52 | 53 | # Freeline 54 | freeline.py 55 | freeline/ 56 | freeline_project_description.json 57 | 58 | # fastlane 59 | fastlane/report.xml 60 | fastlane/Preview.html 61 | fastlane/screenshots 62 | fastlane/test_output 63 | fastlane/readme.md 64 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | xmlns:android 11 | 12 | ^$ 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | xmlns:.* 22 | 23 | ^$ 24 | 25 | 26 | BY_NAME 27 | 28 |
29 |
30 | 31 | 32 | 33 | .*:id 34 | 35 | http://schemas.android.com/apk/res/android 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | .*:name 45 | 46 | http://schemas.android.com/apk/res/android 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | name 56 | 57 | ^$ 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | style 67 | 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | .* 78 | 79 | ^$ 80 | 81 | 82 | BY_NAME 83 | 84 |
85 |
86 | 87 | 88 | 89 | .* 90 | 91 | http://schemas.android.com/apk/res/android 92 | 93 | 94 | ANDROID_ATTRIBUTE_ORDER 95 | 96 |
97 |
98 | 99 | 100 | 101 | .* 102 | 103 | .* 104 | 105 | 106 | BY_NAME 107 | 108 |
109 |
110 |
111 |
112 |
113 |
-------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 项目使用ndk版本为r14b编译 新版ndk会报错 2 | 3 | 感谢 [ywl5320](https://github.com/wanliyang1990) 4 | 5 | 本项目是openGL+mediacodec硬编码摄像头数据,目前支持: 6 | 7 | 8 | 1:rtmp推流 9 | 10 | 2:h264获取 11 | 12 | 3:修改帧率码率,切换摄像头 13 | 14 | 4:从openGL中截图 15 | 16 | 5:不预览摄像头获取摄像头数据,支持不预览拍照和不预览录像,不预览推流 17 | 18 | 6:可以使用MP4VideoExtractor 来分离mp4成aac+h264 进行推流 19 | 20 | 7:mp4录制 21 | 22 | 8:FLV录制工具已经上传过段时间更新代码实现flv视频录制。 23 | 24 | 另外,CSDN博客过来看FLV的朋友请点击[这里](https://github.com/yuxitong/AndroidLivePusher/blob/master/livepusher/src/main/java/com/yxt/livepusher/utils/FLV.java) 25 | 26 | 27 | 后续如果有空就会添加 ffmpeg软编上述这些功能 和flv的录制。 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | src/main/cpp/native-lib.cpp) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log) 34 | 35 | # Specifies libraries CMake should link to your target library. You 36 | # can link multiple libraries, such as libraries you define in this 37 | # build script, prebuilt third-party libraries, or system libraries. 38 | 39 | target_link_libraries( # Specifies the target library. 40 | native-lib 41 | 42 | # Links the target library to the log library 43 | # included in the NDK. 44 | ${log-lib}) -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.yxt.xiaozhenkeji.androidlivepusher" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | externalNativeBuild { 13 | cmake { 14 | cppFlags "" 15 | } 16 | } 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | externalNativeBuild { 25 | cmake { 26 | path "CMakeLists.txt" 27 | } 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(include: ['*.jar'], dir: 'libs') 33 | implementation 'com.android.support:appcompat-v7:28.+' 34 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 38 | implementation project(':livepusher') 39 | } 40 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/yxt/xiaozhenkeji/androidlivepusher/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.yxt.xiaozhenkeji.androidlivepusher; 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 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.yxt.xiaozhenkeji.androidlivepusher", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" JNIEXPORT jstring JNICALL 5 | Java_com_yxt_xiaozhenkeji_androidlivepusher_MainActivity_stringFromJNI( 6 | JNIEnv *env, 7 | jobject /* this */) { 8 | std::string hello = "Hello from C++"; 9 | return env->NewStringUTF(hello.c_str()); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/yxt/xiaozhenkeji/androidlivepusher/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yxt.xiaozhenkeji.androidlivepusher; 2 | 3 | import android.app.Activity; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.Bitmap; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | import android.support.v4.content.ContextCompat; 10 | import android.view.SurfaceView; 11 | import android.widget.TextView; 12 | 13 | import com.yxt.livepusher.test.CameraGLRender; 14 | import com.yxt.livepusher.test.YxtStream; 15 | 16 | public class MainActivity extends Activity { 17 | private static final int PERMISSIONS_REQUEST_CODE = 1; 18 | YxtStream yxt; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | //权限申请 25 | if (!allPermissionsGranted()) { 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 27 | requestPermissions(getRequiredPermissions(), PERMISSIONS_REQUEST_CODE); 28 | } 29 | return; 30 | } 31 | 32 | 33 | //带预览摄像头 34 | yxt = new YxtStream((SurfaceView) findViewById(R.id.surfaceView), this); 35 | //不带预览摄像头 36 | // yxt = new YxtStream(this); 37 | 38 | //设置前后摄像头 39 | yxt.setCameraFacing(YxtStream.FACING_BACK); 40 | //设置帧率 41 | yxt.setFps(15); 42 | 43 | 44 | // yxt.setWidthAndHeight(480, 640); 45 | //开始执行 46 | yxt.star(); 47 | 48 | //开始录制mp4 49 | // yxt.startRecord("录制地址+文件名"); 50 | //停止录制 51 | // yxt.stopRecord(); 52 | 53 | //开始推送rtmp 54 | // yxt.startRtmp(0,"rtmp地址"); 55 | //停止推送rtmp 56 | // yxt.stopRtmpStream(); 57 | 58 | 59 | //截图 60 | // yxt.getCameraRender().requestScreenShot(new CameraGLRender.ScreenShotListener() { 61 | // @Override 62 | // public void onBitmapAvailable(Bitmap bitmap) { 63 | // 64 | // } 65 | // }); 66 | 67 | 68 | } 69 | 70 | private String[] getRequiredPermissions() { 71 | Activity activity = this; 72 | try { 73 | PackageInfo info = 74 | activity 75 | .getPackageManager() 76 | .getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); 77 | String[] ps = info.requestedPermissions; 78 | if (ps != null && ps.length > 0) { 79 | return ps; 80 | } else { 81 | return new String[0]; 82 | } 83 | } catch (Exception e) { 84 | return new String[0]; 85 | } 86 | } 87 | 88 | private boolean allPermissionsGranted() { 89 | for (String permission : getRequiredPermissions()) { 90 | if (ContextCompat.checkSelfPermission(this, permission) 91 | != PackageManager.PERMISSION_GRANTED) { 92 | return false; 93 | } 94 | } 95 | return true; 96 | } 97 | 98 | @Override 99 | protected void onDestroy() { 100 | yxt.onDestory(); 101 | super.onDestroy(); 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidLivePusher 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/yxt/xiaozhenkeji/androidlivepusher/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.yxt.xiaozhenkeji.androidlivepusher; 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 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.2.1' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuxitong/AndroidLivePusher/83dcf8e19d7c564e125cd7613e3b225301ebc238/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /livepusher/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /livepusher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.4.1) 3 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_CRYPTO") 4 | 5 | add_library( # Sets the name of the library. 6 | push 7 | # Sets the library as a shared library. 8 | SHARED 9 | 10 | # Provides a relative path to your source file(s). 11 | src/main/cpp/push.cpp 12 | src/main/cpp/MessageQueue.cpp 13 | src/main/cpp/RtmpPush.cpp 14 | src/main/cpp/CallJava.cpp 15 | 16 | src/main/cpp/librtmp/amf.c 17 | src/main/cpp/librtmp/hashswf.c 18 | src/main/cpp/librtmp/log.c 19 | src/main/cpp/librtmp/parseurl.c 20 | src/main/cpp/librtmp/rtmp.c 21 | 22 | ) 23 | 24 | 25 | find_library( # Sets the name of the path variable. 26 | log-lib 27 | 28 | # Specifies the name of the NDK library that 29 | # you want CMake to locate. 30 | log) 31 | 32 | 33 | target_link_libraries( # Specifies the target library. 34 | push 35 | ${log-lib}) 36 | -------------------------------------------------------------------------------- /livepusher/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | 7 | 8 | defaultConfig { 9 | minSdkVersion 21 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | externalNativeBuild { 15 | cmake { 16 | cppFlags "-frtti -fexceptions" 17 | abiFilters "armeabi-v7a" 18 | } 19 | } 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | externalNativeBuild { 29 | cmake { 30 | path "CMakeLists.txt" 31 | } 32 | } 33 | 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(dir: 'libs', include: ['*.jar']) 38 | 39 | implementation 'com.android.support:appcompat-v7:28.+' 40 | testImplementation 'junit:junit:4.12' 41 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 42 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 43 | } 44 | -------------------------------------------------------------------------------- /livepusher/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /livepusher/src/androidTest/java/com/yxt/livepusher/livepusher/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.livepusher; 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 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.yxt.xiaozhenkeji.livepusher.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /livepusher/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/AndroidLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by ywl on 2017-11-12. 3 | // 4 | #pragma once 5 | #ifndef WLPLAYER_ANDROIDLOG_H 6 | #define WLPLAYER_ANDROIDLOG_H 7 | 8 | #include 9 | 10 | #define LOG_SHOW true 11 | 12 | #define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG,"yxt",FORMAT,##__VA_ARGS__); 13 | #define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"yxt",FORMAT,##__VA_ARGS__); 14 | 15 | #endif //WLPLAYER_ANDROIDLOG_H 16 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/CallJava.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | #include "CallJava.h" 6 | 7 | CallJava::CallJava(JavaVM *javaVM, JNIEnv *jniEnv, jobject *jobj) { 8 | 9 | this->javaVM = javaVM; 10 | this->jniEnv = jniEnv; 11 | this->jobj = jniEnv->NewGlobalRef(*jobj); 12 | 13 | jclass jlz = jniEnv->GetObjectClass(this->jobj); 14 | 15 | jmid_connecting =jniEnv->GetMethodID(jlz, "onConnecting", "()V"); 16 | jmid_connectsuccess = jniEnv->GetMethodID(jlz, "onConnectSuccess", "()V"); 17 | jmid_connectfail = jniEnv->GetMethodID(jlz, "onConnectFial", "(Ljava/lang/String;)V"); 18 | } 19 | 20 | CallJava::~CallJava() { 21 | jniEnv->DeleteGlobalRef(jobj); 22 | javaVM = NULL; 23 | jniEnv = NULL; 24 | } 25 | 26 | void CallJava::onConnectint(int type) { 27 | 28 | if(type == WL_THREAD_CHILD) 29 | { 30 | JNIEnv *jniEnv; 31 | if(javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) 32 | { 33 | return; 34 | } 35 | jniEnv->CallVoidMethod(jobj, jmid_connecting); 36 | javaVM->DetachCurrentThread(); 37 | } 38 | else 39 | { 40 | jniEnv->CallVoidMethod(jobj, jmid_connecting); 41 | } 42 | } 43 | 44 | void CallJava::onConnectsuccess() { 45 | JNIEnv *jniEnv; 46 | if(javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) 47 | { 48 | return; 49 | } 50 | jniEnv->CallVoidMethod(jobj, jmid_connectsuccess); 51 | javaVM->DetachCurrentThread(); 52 | } 53 | 54 | void CallJava::onConnectFail(char *msg) { 55 | 56 | JNIEnv *jniEnv; 57 | if(javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) 58 | { 59 | return; 60 | } 61 | 62 | jstring jmsg = jniEnv->NewStringUTF(msg); 63 | 64 | jniEnv->CallVoidMethod(jobj, jmid_connectfail, jmsg); 65 | 66 | jniEnv->DeleteLocalRef(jmsg); 67 | javaVM->DetachCurrentThread(); 68 | } 69 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/CallJava.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | #include 6 | #include "jni.h" 7 | 8 | #ifndef WLLIVEPUSHER_WLCALLJAVA_H 9 | #define WLLIVEPUSHER_WLCALLJAVA_H 10 | 11 | #define WL_THREAD_MAIN 1 12 | #define WL_THREAD_CHILD 2 13 | 14 | class CallJava { 15 | 16 | public: 17 | 18 | JNIEnv *jniEnv = NULL; 19 | JavaVM *javaVM = NULL; 20 | jobject jobj; 21 | 22 | jmethodID jmid_connecting; 23 | jmethodID jmid_connectsuccess; 24 | jmethodID jmid_connectfail; 25 | 26 | 27 | public: 28 | CallJava(JavaVM *javaVM, JNIEnv *jniEnv, jobject *jobj); 29 | ~CallJava(); 30 | 31 | void onConnectint(int type); 32 | 33 | void onConnectsuccess(); 34 | 35 | void onConnectFail(char *msg); 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | }; 46 | 47 | 48 | #endif //WLLIVEPUSHER_WLCALLJAVA_H 49 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/MessageQueue.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | #include "MessageQueue.h" 6 | 7 | MessageQueue::MessageQueue() { 8 | pthread_mutex_init(&mutexPacket, NULL); 9 | pthread_cond_init(&condPacket, NULL); 10 | } 11 | 12 | MessageQueue::~MessageQueue() { 13 | clearQueue(); 14 | pthread_mutex_destroy(&mutexPacket); 15 | pthread_cond_destroy(&condPacket); 16 | 17 | } 18 | 19 | int MessageQueue::putRtmpPacket(RTMPPacket *packet) { 20 | pthread_mutex_lock(&mutexPacket); 21 | queuePacket.push(packet); 22 | pthread_cond_signal(&condPacket); 23 | pthread_mutex_unlock(&mutexPacket); 24 | return 0; 25 | } 26 | 27 | RTMPPacket *MessageQueue::getRtmpPacket() { 28 | pthread_mutex_lock(&mutexPacket); 29 | 30 | RTMPPacket *p = NULL; 31 | if(!queuePacket.empty()) 32 | { 33 | p = queuePacket.front(); 34 | queuePacket.pop(); 35 | } else{ 36 | pthread_cond_wait(&condPacket, &mutexPacket); 37 | } 38 | pthread_mutex_unlock(&mutexPacket); 39 | return p; 40 | } 41 | 42 | void MessageQueue::clearQueue() { 43 | 44 | pthread_mutex_lock(&mutexPacket); 45 | while(true) 46 | { 47 | if(queuePacket.empty()) 48 | { 49 | break; 50 | } 51 | RTMPPacket *p = queuePacket.front(); 52 | queuePacket.pop(); 53 | RTMPPacket_Free(p); 54 | p = NULL; 55 | } 56 | pthread_mutex_unlock(&mutexPacket); 57 | 58 | } 59 | 60 | void MessageQueue::notifyQueue() { 61 | 62 | pthread_mutex_lock(&mutexPacket); 63 | pthread_cond_signal(&condPacket); 64 | pthread_mutex_unlock(&mutexPacket); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/MessageQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | #ifndef WLLIVEPUSHER_WLQUEUE_H 6 | #define WLLIVEPUSHER_WLQUEUE_H 7 | 8 | #include "queue" 9 | #include "pthread.h" 10 | #include "AndroidLog.h" 11 | 12 | extern "C" 13 | { 14 | #include "librtmp/rtmp.h" 15 | }; 16 | 17 | 18 | class MessageQueue { 19 | 20 | public: 21 | std::queue queuePacket; 22 | pthread_mutex_t mutexPacket; 23 | pthread_cond_t condPacket; 24 | 25 | public: 26 | MessageQueue(); 27 | ~MessageQueue(); 28 | 29 | int putRtmpPacket(RTMPPacket *packet); 30 | 31 | RTMPPacket* getRtmpPacket(); 32 | 33 | void clearQueue(); 34 | 35 | void notifyQueue(); 36 | 37 | 38 | }; 39 | 40 | 41 | #endif //WLLIVEPUSHER_WLQUEUE_H 42 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/RtmpPush.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | 6 | 7 | #include "RtmpPush.h" 8 | 9 | RtmpPush::RtmpPush(const char *url, CallJava *wlCallJava) { 10 | this->url = static_cast(malloc(512)); 11 | strcpy(this->url, url); 12 | this->queue = new MessageQueue(); 13 | this->wlCallJava = wlCallJava; 14 | } 15 | 16 | RtmpPush::~RtmpPush() { 17 | queue->notifyQueue(); 18 | queue->clearQueue(); 19 | free(url); 20 | } 21 | 22 | void *callBackPush(void *data) { 23 | 24 | RtmpPush *rtmpPush = static_cast(data); 25 | rtmpPush->startPushing = false; 26 | rtmpPush->rtmp = RTMP_Alloc(); 27 | RTMP_Init(rtmpPush->rtmp); 28 | rtmpPush->rtmp->Link.timeout = 10; 29 | rtmpPush->rtmp->Link.lFlags |= RTMP_LF_LIVE; 30 | RTMP_SetupURL(rtmpPush->rtmp, rtmpPush->url); 31 | RTMP_EnableWrite(rtmpPush->rtmp); 32 | 33 | if (!RTMP_Connect(rtmpPush->rtmp, NULL)) { 34 | // LOGE("can not connect the url"); 35 | rtmpPush->wlCallJava->onConnectFail("can not connect the url"); 36 | goto end; 37 | } 38 | if (!RTMP_ConnectStream(rtmpPush->rtmp, 0)) { 39 | 40 | rtmpPush->wlCallJava->onConnectFail("can not connect the stream of service"); 41 | goto end; 42 | } 43 | rtmpPush->wlCallJava->onConnectsuccess(); 44 | rtmpPush->startPushing = true; 45 | rtmpPush->startTime = RTMP_GetTime(); 46 | while (true) { 47 | if (!rtmpPush->startPushing) { 48 | break; 49 | } 50 | 51 | RTMPPacket *packet = NULL; 52 | packet = rtmpPush->queue->getRtmpPacket(); 53 | if (packet != NULL) { 54 | int result = RTMP_SendPacket(rtmpPush->rtmp, packet, 1); 55 | LOGD("RTMP_SendPacket result is %d", result); 56 | RTMPPacket_Free(packet); 57 | free(packet); 58 | packet = NULL; 59 | } else { 60 | LOGD("RTMP_SendPacket else %d", 123); 61 | 62 | } 63 | } 64 | end: 65 | RTMP_Close(rtmpPush->rtmp); 66 | RTMP_Free(rtmpPush->rtmp); 67 | rtmpPush->rtmp = NULL; 68 | pthread_exit(&rtmpPush->push_thread); 69 | } 70 | 71 | void RtmpPush::init() { 72 | wlCallJava->onConnectint(WL_THREAD_MAIN); 73 | pthread_create(&push_thread, NULL, callBackPush, this); 74 | 75 | } 76 | 77 | void RtmpPush::pushSPSPPS(char *sps, int sps_len, char *pps, int pps_len) { 78 | 79 | int bodysize = sps_len + pps_len + 16; 80 | RTMPPacket *packet = static_cast(malloc(sizeof(RTMPPacket))); 81 | RTMPPacket_Alloc(packet, bodysize); 82 | RTMPPacket_Reset(packet); 83 | 84 | char *body = packet->m_body; 85 | 86 | int i = 0; 87 | 88 | body[i++] = 0x17; 89 | 90 | body[i++] = 0x00; 91 | body[i++] = 0x00; 92 | body[i++] = 0x00; 93 | body[i++] = 0x00; 94 | 95 | body[i++] = 0x01; 96 | body[i++] = sps[1]; 97 | body[i++] = sps[2]; 98 | body[i++] = sps[3]; 99 | 100 | body[i++] = 0xFF; 101 | 102 | body[i++] = 0xE1; 103 | body[i++] = (sps_len >> 8) & 0xff; 104 | body[i++] = sps_len & 0xff; 105 | memcpy(&body[i], sps, sps_len); 106 | i += sps_len; 107 | 108 | body[i++] = 0x01; 109 | body[i++] = (pps_len >> 8) & 0xff; 110 | body[i++] = pps_len & 0xff; 111 | memcpy(&body[i], pps, pps_len); 112 | 113 | packet->m_packetType = RTMP_PACKET_TYPE_VIDEO; 114 | packet->m_nBodySize = bodysize; 115 | packet->m_nTimeStamp = 0; 116 | packet->m_hasAbsTimestamp = 0; 117 | packet->m_nChannel = 0x04; 118 | packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM; 119 | packet->m_nInfoField2 = rtmp->m_stream_id; 120 | 121 | queue->putRtmpPacket(packet); 122 | } 123 | 124 | void RtmpPush::pushVideoData(char *data, int data_len, bool keyframe) { 125 | 126 | int bodysize = data_len + 9; 127 | RTMPPacket *packet = static_cast(malloc(sizeof(RTMPPacket))); 128 | RTMPPacket_Alloc(packet, bodysize); 129 | RTMPPacket_Reset(packet); 130 | 131 | char *body = packet->m_body; 132 | int i = 0; 133 | 134 | if (keyframe) { 135 | body[i++] = 0x17; 136 | } else { 137 | body[i++] = 0x27; 138 | } 139 | 140 | body[i++] = 0x01; 141 | body[i++] = 0x00; 142 | body[i++] = 0x00; 143 | body[i++] = 0x00; 144 | 145 | body[i++] = (data_len >> 24) & 0xff; 146 | body[i++] = (data_len >> 16) & 0xff; 147 | body[i++] = (data_len >> 8) & 0xff; 148 | body[i++] = data_len & 0xff; 149 | memcpy(&body[i], data, data_len); 150 | 151 | packet->m_packetType = RTMP_PACKET_TYPE_VIDEO; 152 | packet->m_nBodySize = bodysize; 153 | packet->m_nTimeStamp = RTMP_GetTime() - startTime; 154 | packet->m_hasAbsTimestamp = 0; 155 | packet->m_nChannel = 0x04; 156 | packet->m_headerType = RTMP_PACKET_SIZE_LARGE; 157 | packet->m_nInfoField2 = rtmp->m_stream_id; 158 | 159 | queue->putRtmpPacket(packet); 160 | } 161 | 162 | void RtmpPush::pushAudioData(char *data, int data_len) { 163 | 164 | int bodysize = data_len + 2; 165 | RTMPPacket *packet = static_cast(malloc(sizeof(RTMPPacket))); 166 | RTMPPacket_Alloc(packet, bodysize); 167 | RTMPPacket_Reset(packet); 168 | char *body = packet->m_body; 169 | body[0] = 0xAF; 170 | body[1] = 0x01; 171 | memcpy(&body[2], data, data_len); 172 | 173 | packet->m_packetType = RTMP_PACKET_TYPE_AUDIO; 174 | packet->m_nBodySize = bodysize; 175 | packet->m_nTimeStamp = RTMP_GetTime() - startTime; 176 | packet->m_hasAbsTimestamp = 0; 177 | packet->m_nChannel = 0x04; 178 | packet->m_headerType = RTMP_PACKET_SIZE_LARGE; 179 | packet->m_nInfoField2 = rtmp->m_stream_id; 180 | queue->putRtmpPacket(packet); 181 | } 182 | 183 | void RtmpPush::pushStop() { 184 | startPushing = false; 185 | queue->notifyQueue(); 186 | pthread_join(push_thread, NULL); 187 | } 188 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/RtmpPush.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yangw on 2018-9-14. 3 | // 4 | 5 | #ifndef WLLIVEPUSHER_RTMPPUSH_H 6 | #define WLLIVEPUSHER_RTMPPUSH_H 7 | 8 | #include 9 | #include 10 | #include "MessageQueue.h" 11 | #include "pthread.h" 12 | #include "CallJava.h" 13 | 14 | extern "C" 15 | { 16 | #include "librtmp/rtmp.h" 17 | }; 18 | 19 | class RtmpPush { 20 | 21 | public: 22 | RTMP *rtmp = NULL; 23 | char *url = NULL; 24 | MessageQueue *queue = NULL; 25 | pthread_t push_thread; 26 | CallJava *wlCallJava = NULL; 27 | bool startPushing = false; 28 | long startTime = 0; 29 | public: 30 | RtmpPush(const char *url, CallJava *wlCallJava); 31 | ~RtmpPush(); 32 | 33 | void init(); 34 | 35 | void pushSPSPPS(char *sps, int sps_len, char *pps, int pps_len); 36 | 37 | void pushVideoData(char *data, int data_len, bool keyframe); 38 | 39 | void pushAudioData(char *data, int data_len); 40 | 41 | void pushStop(); 42 | 43 | 44 | 45 | }; 46 | 47 | 48 | #endif //WLLIVEPUSHER_RTMPPUSH_H 49 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/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 | -------------------------------------------------------------------------------- /livepusher/src/main/cpp/push.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "RtmpPush.h" 5 | #include "CallJava.h" 6 | 7 | RtmpPush *rtmpPush = NULL; 8 | CallJava *wlCallJava = NULL; 9 | JavaVM *javaVM = NULL; 10 | bool exit = true; 11 | 12 | extern "C" 13 | JNIEXPORT void JNICALL 14 | Java_com_yxt_livepusher_network_rtmp_RtmpPush_initPush(JNIEnv *env, jobject instance, 15 | jstring pushUrl_) { 16 | const char *pushUrl = env->GetStringUTFChars(pushUrl_, 0); 17 | 18 | // TODO 19 | if (wlCallJava == NULL) { 20 | exit = false; 21 | wlCallJava = new CallJava(javaVM, env, &instance); 22 | rtmpPush = new RtmpPush(pushUrl, wlCallJava); 23 | rtmpPush->init(); 24 | } 25 | env->ReleaseStringUTFChars(pushUrl_, pushUrl); 26 | } 27 | 28 | 29 | extern "C" 30 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 31 | javaVM = vm; 32 | JNIEnv *env; 33 | if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { 34 | if (LOG_SHOW) { 35 | LOGE("GetEnv failed!"); 36 | } 37 | return -1; 38 | } 39 | return JNI_VERSION_1_4; 40 | } 41 | 42 | extern "C" 43 | JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { 44 | javaVM = NULL; 45 | } 46 | 47 | extern "C" 48 | JNIEXPORT void JNICALL 49 | Java_com_yxt_livepusher_network_rtmp_RtmpPush_pushSPSPPS(JNIEnv *env, jobject instance, 50 | jbyteArray sps_, jint sps_len, 51 | jbyteArray pps_, jint pps_len) { 52 | jbyte *sps = env->GetByteArrayElements(sps_, NULL); 53 | jbyte *pps = env->GetByteArrayElements(pps_, NULL); 54 | 55 | // TODO 56 | if (rtmpPush != NULL && !exit) { 57 | rtmpPush->pushSPSPPS(reinterpret_cast(sps), sps_len, reinterpret_cast(pps), 58 | pps_len); 59 | } 60 | 61 | env->ReleaseByteArrayElements(sps_, sps, 0); 62 | env->ReleaseByteArrayElements(pps_, pps, 0); 63 | } 64 | 65 | extern "C" 66 | JNIEXPORT void JNICALL 67 | Java_com_yxt_livepusher_network_rtmp_RtmpPush_pushVideoData(JNIEnv *env, jobject instance, 68 | jbyteArray data_, jint data_len, 69 | jboolean keyframe) { 70 | jbyte *data = env->GetByteArrayElements(data_, NULL); 71 | // TODO 72 | if (rtmpPush != NULL && !exit) { 73 | rtmpPush->pushVideoData(reinterpret_cast(data), data_len, keyframe); 74 | } 75 | env->ReleaseByteArrayElements(data_, data, 0); 76 | } 77 | 78 | extern "C" 79 | JNIEXPORT void JNICALL 80 | Java_com_yxt_livepusher_network_rtmp_RtmpPush_pushAudioData(JNIEnv *env, jobject instance, 81 | jbyteArray data_, jint data_len) { 82 | jbyte *data = env->GetByteArrayElements(data_, NULL); 83 | 84 | // TODO 85 | if (rtmpPush != NULL && !exit) { 86 | rtmpPush->pushAudioData(reinterpret_cast(data), data_len); 87 | } 88 | 89 | env->ReleaseByteArrayElements(data_, data, 0); 90 | } 91 | 92 | extern "C" 93 | JNIEXPORT void JNICALL 94 | Java_com_yxt_livepusher_network_rtmp_RtmpPush_pushStop(JNIEnv *env, jobject instance) { 95 | 96 | // TODO 97 | if (rtmpPush != NULL) { 98 | exit = true; 99 | rtmpPush->pushStop(); 100 | delete (rtmpPush); 101 | delete (wlCallJava); 102 | rtmpPush = NULL; 103 | wlCallJava = NULL; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/camera/CameraFboRender.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.camera; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.opengl.GLES20; 6 | 7 | import com.yxt.livepusher.egl.BaseRendeer; 8 | import com.yxt.livepusher.test.CameraGLRender; 9 | import com.yxt.livepusher.utils.ShaderUtils; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.ByteOrder; 13 | 14 | public class CameraFboRender extends BaseRendeer { 15 | 16 | 17 | 18 | public CameraFboRender(Context context) { 19 | super(context); 20 | } 21 | 22 | public void onCreate() { 23 | super.onCreate(); 24 | } 25 | 26 | public void onChange(int width, int height) { 27 | super.onChange(width, height); 28 | } 29 | 30 | public void onDrawFrame(int textureId) { 31 | super.onDrawFrame(textureId); 32 | 33 | } 34 | 35 | // public boolean isRequestScreenShot() { 36 | // return this.screenShotListener == null; 37 | // } 38 | // 39 | // private void sendImage(final int width, final int height) { 40 | // final ByteBuffer rgbaBuf = ByteBuffer.allocateDirect(width * height * 4); 41 | // rgbaBuf.order(ByteOrder.LITTLE_ENDIAN); 42 | // GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, 43 | // rgbaBuf); 44 | // rgbaBuf.rewind(); 45 | // screenShotThread = new Thread(new Runnable() { 46 | // @Override 47 | // public void run() { 48 | // Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 49 | //// bitmap.copyPixelsToBuffer(rgbaBuf); 50 | // bitmap.copyPixelsFromBuffer(rgbaBuf); 51 | // android.graphics.Matrix matrixBitmap = new android.graphics.Matrix(); 52 | // matrixBitmap.preScale(1.0F, -1.0F); 53 | // Bitmap normalBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrixBitmap, true); 54 | // if (screenShotListener != null) 55 | // screenShotListener.onBitmapAvailable(normalBitmap); 56 | // bitmap.recycle(); 57 | // bitmap = null; 58 | // normalBitmap.recycle(); 59 | // normalBitmap = null; 60 | // } 61 | // }); 62 | // screenShotThread.setPriority(Thread.MAX_PRIORITY);//设置最大的线程优先级 63 | // screenShotThread.start(); 64 | // } 65 | // 66 | // public void requestScreenShot(boolean requestScreenBitmap, ScreenShotListener screenShotListener) { 67 | // this.screenShotListener = screenShotListener; 68 | // this.requestScreenBitmap = requestScreenBitmap; 69 | // } 70 | // 71 | // 72 | // public interface ScreenShotListener { 73 | // void onBitmapAvailable(Bitmap bitmap); 74 | // } 75 | // 76 | // public void requestScreenShot(ScreenShotListener screenShotListener) { 77 | // requestScreenShot(true, screenShotListener); 78 | // } 79 | // 80 | // public void setRequestScreenBitmap(boolean requestScreenBitmap) { 81 | // this.requestScreenBitmap = requestScreenBitmap; 82 | // } 83 | // 84 | // void release(){ 85 | // if(screenShotThread !=null&&screenShotThread.isAlive()) 86 | // 87 | // { 88 | // screenShotThread.interrupt(); 89 | // screenShotThread = null; 90 | // } 91 | // } 92 | } -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/camera/CameraView.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.camera; 2 | 3 | import android.content.Context; 4 | import android.graphics.SurfaceTexture; 5 | import android.hardware.Camera; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.Surface; 9 | import android.view.WindowManager; 10 | 11 | import com.yxt.livepusher.egl.YUEGLSurfaceView; 12 | 13 | import javax.microedition.khronos.egl.EGLContext; 14 | 15 | public class CameraView extends YUEGLSurfaceView { 16 | private CameraRender cameraRender; 17 | private YUCamera yuCamera; 18 | 19 | private int cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT; 20 | 21 | private int textureId = -1; 22 | 23 | public CameraView(Context context) { 24 | this(context, null); 25 | } 26 | 27 | public CameraView(Context context, AttributeSet attrs) { 28 | this(context, attrs, 0); 29 | } 30 | 31 | public CameraView(Context context, AttributeSet attrs, int defStyleAttr) { 32 | this(context, attrs, defStyleAttr, 0); 33 | } 34 | 35 | public OnSurfaceCreate onSurfaceCreate; 36 | 37 | public CameraView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 38 | super(context, attrs, defStyleAttr, defStyleRes); 39 | start(640, 480); 40 | 41 | } 42 | 43 | 44 | // @Override 45 | // protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 46 | // super.onLayout(changed, left, top, right, bottom); 47 | // start(this.getWidth(), this.getHeight()); 48 | // 49 | // } 50 | 51 | private void start(int width, int height) { 52 | if (cameraRender == null) { 53 | cameraRender = new CameraRender(this.getContext(), width, height); 54 | cameraRender.setInteractive(new CameraRender.Interactive() { 55 | @Override 56 | public void refresh() { 57 | CameraView.this.requestRender(); 58 | } 59 | }); 60 | setRender(cameraRender); 61 | yuCamera = new YUCamera(width, height); 62 | prevewAngle(this.getContext()); 63 | cameraRender.setOnSurfaceCreateListener(new CameraRender.OnSurfaceCreateListener() { 64 | @Override 65 | public void onSurfaceCreate(SurfaceTexture surfaceTexture, int textureId) { 66 | yuCamera.initCamera(surfaceTexture, cameraId); 67 | CameraView.this.textureId = textureId; 68 | if (onSurfaceCreate != null) 69 | onSurfaceCreate.onSurfaceCreate(textureId, getEglContext()); 70 | } 71 | }); 72 | } 73 | } 74 | 75 | public void setOnSurfaceCreate(OnSurfaceCreate onSurfaceCreate) { 76 | this.onSurfaceCreate = onSurfaceCreate; 77 | } 78 | 79 | public void prevewAngle(Context context) { 80 | int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); 81 | Log.e("angleTotal", " " + angle); 82 | // cameraRender.restMatrix(); 83 | switch (angle) { 84 | case Surface.ROTATION_0: 85 | Log.e("angle", "0"); 86 | if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { 87 | // cameraRender.setAngle(90, 0, 0, 1); 88 | cameraRender.setAngle(180, 0, 0, 1); 89 | Log.e("aaaa", "aaaaa"); 90 | // cameraRender.setAngle(180, 1, 0, 0); 91 | 92 | } else { 93 | Log.e("aaaa", "bbbbb"); 94 | cameraRender.setAngle(180, 0, 0, 1); 95 | // cameraRender.setAngle(90, 0, 0, 1); 96 | } 97 | break; 98 | case Surface.ROTATION_90: 99 | Log.e("angle", "90"); 100 | if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { 101 | cameraRender.setAngle(180, 0, 0, 1); 102 | cameraRender.setAngle(180, 0, 1, 0); 103 | } else { 104 | cameraRender.setAngle(180, 0, 0, 1); 105 | 106 | } 107 | break; 108 | case Surface.ROTATION_180: 109 | Log.e("angle", "180"); 110 | if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { 111 | cameraRender.setAngle(90, 0, 0, 1); 112 | cameraRender.setAngle(180, 0, 1, 0); 113 | } else { 114 | cameraRender.setAngle(-90, 0, 0, 1); 115 | } 116 | break; 117 | case Surface.ROTATION_270: 118 | Log.e("angle", "270"); 119 | if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) { 120 | cameraRender.setAngle(180, 0, 1, 0); 121 | } 122 | break; 123 | } 124 | } 125 | 126 | public int getTextureId() { 127 | return textureId; 128 | } 129 | 130 | 131 | public CameraRender getCameraRender() { 132 | return cameraRender; 133 | } 134 | 135 | public interface OnSurfaceCreate { 136 | void onSurfaceCreate(int textureId, EGLContext eglContext); 137 | } 138 | 139 | @Override 140 | protected void onDetachedFromWindow() { 141 | super.onDetachedFromWindow(); 142 | cameraRender.release(); 143 | if (yuCamera != null) { 144 | yuCamera.stopPreview(); 145 | } 146 | 147 | } 148 | 149 | public int getCameraOrient() { 150 | return cameraId; 151 | } 152 | 153 | public void setCameraOrient(int cameraId) { 154 | this.cameraId = cameraId; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/camera/YUCamera.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.camera; 2 | 3 | import android.content.Context; 4 | import android.graphics.ImageFormat; 5 | import android.graphics.SurfaceTexture; 6 | import android.hardware.Camera; 7 | import android.util.Log; 8 | import android.view.Display; 9 | import android.view.WindowManager; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | public class YUCamera { 15 | 16 | private SurfaceTexture surfaceTexture; 17 | 18 | private Camera camera; 19 | 20 | private int width; 21 | private int height; 22 | 23 | public YUCamera(int width, int height) { 24 | // Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 25 | // width = display.getWidth(); 26 | // height = display.getHeight(); 27 | this.width = width; 28 | this.height = height; 29 | Log.e("width", width + " " + height); 30 | } 31 | 32 | public void setWidthAndHeight(int width, int height) { 33 | this.width = width; 34 | this.height = height; 35 | } 36 | public void initCamera(SurfaceTexture surfaceTexture, int cameraId) { 37 | this.surfaceTexture = surfaceTexture; 38 | setCameraParme(cameraId); 39 | } 40 | 41 | private void setCameraParme(int cameraId) { 42 | try { 43 | camera = Camera.open(cameraId); 44 | camera.setPreviewTexture(surfaceTexture); 45 | Camera.Parameters parameters = camera.getParameters(); 46 | parameters.setFlashMode("off"); 47 | parameters.setPreviewFormat(ImageFormat.NV21); 48 | 49 | Camera.Size size = getFitSize(parameters.getSupportedPictureSizes()); 50 | if (width > height) 51 | parameters.setPictureSize(size.width, size.height); 52 | else 53 | parameters.setPictureSize(size.width, size.height); 54 | 55 | 56 | size = getFitSize(parameters.getSupportedPreviewSizes()); 57 | if (width > height) 58 | parameters.setPreviewSize(size.width, size.height); 59 | else 60 | parameters.setPreviewSize(size.width, size.height); 61 | 62 | camera.setParameters(parameters); 63 | camera.startPreview(); 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | 69 | public void stopPreview() { 70 | if (camera != null) { 71 | camera.stopPreview(); 72 | camera.release(); 73 | camera = null; 74 | } 75 | } 76 | 77 | public void changeCamera(int cameraId) { 78 | if (camera != null) { 79 | stopPreview(); 80 | } 81 | setCameraParme(cameraId); 82 | } 83 | 84 | private Camera.Size getFitSize(List sizes) { 85 | int widtha = width; 86 | int heightt = height; 87 | if (widtha < heightt) { 88 | int t = heightt; 89 | heightt = widtha; 90 | widtha = t; 91 | } 92 | for (Camera.Size size : sizes) { 93 | if (1.0f * size.width / size.height == 1.0f * widtha / heightt) { 94 | return size; 95 | } 96 | } 97 | return sizes.get(0); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/egl/BaseRendeer.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.egl; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.opengl.GLES20; 6 | 7 | 8 | import com.yxt.livepusher.livepusher.R; 9 | import com.yxt.livepusher.utils.ShaderUtils; 10 | 11 | import java.io.IOException; 12 | import java.nio.ByteBuffer; 13 | import java.nio.ByteOrder; 14 | import java.nio.FloatBuffer; 15 | 16 | public abstract class BaseRendeer { 17 | 18 | protected Context context; 19 | protected float[] vertexData = { 20 | -1f, -1f, 21 | 1f, -1f, 22 | -1f, 1f, 23 | 1f, 1f, 24 | 25 | 26 | -1f, 0.75f, 27 | 1f, 0.75f, 28 | -1f, 1f, 29 | 1f, 1f, 30 | }; 31 | protected float[] fragmentData = { 32 | 0f, 1f, 33 | 1f, 1f, 34 | 0f, 0f, 35 | 1f, 0f 36 | }; 37 | protected FloatBuffer vertexBuffer; 38 | protected FloatBuffer fragmentBuffer; 39 | protected int program = -1; 40 | protected int vPosition; 41 | protected int fPosition; 42 | protected int sampler; 43 | protected int vboId = -1; 44 | private int bitmapTextureId = -1; 45 | private Bitmap bitmap; 46 | int vertexShader = -1; 47 | int fragmentShader = -1; 48 | 49 | public BaseRendeer(Context context) { 50 | this.context = context; 51 | 52 | 53 | // bitmap = ShaderUtils.createTextImage(15, "#ffffffff", "#4D000000", 5, "50", "沪A123456", "上海市陆家嘴金融中心金茂大厦十楼", "2019-2-5 10:13:31"); 54 | vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4) 55 | .order(ByteOrder.nativeOrder()) 56 | .asFloatBuffer() 57 | .put(vertexData); 58 | vertexBuffer.position(0); 59 | fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4) 60 | .order(ByteOrder.nativeOrder()) 61 | .asFloatBuffer() 62 | .put(fragmentData); 63 | fragmentBuffer.position(0); 64 | } 65 | 66 | public void onCreate() { 67 | try { 68 | //设置透明渲染 69 | GLES20.glEnable(GLES20.GL_BLEND); 70 | GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); 71 | 72 | String vertexSource = ShaderUtils.getRawResource(context, R.raw.vertex_shader_screen); 73 | 74 | String fragmentSource = ShaderUtils.getRawResource(context, R.raw.fragment_shader_screen); 75 | int[] id = ShaderUtils.createProgram(vertexSource, fragmentSource); 76 | if (id != null) { 77 | vertexShader = id[0]; 78 | fragmentShader = id[1]; 79 | program = id[2]; 80 | } 81 | vPosition = GLES20.glGetAttribLocation(program, "v_Position"); 82 | fPosition = GLES20.glGetAttribLocation(program, "f_Position"); 83 | sampler = GLES20.glGetUniformLocation(program, "sTexture"); 84 | 85 | int[] vbos = new int[1]; 86 | GLES20.glGenBuffers(1, vbos, 0); 87 | vboId = vbos[0]; 88 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId); 89 | GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20.GL_STATIC_DRAW); 90 | GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer); 91 | GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer); 92 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 93 | 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | public void onChange(int width, int height) { 100 | GLES20.glViewport(0, 0, width, height); 101 | } 102 | 103 | public void onDrawFrame(int textureId) { 104 | //清屏 105 | GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 106 | GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); 107 | //使用program 108 | GLES20.glUseProgram(program); 109 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId); 110 | 111 | //绑定fbo纹理 112 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); 113 | GLES20.glEnableVertexAttribArray(vPosition); 114 | GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 0); 115 | GLES20.glEnableVertexAttribArray(fPosition); 116 | GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4); 117 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 118 | 119 | 120 | if (bitmap != null && !bitmap.isRecycled()) { 121 | if (bitmapTextureId != -1) { 122 | GLES20.glDeleteTextures(1, new int[]{bitmapTextureId}, 0); 123 | } 124 | 125 | bitmapTextureId = ShaderUtils.loadBitmapTexture(bitmap); 126 | if (bitmap != null) { 127 | bitmap.recycle(); 128 | bitmap = null; 129 | } 130 | } 131 | if (bitmapTextureId != -1) { 132 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTextureId); 133 | GLES20.glEnableVertexAttribArray(vPosition); 134 | GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 32); 135 | GLES20.glEnableVertexAttribArray(fPosition); 136 | GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4); 137 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 138 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 139 | } 140 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 141 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 142 | } 143 | 144 | public void onDeleteTextureId() { 145 | if (bitmapTextureId != -1) { 146 | GLES20.glDeleteTextures(1, new int[]{bitmapTextureId}, 0); 147 | } 148 | if (vboId != -1) 149 | GLES20.glDeleteBuffers(1, new int[]{vboId}, 0); 150 | if (program != -1) 151 | GLES20.glDeleteProgram(program); 152 | if (vertexShader != -1) 153 | GLES20.glDeleteShader(vertexShader); 154 | if (fragmentShader != -1) 155 | GLES20.glDeleteShader(fragmentShader); 156 | } 157 | 158 | public void setBitmap(int textSize, String textColor, String bgColor, int padding, String speed, String vehicleLicence, String address, String time) { 159 | if (bitmap != null && !bitmap.isRecycled()) { 160 | bitmap.recycle(); 161 | bitmap = null; 162 | } else { 163 | bitmap = null; 164 | } 165 | bitmap = ShaderUtils.createTextImage(textSize, textColor, bgColor, padding, speed, vehicleLicence, address, time); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/egl/EglHelper.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.egl; 2 | 3 | import android.opengl.EGL14; 4 | import android.view.Surface; 5 | 6 | import javax.microedition.khronos.egl.EGL10; 7 | import javax.microedition.khronos.egl.EGLConfig; 8 | import javax.microedition.khronos.egl.EGLContext; 9 | import javax.microedition.khronos.egl.EGLDisplay; 10 | import javax.microedition.khronos.egl.EGLSurface; 11 | 12 | public class EglHelper { 13 | 14 | private EGL10 mEgl; 15 | private EGLDisplay mEglDisplay; 16 | private EGLContext mEglContext; 17 | private EGLSurface mEglSurface; 18 | 19 | private OnEGLContext onEGLContext; 20 | public void initEgl(Surface surface, EGLContext eglContext,int width,int height) { 21 | mEgl = (EGL10) EGLContext.getEGL(); 22 | mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 23 | if (mEglDisplay == EGL10.EGL_NO_DISPLAY) 24 | throw new RuntimeException("eglGetDisplay failed"); 25 | 26 | int[] version = new int[2]; 27 | if (!mEgl.eglInitialize(mEglDisplay, version)) 28 | throw new RuntimeException("eglInitialize failed"); 29 | 30 | int[] attrbutes = new int[]{ 31 | EGL10.EGL_RED_SIZE, 8, 32 | EGL10.EGL_GREEN_SIZE, 8, 33 | EGL10.EGL_BLUE_SIZE, 8, 34 | EGL10.EGL_ALPHA_SIZE, 8, 35 | EGL10.EGL_DEPTH_SIZE, 8, 36 | EGL10.EGL_STENCIL_SIZE, 8, 37 | EGL10.EGL_RENDERABLE_TYPE, 4, 38 | EGL10.EGL_NONE}; 39 | 40 | int[] num_config = new int[1]; 41 | if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, null, 1, num_config)) 42 | throw new IllegalArgumentException("eglChooseConfig failed"); 43 | int numConfigs = num_config[0]; 44 | if (numConfigs <= 0) 45 | throw new IllegalArgumentException("No configs match configSpec"); 46 | 47 | 48 | EGLConfig[] configs = new EGLConfig[numConfigs]; 49 | if (!mEgl.eglChooseConfig(mEglDisplay, attrbutes, configs, numConfigs, num_config)) 50 | throw new IllegalArgumentException("eglChooseConfig#2failed"); 51 | 52 | int[] attrib_list = { 53 | EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 54 | EGL10.EGL_NONE 55 | }; 56 | 57 | if (eglContext != null) 58 | mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], eglContext, attrib_list); 59 | else 60 | mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, attrib_list); 61 | 62 | if(onEGLContext!= null){ 63 | onEGLContext.EGLContext(mEglContext); 64 | } 65 | 66 | if(surface==null){ 67 | int[] attrib_list1 = { 68 | EGL10.EGL_WIDTH,width, 69 | EGL10.EGL_HEIGHT,height, 70 | EGL10.EGL_NONE 71 | }; 72 | mEglSurface = mEgl.eglCreatePbufferSurface(mEglDisplay,configs[0],attrib_list1); 73 | }else{ 74 | mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], surface, null); 75 | } 76 | 77 | if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) 78 | throw new RuntimeException("eglMakeCurrent failed"); 79 | } 80 | 81 | 82 | public boolean swapBuffers() { 83 | if (mEgl != null) 84 | return mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 85 | else 86 | throw new RuntimeException("egl is null"); 87 | } 88 | 89 | public EGLContext getEglContext() { 90 | return mEglContext; 91 | } 92 | 93 | public void onDestoryEgl() { 94 | if (mEgl != null) { 95 | mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 96 | mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 97 | mEglSurface = null; 98 | mEgl.eglDestroyContext(mEglDisplay, mEglContext); 99 | mEglContext = null; 100 | mEgl.eglTerminate(mEglDisplay); 101 | mEglDisplay = null; 102 | mEgl = null; 103 | } 104 | } 105 | public interface OnEGLContext{ 106 | void EGLContext(EGLContext eglContext); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/egl/YUEGLSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.egl; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLSurfaceView; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.Surface; 8 | import android.view.SurfaceHolder; 9 | import android.view.SurfaceView; 10 | 11 | import java.lang.ref.WeakReference; 12 | 13 | import javax.microedition.khronos.egl.EGLContext; 14 | 15 | public abstract class YUEGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { 16 | private EGLContext mEglContext; 17 | public Surface mSurface; 18 | 19 | private YUGLThread mYUGLThread; 20 | 21 | private YuGLRender mYuGLRender; 22 | public final static int RENDERMODE_WHEN_DIRTY = 0; 23 | public final static int RENDERMODE_CONTINUOUSLY = 1; 24 | private int mRenderMode = RENDERMODE_CONTINUOUSLY; 25 | 26 | private int fps = 15; 27 | 28 | public YUEGLSurfaceView(Context context) { 29 | this(context, null); 30 | } 31 | 32 | public YUEGLSurfaceView(Context context, AttributeSet attrs) { 33 | this(context, attrs, 0); 34 | } 35 | 36 | public YUEGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { 37 | this(context, attrs, defStyleAttr, 0); 38 | } 39 | 40 | public YUEGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 41 | super(context, attrs, defStyleAttr, defStyleRes); 42 | getHolder().addCallback(this); 43 | 44 | // this.setRender(); 45 | } 46 | 47 | public void setFps(int fps) { 48 | this.fps = fps; 49 | } 50 | 51 | public void setRenderMode(int mRenderMode) { 52 | if (mYuGLRender == null) 53 | throw new RuntimeException("must set render before"); 54 | 55 | 56 | this.mRenderMode = mRenderMode; 57 | } 58 | 59 | 60 | public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) { 61 | mSurface = surface; 62 | mEglContext = eglContext; 63 | } 64 | 65 | @Override 66 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 67 | Log.e("YUEGLSurfaceView", width + " " + height); 68 | mYUGLThread.width = width; 69 | 70 | mYUGLThread.height = height; 71 | mYUGLThread.isChange = true; 72 | 73 | } 74 | 75 | @Override 76 | public void surfaceCreated(SurfaceHolder holder) { 77 | if (mSurface == null) 78 | mSurface = holder.getSurface(); 79 | mYUGLThread = new YUGLThread(new WeakReference(this)); 80 | mYUGLThread.isCreate = true; 81 | mYUGLThread.start(); 82 | 83 | } 84 | 85 | @Override 86 | public void surfaceDestroyed(SurfaceHolder holder) { 87 | mYUGLThread.onDestory(); 88 | mYUGLThread = null; 89 | mSurface = null; 90 | mEglContext = null; 91 | } 92 | 93 | public EGLContext getEglContext() { 94 | if (mYUGLThread != null) { 95 | return mYUGLThread.getEglContext(); 96 | } 97 | return null; 98 | } 99 | 100 | public void requestRender() { 101 | if (mYUGLThread != null) { 102 | mYUGLThread.requestRender(); 103 | } 104 | } 105 | 106 | public interface YuGLRender { 107 | void onSurfaceCreated(); 108 | 109 | void onSurfaceChanged(int width, int height); 110 | 111 | void onDrawFrame(); 112 | 113 | void onDeleteTextureId(); 114 | } 115 | 116 | public void setRender(YuGLRender yuGLRender) { 117 | mYuGLRender = yuGLRender; 118 | } 119 | 120 | static class YUGLThread extends Thread { 121 | private WeakReference yuEglSurfaceViewWeakReference = null; 122 | private EglHelper eglHelper = null; 123 | private boolean isCreate = false; 124 | private boolean isExit = false; 125 | private boolean isChange = false; 126 | private boolean isStart = false; 127 | private int width; 128 | private int height; 129 | private Object object; 130 | 131 | public YUGLThread(WeakReference yuEglSurfaceViewWeakReference) { 132 | this.yuEglSurfaceViewWeakReference = yuEglSurfaceViewWeakReference; 133 | } 134 | 135 | @Override 136 | public void run() { 137 | super.run(); 138 | isExit = false; 139 | isStart = false; 140 | object = new Object(); 141 | eglHelper = new EglHelper(); 142 | eglHelper.initEgl(yuEglSurfaceViewWeakReference.get().mSurface, yuEglSurfaceViewWeakReference.get().mEglContext,width,height); 143 | while (true) { 144 | if (isExit) { 145 | release(); 146 | break; 147 | } 148 | if (isStart) { 149 | if (yuEglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_WHEN_DIRTY) { 150 | synchronized (object) { 151 | try { 152 | object.wait(); 153 | } catch (InterruptedException e) { 154 | e.printStackTrace(); 155 | } 156 | } 157 | } else if (yuEglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_CONTINUOUSLY) { 158 | try { 159 | Thread.sleep(1000 / yuEglSurfaceViewWeakReference.get().fps); 160 | } catch (InterruptedException e) { 161 | e.printStackTrace(); 162 | } 163 | } else { 164 | throw new RuntimeException("mRenderMode is error"); 165 | } 166 | 167 | } 168 | onCreate(); 169 | onChange(width, height); 170 | onDraw(); 171 | isStart = true; 172 | } 173 | } 174 | 175 | private void onCreate() { 176 | if (isCreate && yuEglSurfaceViewWeakReference.get().mYuGLRender != null) { 177 | isCreate = false; 178 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onSurfaceCreated(); 179 | } 180 | } 181 | 182 | private void onChange(int width, int height) { 183 | if (isChange && yuEglSurfaceViewWeakReference.get().mYuGLRender != null) { 184 | isChange = false; 185 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onSurfaceChanged(width, height); 186 | } 187 | } 188 | 189 | private void onDraw() { 190 | if (yuEglSurfaceViewWeakReference.get().mYuGLRender != null && eglHelper != null) { 191 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onDrawFrame(); 192 | if (!isStart) { 193 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onDrawFrame(); 194 | 195 | } 196 | 197 | eglHelper.swapBuffers(); 198 | } 199 | } 200 | 201 | private void requestRender() { 202 | if (object != null) { 203 | synchronized (object) { 204 | object.notifyAll(); 205 | } 206 | } 207 | 208 | } 209 | 210 | public void onDestory() { 211 | isExit = true; 212 | requestRender(); 213 | } 214 | 215 | public void release() { 216 | if (eglHelper != null) { 217 | eglHelper.onDestoryEgl(); 218 | eglHelper = null; 219 | object = null; 220 | yuEglSurfaceViewWeakReference = null; 221 | } 222 | } 223 | 224 | 225 | public EGLContext getEglContext() { 226 | if (eglHelper != null) { 227 | return eglHelper.getEglContext(); 228 | } 229 | 230 | return null; 231 | } 232 | 233 | 234 | } 235 | 236 | 237 | } 238 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/encodec/EncodecRender.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.encodec; 2 | 3 | import android.content.Context; 4 | 5 | import com.yxt.livepusher.egl.BaseRendeer; 6 | import com.yxt.livepusher.egl.YUEGLSurfaceView; 7 | 8 | 9 | public class EncodecRender extends BaseRendeer implements YUEGLSurfaceView.YuGLRender { 10 | 11 | private int textureId; 12 | 13 | public EncodecRender(Context context, int textureId) { 14 | super(context); 15 | this.textureId = textureId; 16 | } 17 | 18 | @Override 19 | public void onSurfaceCreated() { 20 | super.onCreate(); 21 | } 22 | 23 | @Override 24 | public void onSurfaceChanged(int width, int height) { 25 | super.onChange(width, height); 26 | } 27 | 28 | @Override 29 | public void onDrawFrame() { 30 | super.onDrawFrame(textureId); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/encodec/MP4VideoExtractor.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.encodec; 2 | 3 | import android.media.MediaExtractor; 4 | import android.media.MediaFormat; 5 | import android.util.Log; 6 | 7 | import com.yxt.livepusher.utils.MessageConversionUtils; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.nio.ByteBuffer; 12 | 13 | public class MP4VideoExtractor { 14 | 15 | private static final String TAG = "MP4VideoExtractor"; 16 | 17 | private MediaInfoStream mediaInfoStream; 18 | private int streamType; 19 | 20 | public static final int STREAM_TYPE_JT1078 = 1; 21 | public static final int STREAM_TYPE_RTMP = 0; 22 | 23 | private boolean isExit = false; 24 | private MediaExtractor mediaExtractor; 25 | 26 | private int count = 0; 27 | 28 | private long videoPts = 0; 29 | 30 | private int dubledSpeed = 1; 31 | 32 | 33 | private long seekTime = -1; 34 | 35 | //test3.mp4 h264,aac 36 | public void exactorMedia(final File[] sdcard_path, long startTime) { 37 | // FileOutputStream videoOutputStream = null; 38 | // FileOutputStream audioOutputStream = null; 39 | mediaExtractor = new MediaExtractor(); 40 | 41 | isExit = false; 42 | try { 43 | //分离的视频文件 44 | // File videoFile = new File(sdcard_path, "output_video.h264"); 45 | //分离的音频文件 46 | // File audioFile = new File(sdcard_path, "output_audio.aac"); 47 | // videoOutputStream = new FileOutputStream(videoFile); 48 | // audioOutputStream = new FileOutputStream(audioFile); 49 | //输入文件,也可以是网络文件 50 | //oxford.mp4 视频 h264/baseline 音频 aac/lc 44.1k 2 channel 128kb/s 51 | mediaExtractor.setDataSource(sdcard_path[0].getAbsolutePath()); 52 | //test3.mp4 视频h264 high 音频aac 53 | // mediaExtractor.setDataSource(sdcard_path + "/test3.mp4"); 54 | //test2.mp4 视频mpeg4 音频MP3 55 | // mediaExtractor.setDataSource(sdcard_path + "/test2.mp4"); 56 | //信道总数 57 | int trackCount = mediaExtractor.getTrackCount(); 58 | Log.d(TAG, "trackCount:" + trackCount); 59 | int audioTrackIndex = -1; 60 | int videoTrackIndex = -1; 61 | 62 | 63 | for (int i = 0; i < trackCount; i++) { 64 | MediaFormat trackFormat = mediaExtractor.getTrackFormat(i); 65 | String mineType = trackFormat.getString(MediaFormat.KEY_MIME); 66 | 67 | //视频信道 68 | if (mineType.startsWith("video/")) { 69 | videoTrackIndex = i; 70 | } 71 | //音频信道 72 | if (mineType.startsWith("audio/")) { 73 | audioTrackIndex = i; 74 | } 75 | } 76 | 77 | if (videoTrackIndex == -1) { 78 | isExit = true; 79 | return; 80 | } 81 | if (audioTrackIndex == -1) { 82 | isExit = true; 83 | return; 84 | } 85 | 86 | MediaFormat trackFormat1 = mediaExtractor.getTrackFormat(videoTrackIndex); 87 | ByteBuffer spsb = trackFormat1.getByteBuffer("csd-0"); 88 | final byte[] sps = new byte[spsb.remaining()]; 89 | spsb.get(sps, 0, sps.length); 90 | Log.e("asdffdsa sps", MessageConversionUtils.toHexString1(sps)); 91 | ByteBuffer ppsb = trackFormat1.getByteBuffer("csd-1"); 92 | final byte[] pps = new byte[ppsb.remaining()]; 93 | ppsb.get(pps, 0, pps.length); 94 | Log.e("asdffdsa pps", MessageConversionUtils.toHexString1(pps)); 95 | 96 | 97 | final ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024); 98 | //切换到视频信道 99 | mediaExtractor.selectTrack(videoTrackIndex); 100 | // boolean is = true; 101 | new Thread(new Runnable() { 102 | @Override 103 | public void run() { 104 | try { 105 | while (true) { 106 | if (isExit) { 107 | if (mediaExtractor != null) { 108 | mediaExtractor.release(); 109 | mediaExtractor = null; 110 | } 111 | break; 112 | } 113 | int readSampleCount = mediaExtractor.readSampleData(byteBuffer, 0); 114 | if (readSampleCount < 0) { 115 | break; 116 | } 117 | //保存视频信道信息 118 | // byte[] buffer = new byte[readSampleCount]; 119 | // byteBuffer.get(buffer); 120 | // videoOutputStream.write(buffer);//buffer 写入到 videooutputstream中 121 | if (videoPts == 0) { 122 | videoPts = mediaExtractor.getSampleTime(); 123 | } 124 | if (mediaExtractor.getSampleTime() - videoPts > 0) { 125 | Thread.sleep((((mediaExtractor.getSampleTime() - videoPts)) / 1000) / dubledSpeed); 126 | } 127 | videoPts = mediaExtractor.getSampleTime(); 128 | 129 | 130 | byte[] data = new byte[0]; 131 | if (streamType == BasePushEncoder.STREAM_TYPE_RTMP) { 132 | data = new byte[byteBuffer.remaining()]; 133 | byteBuffer.get(data, 0, data.length); 134 | } else if (streamType == BasePushEncoder.STREAM_TYPE_JT1078) { 135 | if (byteBuffer.get(5) == -120) { 136 | data = new byte[byteBuffer.remaining() + sps.length + pps.length]; 137 | byteBuffer.get(data, sps.length + pps.length, data.length - sps.length - pps.length); 138 | System.arraycopy(sps, 0, data, 0, sps.length); 139 | System.arraycopy(pps, 0, data, sps.length, pps.length); 140 | } else { 141 | data = new byte[byteBuffer.remaining()]; 142 | byteBuffer.get(data, 0, data.length); 143 | } 144 | } 145 | 146 | if (mediaInfoStream != null) { 147 | mediaInfoStream.spsppsData(data); 148 | } 149 | 150 | 151 | byteBuffer.clear(); 152 | if (seekTime == -1) { 153 | mediaExtractor.advance(); 154 | } else { 155 | 156 | long time = Long.parseLong(sdcard_path[0].getName().replace("A", "").replace("B", "").replace(".mp4", "")); 157 | if (seekTime > time) { 158 | mediaExtractor.seekTo(seekTime - time, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 159 | while (mediaExtractor.getSampleTime() < seekTime - time) { 160 | mediaExtractor.advance(); 161 | 162 | } 163 | seekTime = -1; 164 | } 165 | } 166 | } 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | } finally { 170 | stopStream(); 171 | } 172 | } 173 | }). 174 | 175 | start(); 176 | 177 | final int finalAudioTrackIndex = audioTrackIndex; 178 | // new Thread(new Runnable() { 179 | // @Override 180 | // public void run() { 181 | // //切换到音频信道 182 | // mediaExtractor.selectTrack(finalAudioTrackIndex); 183 | // while (true) { 184 | // if (isExit) { 185 | // if (mediaExtractor != null) { 186 | // mediaExtractor.release(); 187 | // mediaExtractor = null; 188 | // } 189 | // break; 190 | // } 191 | // int readSampleCount = mediaExtractor.readSampleData(byteBuffer, 0); 192 | // Log.d(TAG, "audio:readSampleCount:" + readSampleCount); 193 | // if (readSampleCount < 0) { 194 | // break; 195 | // } 196 | // //保存音频信息 197 | // byte[] buffer = new byte[readSampleCount]; 198 | // byteBuffer.get(buffer); 199 | // /************************* 用来为aac添加adts头**************************/ 200 | // byte[] aacaudiobuffer = new byte[readSampleCount + 7]; 201 | // addADTStoPacket(aacaudiobuffer, readSampleCount + 7); 202 | // System.arraycopy(buffer, 0, aacaudiobuffer, 7, readSampleCount); 203 | //// audioOutputStream.write(aacaudiobuffer); 204 | // /***************************************close**************************/ 205 | // // audioOutputStream.write(buffer); 206 | // byteBuffer.clear(); 207 | // mediaExtractor.advance(); 208 | // } 209 | // } 210 | // }); 211 | 212 | 213 | } catch (IOException e) { 214 | stopStream(); 215 | } 216 | } 217 | 218 | public void setSeekTime(long seekTime) { 219 | this.seekTime = seekTime; 220 | } 221 | 222 | /** 223 | * 这里之前遇到一个坑,以为这个packetLen是adts头的长度,也就是7,仔细看了下代码,发现这个不是adts头的长度,而是一帧音频的长度 224 | * 225 | * @param packet 一帧数据(包含adts头长度) 226 | * @param packetLen 一帧数据(包含adts头)的长度 227 | */ 228 | public static void addADTStoPacket(byte[] packet, int packetLen,int sampleRate) { 229 | int profile = 2; // AAC LC 230 | int freqIdx = getFreqIdx(sampleRate); 231 | int chanCfg = 2; // CPE 232 | 233 | // fill in ADTS data 234 | packet[0] = (byte) 0xFF; 235 | packet[1] = (byte) 0xF1; 236 | packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2)); 237 | packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11)); 238 | packet[4] = (byte) ((packetLen & 0x7FF) >> 3); 239 | packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F); 240 | packet[6] = (byte) 0xFC; 241 | } 242 | 243 | public void stopStream() { 244 | isExit = true; 245 | 246 | if (mediaExtractor != null) { 247 | mediaExtractor.release(); 248 | mediaExtractor = null; 249 | } 250 | } 251 | 252 | private static int getFreqIdx(int sampleRate) { 253 | int freqIdx; 254 | 255 | switch (sampleRate) { 256 | case 96000: 257 | freqIdx = 0; 258 | break; 259 | case 88200: 260 | freqIdx = 1; 261 | break; 262 | case 64000: 263 | freqIdx = 2; 264 | break; 265 | case 48000: 266 | freqIdx = 3; 267 | break; 268 | case 44100: 269 | freqIdx = 4; 270 | break; 271 | case 32000: 272 | freqIdx = 5; 273 | break; 274 | case 24000: 275 | freqIdx = 6; 276 | break; 277 | case 22050: 278 | freqIdx = 7; 279 | break; 280 | case 16000: 281 | freqIdx = 8; 282 | break; 283 | case 12000: 284 | freqIdx = 9; 285 | break; 286 | case 11025: 287 | freqIdx = 10; 288 | break; 289 | case 8000: 290 | freqIdx = 11; 291 | break; 292 | case 7350: 293 | freqIdx = 12; 294 | break; 295 | default: 296 | freqIdx = 8; 297 | break; 298 | } 299 | 300 | return freqIdx; 301 | } 302 | 303 | public void setMediaInfoStream(MediaInfoStream mediaInfoStream) { 304 | this.mediaInfoStream = mediaInfoStream; 305 | } 306 | 307 | public void setStreamType(int streamType) { 308 | this.streamType = streamType; 309 | } 310 | 311 | public interface MediaInfoStream { 312 | void spsppsData(byte[] data); 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/encodec/PushEncoder.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.encodec; 2 | 3 | import android.content.Context; 4 | 5 | public class PushEncoder extends BasePushEncoder { 6 | private EncodecRender encodecRender; 7 | 8 | public PushEncoder(Context context, int textureId) { 9 | super(); 10 | encodecRender = new EncodecRender(context, textureId); 11 | setRender(encodecRender); 12 | } 13 | public EncodecRender getRender(){ 14 | return encodecRender; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/encodec/RecordEncoder.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.encodec; 2 | 3 | import android.content.Context; 4 | 5 | public class RecordEncoder extends BaseVideoEncoder { 6 | private EncodecRender encodecRender; 7 | public RecordEncoder(Context context,int textureId) { 8 | super(); 9 | encodecRender = new EncodecRender(context,textureId); 10 | setRender(encodecRender); 11 | } 12 | 13 | public EncodecRender getRender(){ 14 | return encodecRender; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/network/NetSocket.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.network; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.Socket; 7 | import java.util.Arrays; 8 | 9 | public class NetSocket { 10 | private String url; 11 | private int port; 12 | private Socket client; 13 | private PullData pd; 14 | private ErrorCallBack ecb; 15 | 16 | // 正常停止运行报错 17 | public static final int ERROR_STOP = 0; 18 | // 网络报错比如网线没插 19 | public static final int ERROR_NET = 1; 20 | // 写入错误 21 | public static final int ERROR_UPDATA = 2; 22 | 23 | private boolean isStop; 24 | private boolean isConnect = false; 25 | 26 | public NetSocket connect(String url, int port) { 27 | isConnect = false; 28 | isStop = true; 29 | this.url = url; 30 | this.port = port; 31 | getBody(); 32 | return this; 33 | } 34 | 35 | public NetSocket getBody() { 36 | 37 | new Thread(new Runnable() { 38 | public void run() { 39 | 40 | try { 41 | isConnect = false; 42 | client = new Socket(url, port); 43 | } catch (Exception e) { 44 | // TODO Auto-generated catch block 45 | if (ecb != null) 46 | ecb.error("net error" + e.toString(), ERROR_NET); 47 | isStop = false; 48 | isConnect = false; 49 | return; 50 | } 51 | if (ecb != null) 52 | ecb.successfulConnection(); 53 | isConnect = true; 54 | try { 55 | byte[] b = new byte[1024]; 56 | int len = 0; 57 | InputStream bodyInput = client.getInputStream(); 58 | while ((len = bodyInput.read(b)) != -1) { 59 | if (!isStop) { 60 | break; 61 | } 62 | if (pd != null) { 63 | splitPullDate(b); 64 | } 65 | 66 | } 67 | 68 | } catch (Exception e) { 69 | // TODO Auto-generated catch block 70 | isConnect = false; 71 | if (ecb != null) 72 | ecb.error("net error", ERROR_UPDATA); 73 | } 74 | } 75 | }).start(); 76 | 77 | return this; 78 | } 79 | 80 | public void splitPullDate(byte[] data) { 81 | int len = data.length; 82 | int startLocation = 0; 83 | for (int i = 0; i < data.length; i++) 84 | if (data[i] == 0x7e) { 85 | startLocation = i; 86 | break; 87 | } 88 | if (startLocation < len - 1) 89 | if (data[startLocation + 1] == 0x7e) { 90 | splitPullDate(Arrays.copyOfRange(data, startLocation + 1, data.length)); 91 | } else { 92 | for (int i = startLocation + 1; i < data.length; i++) { 93 | if (data[i] == 0x7e) { 94 | if (i < data.length - 1 && data[i + 1] == 0x7e) { 95 | splitPullDate(Arrays.copyOfRange(data, i + 1, data.length)); 96 | } 97 | // System.out.println(RadixTransformationUtils.toHexString1(Arrays.copyOfRange(data, 98 | // 0, i))); 99 | if (pd != null) { 100 | pd.getPullData(Arrays.copyOfRange(data, 0, i + 1)); 101 | } 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * 是否需要编码 110 | * 111 | * @param b 数据 112 | */ 113 | public void upBody(byte[] b) { 114 | try { 115 | if (isConnect && !client.isInputShutdown()) { 116 | OutputStream op = client.getOutputStream(); 117 | 118 | 119 | // Log.e("JT1078", RadixTransformationUtils.toHexString1(b)); 120 | op.write(b); 121 | op.flush(); 122 | } 123 | } catch (Exception e) { 124 | // TODO Auto-generated catch block 125 | isConnect = false; 126 | if (ecb != null) { 127 | ecb.error("stop run " + e.toString(), ERROR_UPDATA); 128 | } 129 | // if (ecb != null) { 130 | // ecb.error("upDataError", ERROR_UPDATA); 131 | // } 132 | } 133 | 134 | } 135 | 136 | public NetSocket setPullDataListener(PullData pd) { 137 | this.pd = pd; 138 | return this; 139 | } 140 | 141 | public NetSocket setErrorCallBack(ErrorCallBack ecb) { 142 | this.ecb = ecb; 143 | return this; 144 | } 145 | 146 | public interface PullData { 147 | void getPullData(byte[] data); 148 | } 149 | 150 | public interface ErrorCallBack { 151 | void error(String err, int code); 152 | 153 | void successfulConnection(); 154 | } 155 | 156 | public void onDestroy() { 157 | ecb = null; 158 | isConnect = false; 159 | isStop = false; 160 | try { 161 | if (client != null) 162 | client.close(); 163 | } catch (IOException e) { 164 | e.printStackTrace(); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/network/rtmp/ConnectListenr.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.network.rtmp; 2 | 3 | public interface ConnectListenr { 4 | 5 | void onConnecting(); 6 | 7 | void onConnectSuccess(); 8 | 9 | void onConnectFail(String msg); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/network/rtmp/RtmpPush.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.network.rtmp; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | public class RtmpPush { 7 | 8 | 9 | static { 10 | System.loadLibrary("push"); 11 | } 12 | 13 | private ConnectListenr connectListenr; 14 | 15 | 16 | public void setConnectListenr(ConnectListenr connectListenr) { 17 | this.connectListenr = connectListenr; 18 | } 19 | 20 | 21 | private void onConnecting() { 22 | if (connectListenr != null) { 23 | connectListenr.onConnecting(); 24 | } 25 | } 26 | 27 | private void onConnectSuccess() { 28 | if (connectListenr != null) { 29 | connectListenr.onConnectSuccess(); 30 | } 31 | } 32 | 33 | private void onConnectFial(String msg) { 34 | if (connectListenr != null) { 35 | connectListenr.onConnectFail(msg); 36 | } 37 | } 38 | 39 | 40 | public void initLivePush(String url) { 41 | if (!TextUtils.isEmpty(url)) { 42 | initPush(url); 43 | } 44 | } 45 | 46 | public void pushSPSPPS(byte[] sps, byte[] pps) { 47 | if (sps != null && pps != null) { 48 | pushSPSPPS(sps, sps.length, pps, pps.length); 49 | } 50 | } 51 | 52 | public void pushVideoData(byte[] data, boolean keyframe) { 53 | if (data != null) { 54 | pushVideoData(data, data.length, keyframe); 55 | } 56 | } 57 | 58 | public void pushAudioData(byte[] data) { 59 | if (data != null) { 60 | pushAudioData(data, data.length); 61 | } 62 | } 63 | 64 | public void stopPush() { 65 | pushStop(); 66 | } 67 | 68 | 69 | private native void initPush(String pushUrl); 70 | 71 | private native void pushSPSPPS(byte[] sps, int sps_len, byte[] pps, int pps_len); 72 | 73 | private native void pushVideoData(byte[] data, int data_len, boolean keyframe); 74 | 75 | private native void pushAudioData(byte[] data, int data_len); 76 | 77 | private native void pushStop(); 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/network/rtmp/RtmpPushBack.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.network.rtmp; 2 | 3 | import android.text.TextUtils; 4 | 5 | //用来推送另一路流的 6 | public class RtmpPushBack { 7 | static { 8 | System.loadLibrary("pushback"); 9 | } 10 | 11 | private ConnectListenr connectListenr; 12 | 13 | 14 | public void setConnectListenr(ConnectListenr connectListenr) { 15 | this.connectListenr = connectListenr; 16 | } 17 | 18 | 19 | private void onConnecting() { 20 | if (connectListenr != null) { 21 | connectListenr.onConnecting(); 22 | } 23 | } 24 | 25 | private void onConnectSuccess() { 26 | if (connectListenr != null) { 27 | connectListenr.onConnectSuccess(); 28 | } 29 | } 30 | 31 | private void onConnectFial(String msg) { 32 | if (connectListenr != null) { 33 | connectListenr.onConnectFail(msg); 34 | } 35 | } 36 | 37 | 38 | public void initLivePush(String url) { 39 | if (!TextUtils.isEmpty(url)) { 40 | initPush(url); 41 | } 42 | } 43 | 44 | public void pushSPSPPS(byte[] sps, byte[] pps) { 45 | if (sps != null && pps != null) { 46 | pushSPSPPS(sps, sps.length, pps, pps.length); 47 | } 48 | } 49 | 50 | public void pushVideoData(byte[] data, boolean keyframe) { 51 | if (data != null) { 52 | pushVideoData(data, data.length, keyframe); 53 | } 54 | } 55 | 56 | public void pushAudioData(byte[] data) { 57 | if (data != null) { 58 | pushAudioData(data, data.length); 59 | } 60 | } 61 | 62 | public void stopPush() { 63 | pushStop(); 64 | } 65 | 66 | 67 | private native void initPush(String pushUrl); 68 | 69 | private native void pushSPSPPS(byte[] sps, int sps_len, byte[] pps, int pps_len); 70 | 71 | private native void pushVideoData(byte[] data, int data_len, boolean keyframe); 72 | 73 | private native void pushAudioData(byte[] data, int data_len); 74 | 75 | private native void pushStop(); 76 | } 77 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/test/EGLSurface.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.test; 2 | 3 | import android.graphics.SurfaceTexture; 4 | import android.util.Log; 5 | import android.view.Surface; 6 | import android.view.SurfaceHolder; 7 | 8 | import com.yxt.livepusher.egl.EglHelper; 9 | import com.yxt.livepusher.egl.YUEGLSurfaceView; 10 | 11 | import java.lang.ref.WeakReference; 12 | 13 | import javax.microedition.khronos.egl.EGLContext; 14 | 15 | public class EGLSurface { 16 | 17 | private EGLContext mEglContext; 18 | public Surface mSurface; 19 | 20 | private GLThread mYUGLThread; 21 | public final static int RENDERMODE_WHEN_DIRTY = 0; 22 | public final static int RENDERMODE_CONTINUOUSLY = 1; 23 | private int mRenderMode = RENDERMODE_CONTINUOUSLY; 24 | private YUEGLSurfaceView.YuGLRender mYuGLRender; 25 | 26 | private int fps; 27 | 28 | public EGLSurface() { 29 | 30 | 31 | } 32 | 33 | public void setFps(int fps) { 34 | this.fps = fps; 35 | } 36 | 37 | public void setRenderMode(int mRenderMode) { 38 | if (mYuGLRender == null) 39 | throw new RuntimeException("must set render before"); 40 | 41 | 42 | this.mRenderMode = mRenderMode; 43 | } 44 | 45 | public void setSurfaceAndEglContext(Surface surface, EGLContext eglContext) { 46 | mSurface = surface; 47 | mEglContext = eglContext; 48 | } 49 | 50 | public void surfaceChanged(int format, int width, int height) { 51 | Log.e("YUEGLSurfaceView", width + " " + height); 52 | mYUGLThread.width = width; 53 | 54 | mYUGLThread.height = height; 55 | mYUGLThread.isChange = true; 56 | 57 | } 58 | 59 | public void star() { 60 | surfaceCreated(); 61 | surfaceChanged(1, 640, 480); 62 | } 63 | 64 | public void surfaceCreated() { 65 | if (mSurface == null) 66 | mSurface = new Surface(new SurfaceTexture(10)); 67 | mYUGLThread = new GLThread(new WeakReference(this)); 68 | mYUGLThread.isCreate = true; 69 | mYUGLThread.start(); 70 | 71 | } 72 | 73 | public void surfaceDestroyed(SurfaceHolder holder) { 74 | if (mYUGLThread != null) { 75 | mYUGLThread.onDestory(); 76 | mYUGLThread = null; 77 | } 78 | mSurface = null; 79 | mEglContext = null; 80 | } 81 | 82 | public EGLContext getEglContext() { 83 | if (mYUGLThread != null) { 84 | return mYUGLThread.getEglContext(); 85 | } 86 | return null; 87 | } 88 | 89 | public void requestRender() { 90 | if (mYUGLThread != null) { 91 | mYUGLThread.requestRender(); 92 | } 93 | } 94 | 95 | public void setRender(YUEGLSurfaceView.YuGLRender yuGLRender) { 96 | mYuGLRender = yuGLRender; 97 | } 98 | 99 | 100 | static class GLThread extends Thread { 101 | private WeakReference yuEglSurfaceViewWeakReference = null; 102 | private EglHelper eglHelper = null; 103 | private boolean isCreate = false; 104 | private boolean isExit = false; 105 | private boolean isChange = false; 106 | private boolean isStart = false; 107 | private int width; 108 | private int height; 109 | private Object object; 110 | 111 | public GLThread(WeakReference yuEglSurfaceViewWeakReference) { 112 | this.yuEglSurfaceViewWeakReference = yuEglSurfaceViewWeakReference; 113 | } 114 | 115 | @Override 116 | public void run() { 117 | super.run(); 118 | isExit = false; 119 | isStart = false; 120 | object = new Object(); 121 | eglHelper = new EglHelper(); 122 | eglHelper.initEgl(yuEglSurfaceViewWeakReference.get().mSurface, yuEglSurfaceViewWeakReference.get().mEglContext,width,height); 123 | while (true) { 124 | if (isExit) { 125 | release(); 126 | break; 127 | } 128 | if (isStart) { 129 | if (yuEglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_WHEN_DIRTY) { 130 | synchronized (object) { 131 | try { 132 | object.wait(); 133 | } catch (InterruptedException e) { 134 | e.printStackTrace(); 135 | } 136 | } 137 | } else if (yuEglSurfaceViewWeakReference.get().mRenderMode == RENDERMODE_CONTINUOUSLY) { 138 | try { 139 | Thread.sleep(1000 / yuEglSurfaceViewWeakReference.get().fps); 140 | } catch (InterruptedException e) { 141 | e.printStackTrace(); 142 | } 143 | } else { 144 | throw new RuntimeException("mRenderMode is error"); 145 | } 146 | 147 | } 148 | onCreate(); 149 | onChange(width, height); 150 | onDraw(); 151 | isStart = true; 152 | } 153 | } 154 | 155 | private void onCreate() { 156 | if (isCreate && yuEglSurfaceViewWeakReference.get().mYuGLRender != null) { 157 | isCreate = false; 158 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onSurfaceCreated(); 159 | } 160 | } 161 | 162 | private void onChange(int width, int height) { 163 | if (isChange && yuEglSurfaceViewWeakReference.get().mYuGLRender != null) { 164 | isChange = false; 165 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onSurfaceChanged(width, height); 166 | } 167 | } 168 | 169 | private void onDraw() { 170 | if (yuEglSurfaceViewWeakReference.get().mYuGLRender != null && eglHelper != null) { 171 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onDrawFrame(); 172 | if (!isStart) { 173 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onDrawFrame(); 174 | 175 | } 176 | 177 | eglHelper.swapBuffers(); 178 | } 179 | } 180 | 181 | private void requestRender() { 182 | if (object != null) { 183 | synchronized (object) { 184 | object.notifyAll(); 185 | } 186 | } 187 | 188 | } 189 | 190 | public void onDestory() { 191 | isExit = true; 192 | requestRender(); 193 | } 194 | 195 | public void release() { 196 | if (eglHelper != null) { 197 | eglHelper.onDestoryEgl(); 198 | eglHelper = null; 199 | object = null; 200 | if (yuEglSurfaceViewWeakReference != null && yuEglSurfaceViewWeakReference.get() != null && yuEglSurfaceViewWeakReference.get().mYuGLRender != null) 201 | yuEglSurfaceViewWeakReference.get().mYuGLRender.onDeleteTextureId(); 202 | yuEglSurfaceViewWeakReference = null; 203 | } 204 | } 205 | 206 | 207 | public EGLContext getEglContext() { 208 | if (eglHelper != null) { 209 | return eglHelper.getEglContext(); 210 | } 211 | 212 | return null; 213 | } 214 | 215 | 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/utils/AudioRecordUitl.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.utils; 2 | 3 | import android.media.AudioFormat; 4 | import android.media.AudioRecord; 5 | import android.media.MediaRecorder; 6 | 7 | public class AudioRecordUitl { 8 | 9 | private AudioRecord audioRecord; 10 | private int bufferSizeInBytes; 11 | private boolean start = false; 12 | private int readSize = 0; 13 | 14 | private OnRecordLisener onRecordLisener; 15 | 16 | public AudioRecordUitl() { 17 | bufferSizeInBytes = AudioRecord.getMinBufferSize( 18 | 44100, 19 | AudioFormat.CHANNEL_IN_STEREO, 20 | AudioFormat.ENCODING_PCM_16BIT); 21 | 22 | audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 23 | 44100, 24 | AudioFormat.CHANNEL_IN_STEREO, 25 | AudioFormat.ENCODING_PCM_16BIT, 26 | bufferSizeInBytes 27 | ); 28 | } 29 | 30 | public void setOnRecordLisener(OnRecordLisener onRecordLisener) { 31 | this.onRecordLisener = onRecordLisener; 32 | } 33 | 34 | public void startRecord() { 35 | new Thread() { 36 | @Override 37 | public void run() { 38 | super.run(); 39 | start = true; 40 | audioRecord.startRecording(); 41 | byte[] audiodata = new byte[bufferSizeInBytes]; 42 | // short[] audiodata = new byte[bufferSizeInBytes]; 43 | while (start) { 44 | readSize = audioRecord.read(audiodata, 0, bufferSizeInBytes); 45 | if (onRecordLisener != null) { 46 | onRecordLisener.recordByte(audiodata, readSize); 47 | } 48 | } 49 | if (audioRecord != null) { 50 | audioRecord.stop(); 51 | audioRecord.release(); 52 | audioRecord = null; 53 | } 54 | } 55 | }.start(); 56 | } 57 | 58 | public void stopRecord() { 59 | onRecordLisener = null; 60 | start = false; 61 | } 62 | 63 | public interface OnRecordLisener { 64 | void recordByte(byte[] audioData, int readSize); 65 | } 66 | 67 | public boolean isStart() { 68 | return start; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/utils/G711Code.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.utils; 2 | 3 | import android.util.Log; 4 | 5 | public class G711Code { 6 | private final static int SIGN_BIT = 0x80; 7 | private final static int QUANT_MASK = 0xf; 8 | private final static int SEG_SHIFT = 4; 9 | private final static int SEG_MASK = 0x70; 10 | 11 | static short[] seg_end = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; 12 | 13 | static short search(short val, short[] table, short size) { 14 | 15 | for (short i = 0; i < size; i++) { 16 | if (val <= table[i]) { 17 | return i; 18 | } 19 | } 20 | return size; 21 | } 22 | 23 | static byte linear2alaw(short pcm_val) { 24 | short mask; 25 | short seg; 26 | char aval; 27 | if (pcm_val >= 0) { 28 | mask = 0xD5; 29 | } else { 30 | mask = 0x55; 31 | pcm_val = (short) (-pcm_val - 1); 32 | if (pcm_val < 0) { 33 | pcm_val = 32767; 34 | } 35 | } 36 | 37 | /* Convert the scaled magnitude to segment number. */ 38 | seg = search(pcm_val, seg_end, (short) 8); 39 | 40 | /* Combine the sign, segment, and quantization bits. */ 41 | 42 | if (seg >= 8) /* out of range, return maximum value. */ 43 | return (byte) (0x7F ^ mask); 44 | else { 45 | aval = (char) (seg << SEG_SHIFT); 46 | if (seg < 2) 47 | aval |= (pcm_val >> 4) & QUANT_MASK; 48 | else 49 | aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; 50 | return (byte) (aval ^ mask); 51 | } 52 | } 53 | 54 | 55 | static short alaw2linear(byte a_val) { 56 | short t; 57 | short seg; 58 | 59 | a_val ^= 0x55; 60 | 61 | t = (short) ((a_val & QUANT_MASK) << 4); 62 | seg = (short) ((a_val & SEG_MASK) >> SEG_SHIFT); 63 | switch (seg) { 64 | case 0: 65 | t += 8; 66 | break; 67 | case 1: 68 | t += 0x108; 69 | break; 70 | default: 71 | t += 0x108; 72 | t <<= seg - 1; 73 | } 74 | return (a_val & SIGN_BIT) != 0 ? t : (short) -t; 75 | } 76 | 77 | /** 78 | * pcm 转 G711 a率 79 | * 80 | * @param pcm 81 | * @param code 82 | * @param size 83 | */ 84 | public static void G711aEncoder(short[] pcm, byte[] code, int size) { 85 | for (int i = 0; i < size; i++) { 86 | code[i] = linear2alaw(pcm[i]); 87 | Log.e("-------------", "数据编码"); 88 | } 89 | } 90 | 91 | /** 92 | * G.711 转 PCM 93 | * 94 | * @param pcm 95 | * @param code 96 | * @param size 97 | */ 98 | public static void G711aDecoder(short[] pcm, byte[] code, int size) { 99 | for (int i = 0; i < size; i++) { 100 | pcm[i] = alaw2linear(code[i]); 101 | } 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/utils/MessageConversionUtils.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.utils; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | 8 | public class MessageConversionUtils { 9 | public static int videoStreamSerial = -1; 10 | 11 | public static String toHexString1(byte[] b) { 12 | StringBuffer buffer = new StringBuffer(); 13 | for (int i = 0; i < b.length; ++i) { 14 | buffer.append(toHexString1(b[i])); 15 | } 16 | return buffer.toString(); 17 | } 18 | 19 | public static String toHexString1(byte[] b, int len) { 20 | StringBuffer buffer = new StringBuffer(); 21 | for (int i = 0; i < len; ++i) { 22 | buffer.append(toHexString1(b[i])); 23 | } 24 | return buffer.toString(); 25 | } 26 | 27 | public static String toHexString1(byte b) { 28 | String s = Integer.toHexString(b & 0xFF); 29 | if (s.length() == 1) { 30 | return "0" + s; 31 | } else { 32 | return s; 33 | } 34 | } 35 | 36 | /** 37 | * 把一个整形该为byte 38 | * 39 | * @param value 40 | * @return 41 | * @throws Exception 42 | */ 43 | public static byte integerTo1Byte(int value) { 44 | return (byte) (value & 0xFF); 45 | } 46 | 47 | /** 48 | * 把一个整形该为1位的byte数组 49 | * 50 | * @param value 51 | * @return 52 | * @throws Exception 53 | */ 54 | public static byte[] integerTo1Bytes(int value) { 55 | byte[] result = new byte[1]; 56 | result[0] = (byte) (value & 0xFF); 57 | return result; 58 | } 59 | 60 | /** 61 | * 把一个整形改为2位的byte数组 62 | * 63 | * @param value 64 | * @return 65 | * @throws Exception 66 | */ 67 | public static byte[] integerTo2Bytes(long value) { 68 | byte[] result = new byte[2]; 69 | result[0] = (byte) ((value >>> 8) & 0xFF); 70 | result[1] = (byte) (value & 0xFF); 71 | return result; 72 | } 73 | 74 | /** 75 | * 把一个整形改为3位的byte数组 76 | * 77 | * @param value 78 | * @return 79 | * @throws Exception 80 | */ 81 | public static byte[] integerTo3Bytes(int value) { 82 | byte[] result = new byte[3]; 83 | result[0] = (byte) ((value >>> 16) & 0xFF); 84 | result[1] = (byte) ((value >>> 8) & 0xFF); 85 | result[2] = (byte) (value & 0xFF); 86 | return result; 87 | } 88 | 89 | /** 90 | * 把一个整形改为4位的byte数组 91 | * 92 | * @param value 93 | * @return 94 | * @throws Exception 95 | */ 96 | public static byte[] integerTo4Bytes(int value) { 97 | byte[] result = new byte[4]; 98 | result[0] = (byte) ((value >>> 24) & 0xFF); 99 | result[1] = (byte) ((value >>> 16) & 0xFF); 100 | result[2] = (byte) ((value >>> 8) & 0xFF); 101 | result[3] = (byte) (value & 0xFF); 102 | return result; 103 | } 104 | 105 | /** 106 | * 把一个整形改为8位的byte数组 107 | * 108 | * @param value 109 | * @return 110 | * @throws Exception 111 | */ 112 | public static byte[] integerTo8Bytes(long value) { 113 | byte[] result = new byte[8]; 114 | result[0] = (byte) ((value >>> 50) & 0xFF); 115 | result[1] = (byte) ((value >>> 42) & 0xFF); 116 | result[2] = (byte) ((value >>> 40) & 0xFF); 117 | result[3] = (byte) ((value >>> 32) & 0xFF); 118 | result[4] = (byte) ((value >>> 24) & 0xFF); 119 | result[5] = (byte) ((value >>> 16) & 0xFF); 120 | result[6] = (byte) ((value >>> 8) & 0xFF); 121 | result[7] = (byte) (value & 0xFF); 122 | return result; 123 | } 124 | 125 | /** 126 | * 二制度字符串转字节数组,如 101000000100100101110000 -> A0 09 70 127 | * 128 | * @param input 输入字符串。 129 | * @return 转换好的字节数组。 130 | */ 131 | public static byte[] string2bytes(String input) { 132 | StringBuilder in = new StringBuilder(input); 133 | // 注:这里in.length() 不可在for循环内调用,因为长度在变化 134 | int remainder = in.length() % 8; 135 | if (remainder > 0) 136 | for (int i = 0; i < 8 - remainder; i++) 137 | in.append("0"); 138 | byte[] bts = new byte[in.length() / 8]; 139 | 140 | // Step 8 Apply compression 141 | for (int i = 0; i < bts.length; i++) 142 | bts[i] = (byte) Integer.parseInt(in.substring(i * 8, i * 8 + 8), 2); 143 | 144 | return bts; 145 | } 146 | 147 | 148 | /** 149 | * String 转换BCD 150 | * 151 | * @param str 152 | * @return BCD数组 153 | */ 154 | public static byte[] string2Bcd(String str) { 155 | // 濂囨暟,鍓嶈ˉ闆? 156 | if ((str.length() & 0x1) == 1) { 157 | str = "0" + str; 158 | } 159 | 160 | byte ret[] = new byte[str.length() / 2]; 161 | byte bs[] = str.getBytes(); 162 | for (int i = 0; i < ret.length; i++) { 163 | 164 | byte high = ascII2Bcd(bs[2 * i]); 165 | byte low = ascII2Bcd(bs[2 * i + 1]); 166 | 167 | // TODO 鍙伄缃〣CD浣庡洓浣?? 168 | ret[i] = (byte) ((high << 4) | low); 169 | } 170 | return ret; 171 | } 172 | 173 | private static byte ascII2Bcd(byte asc) { 174 | if ((asc >= '0') && (asc <= '9')) 175 | return (byte) (asc - '0'); 176 | else if ((asc >= 'A') && (asc <= 'F')) 177 | return (byte) (asc - 'A' + 10); 178 | else if ((asc >= 'a') && (asc <= 'f')) 179 | return (byte) (asc - 'a' + 10); 180 | else 181 | return (byte) (asc - 48); 182 | } 183 | 184 | public static byte[] getVideoStreamSerial() { 185 | videoStreamSerial++; 186 | return integerTo2Bytes(videoStreamSerial); 187 | } 188 | 189 | /** 190 | * //实时视频流上传 191 | * 192 | * @param boundary 是否是帧边界 193 | * @param videoStreamSerial 包序号 194 | * @param date 包数据 195 | * @param state 数据状态:1原子包,不可拆分 2分包处理时第一个包 3分包处理时最后一个包 4分包处理时中间包 196 | * @param time 时间戳 197 | * @param dataType 数据类型 0000为I帧 0001为P帧 0010为B帧 0011音频帧 0100透传数据 198 | * @param dataLen 数据包长度 长度为0的时候没有该数据 199 | * @param LastIFrameInterval 距离上一次I帧间隔时间单位ms 200 | * @param LastFrameInterval 距离上一帧间隔时间单位ms 201 | * @param phone 手机号 202 | * @return 203 | * @throws IOException 204 | */ 205 | public static byte[] RealTimeVideoStreaming(byte cameraId, boolean boundary, byte[] videoStreamSerial, byte[] date, int state, byte[] time, String dataType, int dataLen, byte[] LastIFrameInterval, byte[] LastFrameInterval, String phone) throws IOException { 206 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 207 | baos.write(0x30); 208 | baos.write(0x31); 209 | baos.write(0x63); 210 | baos.write(0x64); 211 | baos.write(0x81); 212 | //01100010 213 | if (boundary) { 214 | baos.write(0xE2); 215 | } else { 216 | baos.write(0x62); 217 | } 218 | baos.write(videoStreamSerial); 219 | baos.write(string2Bcd(phone)); 220 | baos.write(cameraId); 221 | // Log.e("serial", RadixTransformationUtils.toHexString1(videoStreamSerial)); 222 | String dataTypeState = ""; 223 | switch (state) { 224 | case 1: 225 | // dataType = "0000" + dataType; 226 | dataTypeState = dataType + "0000"; 227 | break; 228 | case 2: 229 | // dataType = "0001"+dataType; 230 | dataTypeState = dataType + "0001"; 231 | break; 232 | case 3: 233 | // dataType = "0010"+dataType; 234 | dataTypeState = dataType + "0010"; 235 | break; 236 | case 4: 237 | // dataType = "0011"+dataType; 238 | dataTypeState = dataType + "0011"; 239 | break; 240 | } 241 | // Log.e("byte in :", MessageConversionUtils.toHexString1(MessageConversionUtils.string2bytes(dataType)) + " " + dataTypeState); 242 | baos.write(MessageConversionUtils.string2bytes(dataTypeState)); 243 | baos.write(time); 244 | if (!dataType.equals("0011")) { 245 | baos.write(LastIFrameInterval); 246 | baos.write(LastFrameInterval); 247 | } 248 | // if (dataLen != 0) { 249 | baos.write(MessageConversionUtils.integerTo2Bytes(dataLen)); 250 | // } 251 | baos.write(date); 252 | baos.flush(); 253 | baos.close(); 254 | return baos.toByteArray(); 255 | } 256 | 257 | 258 | /** 259 | * //实时音频流上传 260 | * 261 | * @param boundary 是否是帧边界 262 | * @param videoStreamSerial 包序号 263 | * @param date 包数据 264 | * @param state 数据状态:1原子包,不可拆分 2分包处理时第一个包 3分包处理时最后一个包 4分包处理时中间包 265 | * @param time 时间戳 266 | * @param dataLen 数据包长度 长度为0的时候没有该数据 267 | * @param phone 手机号 268 | * @return 269 | * @throws IOException 270 | */ 271 | public static byte[] RealTimeAudioStreaming(byte cameraId, boolean boundary, byte[] videoStreamSerial, byte[] date, int state, byte[] time, int dataLen, String phone) throws IOException { 272 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 273 | baos.write(0x30); 274 | baos.write(0x31); 275 | baos.write(0x63); 276 | baos.write(0x64); 277 | baos.write(0x81); 278 | 279 | if (boundary) { 280 | baos.write(0x93); 281 | } else { 282 | baos.write(0x13); 283 | } 284 | baos.write(videoStreamSerial); 285 | baos.write(string2Bcd(phone)); 286 | baos.write(cameraId); 287 | // Log.e("serial", RadixTransformationUtils.toHexString1(videoStreamSerial)); 288 | String dataTypeState = ""; 289 | switch (state) { 290 | case 1: 291 | // dataType = "0000" + dataType; 292 | dataTypeState = "00110000"; 293 | break; 294 | case 2: 295 | // dataType = "0001"+dataType; 296 | dataTypeState = "00110001"; 297 | break; 298 | case 3: 299 | // dataType = "0010"+dataType; 300 | dataTypeState = "00110010"; 301 | break; 302 | case 4: 303 | // dataType = "0011"+dataType; 304 | dataTypeState = "00110011"; 305 | break; 306 | } 307 | baos.write(MessageConversionUtils.string2bytes(dataTypeState)); 308 | baos.write(time); 309 | // if (dataLen != 0) { 310 | baos.write(MessageConversionUtils.integerTo2Bytes(dataLen)); 311 | // } 312 | baos.write(date); 313 | baos.flush(); 314 | baos.close(); 315 | return baos.toByteArray(); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/utils/ShaderUtils.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.opengl.GLES11Ext; 9 | import android.opengl.GLES20; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.InputStreamReader; 15 | import java.nio.ByteBuffer; 16 | 17 | public class ShaderUtils { 18 | public static String getRawResource(Context context, int ids) throws IOException { 19 | InputStream is = context.getResources().openRawResource(ids); 20 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 21 | StringBuffer sb = new StringBuffer(); 22 | String line; 23 | while ((line = reader.readLine()) != null) { 24 | sb.append(line).append("\n"); 25 | } 26 | reader.close(); 27 | return sb.toString(); 28 | } 29 | 30 | private static int loadShader(int shaderType, String source) { 31 | int shader = GLES20.glCreateShader(shaderType); 32 | if (shader != 0) { 33 | GLES20.glShaderSource(shader, source); 34 | GLES20.glCompileShader(shader); 35 | int[] compile = new int[1]; 36 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0); 37 | if (compile[0] != GLES20.GL_TRUE) { 38 | GLES20.glDeleteShader(shader); 39 | shader = -1; 40 | } 41 | return shader; 42 | } else 43 | return -1; 44 | } 45 | 46 | public static int[] createProgram(String vertexSource, String fragmentSource) { 47 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 48 | int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 49 | 50 | if (vertexShader != 0 && fragmentShader != 0) { 51 | int program = GLES20.glCreateProgram(); 52 | GLES20.glAttachShader(program, vertexShader); 53 | GLES20.glAttachShader(program, fragmentShader); 54 | GLES20.glLinkProgram(program); 55 | return new int[]{vertexShader, fragmentShader, program}; 56 | } 57 | 58 | return null; 59 | } 60 | 61 | 62 | public static Bitmap createTextImage(int textSize, String textColor, String bgColor, int padding, String speed, String vehicleLicence, String address, String time) { 63 | Paint paint = new Paint(); 64 | paint.setColor(Color.parseColor(textColor)); 65 | paint.setTextSize(textSize); 66 | paint.setStyle(Paint.Style.FILL); 67 | paint.setAntiAlias(true); 68 | paint.setStrokeWidth(1); 69 | 70 | 71 | // Paint paintBG = new Paint(); 72 | // paintBG.setColor(0x4D000000); 73 | // paintBG.setAntiAlias(true); 74 | 75 | float width = paint.measureText("地址:" + address) < 310 ? 350 : (paint.measureText("地址:" + address) + 40); 76 | 77 | float top = paint.getFontMetrics().top; 78 | float bottom = paint.getFontMetrics().bottom; 79 | Bitmap bm = Bitmap.createBitmap(640, 70, Bitmap.Config.ARGB_8888); 80 | Canvas canvas = new Canvas(bm); 81 | canvas.drawColor(Color.parseColor("#00000000")); 82 | // canvas.drawRect(10, 80, 83 | // paint.measureText("地址:" + address) < 310 84 | // ? 350 : (paint.measureText("地址:" + address) + 40) 85 | // , 10, paintBG); 86 | canvas.drawColor(Color.parseColor(bgColor)); 87 | canvas.drawText("速度:" + speed + " km/h", 20, 30, paint); 88 | canvas.drawText("车牌号:" + vehicleLicence, 40 + paint.measureText("速度:" + speed + " km/h"), 30, paint); 89 | canvas.drawText("时间:" + time, 60 + paint.measureText("速度:" + speed + " km/h" + "车牌号:" + vehicleLicence), 30, paint); 90 | canvas.drawText("地址:" + address, 20, 50, paint); 91 | 92 | 93 | return bm; 94 | } 95 | 96 | public static int loadBitmapTexture(Bitmap bitmap) { 97 | int[] textureIds = new int[1]; 98 | GLES20.glGenBuffers(1, textureIds, 0); 99 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]); 100 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); 101 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); 102 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 103 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 104 | ByteBuffer bitmapBuffer = ByteBuffer.allocate(bitmap.getHeight() * bitmap.getWidth() * 4); 105 | bitmap.copyPixelsToBuffer(bitmapBuffer); 106 | bitmapBuffer.flip(); 107 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), 108 | bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bitmapBuffer); 109 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 110 | return textureIds[0]; 111 | 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /livepusher/src/main/java/com/yxt/livepusher/view/AutoFitTextureView.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | ==============================================================================*/ 15 | 16 | package com.yxt.livepusher.view; 17 | 18 | import android.content.Context; 19 | import android.util.AttributeSet; 20 | import android.util.Log; 21 | import android.view.TextureView; 22 | 23 | /** 24 | * A {@link TextureView} that can be adjusted to a specified aspect ratio. 25 | */ 26 | public class AutoFitTextureView extends TextureView { 27 | 28 | private int mRatioWidth = 0; 29 | private int mRatioHeight = 0; 30 | 31 | public AutoFitTextureView(Context context) { 32 | this(context, null); 33 | } 34 | 35 | public AutoFitTextureView(Context context, AttributeSet attrs) { 36 | this(context, attrs, 0); 37 | } 38 | 39 | public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { 40 | super(context, attrs, defStyle); 41 | } 42 | 43 | /** 44 | * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio 45 | * calculated from the parameters. Note that the actual sizes of parameters don't matter, that is, 46 | * calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. 47 | * 48 | * @param width Relative horizontal size 49 | * @param height Relative vertical size 50 | */ 51 | public void setAspectRatio(int width, int height) { 52 | if (width < 0 || height < 0) { 53 | throw new IllegalArgumentException("Size cannot be negative."); 54 | } 55 | 56 | Log.e("qwer", width + " " + height); 57 | mRatioWidth = width; 58 | mRatioHeight = height; 59 | requestLayout(); 60 | } 61 | 62 | @Override 63 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 64 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 65 | int width = MeasureSpec.getSize(widthMeasureSpec); 66 | int height = MeasureSpec.getSize(heightMeasureSpec); 67 | if (0 == mRatioWidth || 0 == mRatioHeight) { 68 | setMeasuredDimension(width, height); 69 | } else { 70 | if (width < height * mRatioWidth / mRatioHeight) { 71 | setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); 72 | } else { 73 | setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /livepusher/src/main/res/raw/fragment_shader.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_OES_EGL_image_external : require 2 | precision mediump float; 3 | varying vec2 ft_Position; 4 | uniform samplerExternalOES sTexture; 5 | void main() { 6 | gl_FragColor=texture2D(sTexture, ft_Position); 7 | } 8 | -------------------------------------------------------------------------------- /livepusher/src/main/res/raw/fragment_shader_screen.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec2 ft_Position; 3 | uniform sampler2D sTexture; 4 | void main() { 5 | gl_FragColor=texture2D(sTexture, ft_Position); 6 | } 7 | -------------------------------------------------------------------------------- /livepusher/src/main/res/raw/vertex_shader.glsl: -------------------------------------------------------------------------------- 1 | attribute vec4 v_Position; 2 | attribute vec2 f_Position; 3 | varying vec2 ft_Position; 4 | uniform mat4 u_Matrix; 5 | uniform mat4 m_Matrix; 6 | void main() { 7 | ft_Position = f_Position; 8 | gl_Position = v_Position * u_Matrix; 9 | } 10 | -------------------------------------------------------------------------------- /livepusher/src/main/res/raw/vertex_shader_screen.glsl: -------------------------------------------------------------------------------- 1 | attribute vec4 v_Position; 2 | attribute vec2 f_Position; 3 | varying vec2 ft_Position; 4 | void main() { 5 | ft_Position = f_Position; 6 | gl_Position = v_Position; 7 | } 8 | -------------------------------------------------------------------------------- /livepusher/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | LivePusher 3 | 4 | -------------------------------------------------------------------------------- /livepusher/src/test/java/com/yxt/livepusher/livepusher/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.yxt.livepusher.livepusher; 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 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':livepusher' 2 | --------------------------------------------------------------------------------