├── .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 |
5 |
6 |
19 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
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 |
--------------------------------------------------------------------------------