diff --git a/webrtc_player/android/app/build.gradle b/webrtc_player/android/app/build.gradle index cfd25638..ba13d606 100644 --- a/webrtc_player/android/app/build.gradle +++ b/webrtc_player/android/app/build.gradle @@ -41,6 +41,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation project(':zlm') testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/webrtc_player/android/app/src/main/AndroidManifest.xml b/webrtc_player/android/app/src/main/AndroidManifest.xml index 81281350..f80d6143 100644 --- a/webrtc_player/android/app/src/main/AndroidManifest.xml +++ b/webrtc_player/android/app/src/main/AndroidManifest.xml @@ -41,6 +41,9 @@ + + \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/MainActivity.kt b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/MainActivity.kt index 15a8efc8..14f55008 100644 --- a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/MainActivity.kt +++ b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/MainActivity.kt @@ -1,79 +1,26 @@ package com.zlmediakit.webrtc -import android.annotation.SuppressLint -import android.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Drawable +import android.content.Intent import android.os.Bundle -import android.widget.Toast +import android.view.View import androidx.appcompat.app.AppCompatActivity -import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.activity_main.view.* - class MainActivity : AppCompatActivity() { - private var isSpeaker = true - - @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + } - lifecycle.addObserver(web_rtc_sv) + fun toPlayActivity(view: View) { + startActivity(Intent(this, PlayDemoActivity::class.java)) + } - //http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play - url.setText("http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play") - - //http://192.168.1.17/index/api/webrtc?app=live&stream=test&type=play - btn_play.setOnClickListener { - web_rtc_sv?.setVideoPath(url.text.toString()) - web_rtc_sv.start() - } - - web_rtc_sv.setOnErrorListener { errorCode, errorMsg -> - runOnUiThread { - Toast.makeText(this, "errorCode:$errorCode,errorMsg:$errorMsg", Toast.LENGTH_SHORT) - .show() - } - } - - - btn_pause.setOnClickListener { - web_rtc_sv?.pause() - } - - btn_resume.setOnClickListener { - web_rtc_sv?.resume() - } - - btn_screenshot.setOnClickListener { - web_rtc_sv?.screenshot { - runOnUiThread { - iv_screen.setImageDrawable(BitmapDrawable(it)) - } - } - } - - btn_mute.setOnClickListener { - web_rtc_sv.mute(true) - } - - - selectAudio() - btn_speaker.setOnClickListener { - selectAudio() - } + fun toPushActivity(view: View) { } - fun selectAudio(){ - if (isSpeaker){ - btn_speaker.setText("扬声器") - web_rtc_sv.setSpeakerphoneOn(isSpeaker) - }else{ - btn_speaker.setText("话筒") - web_rtc_sv.setSpeakerphoneOn(isSpeaker) - } - isSpeaker=!isSpeaker + fun toDataChannelActivity(view: View) { + } } \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayDemoActivity.kt b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayDemoActivity.kt new file mode 100644 index 00000000..fb950b32 --- /dev/null +++ b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayDemoActivity.kt @@ -0,0 +1,84 @@ +package com.zlmediakit.webrtc + +import android.annotation.SuppressLint +import android.graphics.drawable.BitmapDrawable +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.zlm.rtc.ZLMRTCPlayer +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.activity_main.view.* +import kotlinx.android.synthetic.main.activity_play.* + + +class PlayDemoActivity : AppCompatActivity() { + + private var isSpeaker = true + + @SuppressLint("SetTextI18n") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_play) + + lifecycle.addObserver(web_rtc_sv) + + + + + + //http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play + url.setText("https://zlmediakit.com/index/api/webrtc?app=live&stream=test&type=play") + + //http://192.168.1.17/index/api/webrtc?app=live&stream=test&type=play + btn_play.setOnClickListener { + web_rtc_sv?.setVideoPath(url.text.toString()) + web_rtc_sv.start() + } + + web_rtc_sv.setOnErrorListener { errorCode, errorMsg -> + runOnUiThread { + Toast.makeText(this, "errorCode:$errorCode,errorMsg:$errorMsg", Toast.LENGTH_SHORT) + .show() + } + } + + + btn_pause.setOnClickListener { + web_rtc_sv?.pause() + } + + btn_resume.setOnClickListener { + web_rtc_sv?.resume() + } + + btn_screenshot.setOnClickListener { + web_rtc_sv?.screenshot { + runOnUiThread { + iv_screen.setImageDrawable(BitmapDrawable(it)) + } + } + } + + btn_mute.setOnClickListener { + web_rtc_sv.mute(true) + } + + + selectAudio() + btn_speaker.setOnClickListener { + selectAudio() + } + + } + + fun selectAudio(){ + if (isSpeaker){ + btn_speaker.setText("扬声器") + web_rtc_sv.setSpeakerphoneOn(isSpeaker) + }else{ + btn_speaker.setText("话筒") + web_rtc_sv.setSpeakerphoneOn(isSpeaker) + } + isSpeaker=!isSpeaker + } +} \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayerDemoActivity.kt b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayerDemoActivity.kt new file mode 100644 index 00000000..26ed3462 --- /dev/null +++ b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PlayerDemoActivity.kt @@ -0,0 +1,19 @@ +package com.zlmediakit.webrtc + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.zlm.rtc.ZLMRTCPlayer +import kotlinx.android.synthetic.main.activity_player.surface_view_renderer + +class PlayerDemoActivity:AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_play) + + ZLMRTCPlayer.shareInstance().bind(this,surface_view_renderer,true) + + + } +} \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PushDemoActivity.kt b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PushDemoActivity.kt new file mode 100644 index 00000000..4ec9002a --- /dev/null +++ b/webrtc_player/android/app/src/main/java/com/zlmediakit/webrtc/PushDemoActivity.kt @@ -0,0 +1,12 @@ +package com.zlmediakit.webrtc + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +class PushDemoActivity: AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } +} \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/res/layout/activity_main.xml b/webrtc_player/android/app/src/main/res/layout/activity_main.xml index fdefb4e5..6cb0ffaa 100644 --- a/webrtc_player/android/app/src/main/res/layout/activity_main.xml +++ b/webrtc_player/android/app/src/main/res/layout/activity_main.xml @@ -1,93 +1,27 @@ - + android:orientation="vertical"> - - - - - + + + android:onClick="toPushActivity" + android:text="推流" /> - - - - - - - - - - - - + android:onClick="toDataChannelActivity" + android:text="DataChannel" + android:textAllCaps="false" /> - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/res/layout/activity_play.xml b/webrtc_player/android/app/src/main/res/layout/activity_play.xml new file mode 100644 index 00000000..c84e17e0 --- /dev/null +++ b/webrtc_player/android/app/src/main/res/layout/activity_play.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/res/layout/activity_player.xml b/webrtc_player/android/app/src/main/res/layout/activity_player.xml new file mode 100644 index 00000000..88e8f09b --- /dev/null +++ b/webrtc_player/android/app/src/main/res/layout/activity_player.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/webrtc_player/android/app/src/main/res/values/strings.xml b/webrtc_player/android/app/src/main/res/values/strings.xml index a7484826..7a3df89f 100644 --- a/webrtc_player/android/app/src/main/res/values/strings.xml +++ b/webrtc_player/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - AndroidWebRTC + ZLMediakit WebRTC \ No newline at end of file diff --git a/webrtc_player/android/settings.gradle b/webrtc_player/android/settings.gradle index 4a669b32..60b70506 100644 --- a/webrtc_player/android/settings.gradle +++ b/webrtc_player/android/settings.gradle @@ -22,3 +22,4 @@ dependencyResolutionManagement { } rootProject.name = "android" include ':app' +include ':zlm' diff --git a/webrtc_player/android/zlm/.gitignore b/webrtc_player/android/zlm/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/webrtc_player/android/zlm/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/webrtc_player/android/zlm/build.gradle b/webrtc_player/android/zlm/build.gradle new file mode 100644 index 00000000..cbefd241 --- /dev/null +++ b/webrtc_player/android/zlm/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.zlm.rtc' + compileSdk 34 + + defaultConfig { + minSdk 24 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + externalNativeBuild { + cmake { + cppFlags "" + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.22.1" + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.13.1' + implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.12.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + implementation 'org.webrtc:google-webrtc:1.0.32006' +} \ No newline at end of file diff --git a/webrtc_player/android/zlm/consumer-rules.pro b/webrtc_player/android/zlm/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.a b/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.a new file mode 100644 index 00000000..2d478107 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.a differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.so b/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.so new file mode 100644 index 00000000..060457de Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libcrypto.so differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.a b/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.a new file mode 100644 index 00000000..8a3ea083 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.a differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.so b/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.so new file mode 100644 index 00000000..a57f1916 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libcurl.so differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libssl.a b/webrtc_player/android/zlm/libs/arm64-v8a/libssl.a new file mode 100644 index 00000000..98ec2d04 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libssl.a differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libssl.so b/webrtc_player/android/zlm/libs/arm64-v8a/libssl.so new file mode 100644 index 00000000..210c7814 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libssl.so differ diff --git a/webrtc_player/android/zlm/libs/arm64-v8a/libz.a b/webrtc_player/android/zlm/libs/arm64-v8a/libz.a new file mode 100644 index 00000000..654aa523 Binary files /dev/null and b/webrtc_player/android/zlm/libs/arm64-v8a/libz.a differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.a b/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.a new file mode 100644 index 00000000..e1e23590 Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.a differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.so b/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.so new file mode 100644 index 00000000..3027104f Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libcrypto.so differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.a b/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.a new file mode 100644 index 00000000..f0ed7b9b Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.a differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.so b/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.so new file mode 100644 index 00000000..83836f13 Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libcurl.so differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.a b/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.a new file mode 100644 index 00000000..c49e3f48 Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.a differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.so b/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.so new file mode 100644 index 00000000..b4fb776a Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libssl.so differ diff --git a/webrtc_player/android/zlm/libs/armeabi-v7a/libz.a b/webrtc_player/android/zlm/libs/armeabi-v7a/libz.a new file mode 100644 index 00000000..89cccb33 Binary files /dev/null and b/webrtc_player/android/zlm/libs/armeabi-v7a/libz.a differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libcrypto.a b/webrtc_player/android/zlm/libs/x86-64/libcrypto.a new file mode 100644 index 00000000..ea216f54 Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libcrypto.a differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libcrypto.so b/webrtc_player/android/zlm/libs/x86-64/libcrypto.so new file mode 100644 index 00000000..b5b2325e Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libcrypto.so differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libcurl.a b/webrtc_player/android/zlm/libs/x86-64/libcurl.a new file mode 100644 index 00000000..1dedc2c9 Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libcurl.a differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libcurl.so b/webrtc_player/android/zlm/libs/x86-64/libcurl.so new file mode 100644 index 00000000..8dbdf906 Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libcurl.so differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libssl.a b/webrtc_player/android/zlm/libs/x86-64/libssl.a new file mode 100644 index 00000000..fc62e1fb Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libssl.a differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libssl.so b/webrtc_player/android/zlm/libs/x86-64/libssl.so new file mode 100644 index 00000000..64e114a9 Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libssl.so differ diff --git a/webrtc_player/android/zlm/libs/x86-64/libz.a b/webrtc_player/android/zlm/libs/x86-64/libz.a new file mode 100644 index 00000000..e8ad9dd3 Binary files /dev/null and b/webrtc_player/android/zlm/libs/x86-64/libz.a differ diff --git a/webrtc_player/android/zlm/proguard-rules.pro b/webrtc_player/android/zlm/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/webrtc_player/android/zlm/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/androidTest/java/com/zlm/rtc/ExampleInstrumentedTest.kt b/webrtc_player/android/zlm/src/androidTest/java/com/zlm/rtc/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..bb55c2f0 --- /dev/null +++ b/webrtc_player/android/zlm/src/androidTest/java/com/zlm/rtc/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.zlm.rtc + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.zlm.rtc.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/AndroidManifest.xml b/webrtc_player/android/zlm/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/CMakeLists.txt b/webrtc_player/android/zlm/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..7b804cd9 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/CMakeLists.txt @@ -0,0 +1,46 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) + +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, +# Since this is the top level CMakeLists.txt, the project name is also accessible +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level +# build script scope). +project("rtc") + + + +add_library(ZLToolKit IMPORTED STATIC) +set_target_properties(tools PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI}/libZLToolKit.a" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include" + ) + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. +# +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} +# is preferred for the same purpose. +# +# In order to load a library into your app from Java/Kotlin, you must call +# System.loadLibrary() and pass the name of the library defined here; +# for GameActivity/NativeActivity derived applications, the same library name must be +# used in the AndroidManifest.xml file. +add_library(${CMAKE_PROJECT_NAME} SHARED + # List C/C++ source files with relative paths to this CMakeLists.txt. + rtc.cpp) + +# Specifies libraries CMake should link to your target library. You +# can link libraries from various origins, such as libraries defined in this +# build script, prebuilt third-party libraries, or Android system libraries. +target_link_libraries(${CMAKE_PROJECT_NAME} + # List libraries link to the target library + android + log + ZLToolKit) \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Codec/AACEncoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Codec/AACEncoder.h new file mode 100644 index 00000000..657a43b9 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Codec/AACEncoder.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef CODEC_AACENCODER_H_ +#define CODEC_AACENCODER_H_ + +namespace mediakit { + +class AACEncoder { +public: + AACEncoder(void); + virtual ~AACEncoder(void); + bool init(int iSampleRate, int iAudioChannel, int iAudioSampleBit); + int inputData(char *pcData, int iLen, unsigned char **ppucOutBuffer); + +private: + unsigned char *_pucPcmBuf = nullptr; + unsigned int _uiPcmLen = 0; + + unsigned char *_pucAacBuf = nullptr; + void *_hEncoder = nullptr; + + unsigned long _ulInputSamples = 0; + unsigned long _ulMaxInputBytes = 0; + unsigned long _ulMaxOutputBytes = 0; + +}; + +} /* namespace mediakit */ + +#endif /* CODEC_AACENCODER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Codec/H264Encoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Codec/H264Encoder.h new file mode 100644 index 00000000..46c86150 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Codec/H264Encoder.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ +#ifndef CODEC_H264ENCODER_H_ +#define CODEC_H264ENCODER_H_ + +#include +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus +#include +#ifdef __cplusplus +} +#endif //__cplusplus + +namespace mediakit { + +class H264Encoder { +public: + typedef struct { + int iType; + int iLength; + uint8_t *pucData; + } H264Frame; + + H264Encoder(); + ~H264Encoder(); + + bool init(int iWidth, int iHeight, int iFps, int iBitRate); + int inputData(char *yuv[3], int linesize[3], int64_t cts, H264Frame **out_frame); + +private: + x264_t *_pX264Handle = nullptr; + x264_picture_t *_pPicIn = nullptr; + x264_picture_t *_pPicOut = nullptr; + H264Frame _aFrames[10]; +}; + +} /* namespace mediakit */ + +#endif /* CODEC_H264ENCODER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Codec/Transcode.h b/webrtc_player/android/zlm/src/main/cpp/include/Codec/Transcode.h new file mode 100644 index 00000000..4cfaee62 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Codec/Transcode.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TRANSCODE_H +#define ZLMEDIAKIT_TRANSCODE_H + +#if defined(ENABLE_FFMPEG) + +#include "Util/TimeTicker.h" +#include "Common/MediaSink.h" + +#ifdef __cplusplus +extern "C" { +#endif +#include "libswscale/swscale.h" +#include "libavutil/avutil.h" +#include "libavutil/pixdesc.h" +#include "libavcodec/avcodec.h" +#include "libswresample/swresample.h" +#include "libavutil/audio_fifo.h" +#include "libavutil/imgutils.h" +#ifdef __cplusplus +} +#endif + +namespace mediakit { + +class FFmpegFrame { +public: + using Ptr = std::shared_ptr; + + FFmpegFrame(std::shared_ptr frame = nullptr); + ~FFmpegFrame(); + + AVFrame *get() const; + void fillPicture(AVPixelFormat target_format, int target_width, int target_height); + +private: + char *_data = nullptr; + std::shared_ptr _frame; +}; + +class FFmpegSwr { +public: + using Ptr = std::shared_ptr; + + FFmpegSwr(AVSampleFormat output, int channel, int channel_layout, int samplerate); + ~FFmpegSwr(); + FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame); + +private: + int _target_channels; + int _target_channel_layout; + int _target_samplerate; + AVSampleFormat _target_format; + SwrContext *_ctx = nullptr; +}; + +class TaskManager { +public: + virtual ~TaskManager(); + + void setMaxTaskSize(size_t size); + void stopThread(bool drop_task); + +protected: + void startThread(const std::string &name); + bool addEncodeTask(std::function task); + bool addDecodeTask(bool key_frame, std::function task); + bool isEnabled() const; + +private: + void onThreadRun(const std::string &name); + +private: + class ThreadExitException : public std::runtime_error { + public: + ThreadExitException() : std::runtime_error("exit") {} + }; + +private: + bool _decode_drop_start = false; + bool _exit = false; + size_t _max_task = 30; + std::mutex _task_mtx; + toolkit::semaphore _sem; + toolkit::List > _task; + std::shared_ptr _thread; +}; + +class FFmpegDecoder : public TaskManager { +public: + using Ptr = std::shared_ptr; + using onDec = std::function; + + FFmpegDecoder(const Track::Ptr &track, int thread_num = 2, const std::vector &codec_name = {}); + ~FFmpegDecoder() override; + + bool inputFrame(const Frame::Ptr &frame, bool live, bool async, bool enable_merge = true); + void setOnDecode(onDec cb); + void flush(); + const AVCodecContext *getContext() const; + +private: + void onDecode(const FFmpegFrame::Ptr &frame); + bool inputFrame_l(const Frame::Ptr &frame, bool live, bool enable_merge); + bool decodeFrame(const char *data, size_t size, uint64_t dts, uint64_t pts, bool live, bool key_frame); + +private: + bool _do_merger = false; + toolkit::Ticker _ticker; + onDec _cb; + std::shared_ptr _context; + FrameMerger _merger{FrameMerger::h264_prefix}; +}; + +class FFmpegSws { +public: + using Ptr = std::shared_ptr; + + FFmpegSws(AVPixelFormat output, int width, int height); + ~FFmpegSws(); + FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame); + int inputFrame(const FFmpegFrame::Ptr &frame, uint8_t *data); + +private: + FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame, int &ret, uint8_t *data); + +private: + int _target_width = 0; + int _target_height = 0; + int _src_width = 0; + int _src_height = 0; + SwsContext *_ctx = nullptr; + AVPixelFormat _src_format = AV_PIX_FMT_NONE; + AVPixelFormat _target_format = AV_PIX_FMT_NONE; +}; + +}//namespace mediakit +#endif// ENABLE_FFMPEG +#endif //ZLMEDIAKIT_TRANSCODE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/Device.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/Device.h new file mode 100644 index 00000000..6af52626 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/Device.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef DEVICE_DEVICE_H_ +#define DEVICE_DEVICE_H_ + +#include +#include +#include +#include "Util/TimeTicker.h" +#include "Common/MultiMediaSourceMuxer.h" + +namespace mediakit { + +class H264Encoder; +class AACEncoder; + +class VideoInfo { +public: + CodecId codecId = CodecH264; + int iWidth; + int iHeight; + float iFrameRate; + int iBitRate = 2 * 1024 * 1024; +}; + +class AudioInfo { +public: + CodecId codecId = CodecAAC; + int iChannel; + int iSampleBit; + int iSampleRate; +}; + +/** + * MultiMediaSourceMuxer类的包装,方便初学者使用 + */ +class DevChannel : public MultiMediaSourceMuxer{ +public: + using Ptr = std::shared_ptr; + + //fDuration<=0为直播,否则为点播 + DevChannel(const MediaTuple& tuple, float duration = 0, const ProtocolOption &option = ProtocolOption()) + : MultiMediaSourceMuxer(tuple, duration, option) {} + + /** + * 初始化视频Track + * 相当于MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr ); + * @param info 视频相关信息 + */ + bool initVideo(const VideoInfo &info); + + /** + * 初始化音频Track + * 相当于MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr ); + * @param info 音频相关信息 + */ + bool initAudio(const AudioInfo &info); + + /** + * 输入264帧 + * @param data 264单帧数据指针 + * @param len 数据指针长度 + * @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳 + * @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts + */ + bool inputH264(const char *data, int len, uint64_t dts, uint64_t pts = 0); + + /** + * 输入265帧 + * @param data 265单帧数据指针 + * @param len 数据指针长度 + * @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳 + * @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts + */ + bool inputH265(const char *data, int len, uint64_t dts, uint64_t pts = 0); + + /** + * 输入aac帧 + * @param data_without_adts 不带adts头的aac帧 + * @param len 帧数据长度 + * @param dts 时间戳,单位毫秒 + * @param adts_header adts头 + */ + bool inputAAC(const char *data_without_adts, int len, uint64_t dts, const char *adts_header); + + /** + * 输入OPUS/G711音频帧 + * @param data 音频帧 + * @param len 帧数据长度 + * @param dts 时间戳,单位毫秒 + */ + bool inputAudio(const char *data, int len, uint64_t dts); + + /** + * 输入yuv420p视频帧,内部会完成编码并调用inputH264方法 + * @param yuv yuv420p数据指针 + * @param linesize yuv420p数据linesize + * @param cts 采集时间戳,单位毫秒 + */ + bool inputYUV(char *yuv[3], int linesize[3], uint64_t cts); + + /** + * 输入pcm数据,内部会完成编码并调用inputAAC方法 + * @param data pcm数据指针,int16整形 + * @param len pcm数据长度 + * @param cts 采集时间戳,单位毫秒 + */ + bool inputPCM(char *data, int len, uint64_t cts); + + //// 重载基类方法,确保线程安全 //// + bool inputFrame(const Frame::Ptr &frame) override; + bool addTrack(const Track::Ptr & track) override; + void addTrackCompleted() override; + +private: + MediaOriginType getOriginType(MediaSource &sender) const override; + +private: + std::shared_ptr _pH264Enc; + std::shared_ptr _pAacEnc; + std::shared_ptr _video; + std::shared_ptr _audio; + toolkit::SmoothTicker _aTicker[2]; +}; + +} /* namespace mediakit */ + +#endif /* DEVICE_DEVICE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/JemallocUtil.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/JemallocUtil.h new file mode 100644 index 00000000..b1c1472a --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/JemallocUtil.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. +* +* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). +* +* Use of this source code is governed by MIT-like license that can be found in the +* LICENSE file in the root of the source tree. All contributing project authors +* may be found in the AUTHORS file in the root of the source tree. +*/ + +#ifndef ZLMEDIAKIT_JEMALLOCUTIL_H +#define ZLMEDIAKIT_JEMALLOCUTIL_H +#include +#include +#include +namespace mediakit { +class JemallocUtil { +public: + static void enable_profiling(); + + static void disable_profiling(); + + static void dump(const std::string &file_name); + static std::string get_malloc_stats(); + static void some_malloc_stats(const std::function &fn); +}; +} // namespace mediakit +#endif // ZLMEDIAKIT_JEMALLOCUTIL_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSink.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSink.h new file mode 100644 index 00000000..683878e3 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSink.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MEDIASINK_H +#define ZLMEDIAKIT_MEDIASINK_H + +#include +#include +#include "Util/TimeTicker.h" +#include "Extension/Frame.h" +#include "Extension/Track.h" + +namespace mediakit{ + +class TrackListener { +public: + virtual ~TrackListener() = default; + + /** + * 添加track,内部会调用Track的clone方法 + * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 + * @param track + */ + virtual bool addTrack(const Track::Ptr & track) = 0; + + /** + * 添加track完毕 + */ + virtual void addTrackCompleted() {}; + + /** + * 重置track + */ + virtual void resetTracks() {}; +}; + +class MediaSinkInterface : public FrameWriterInterface, public TrackListener { +public: + using Ptr = std::shared_ptr; +}; + +/** + * aac静音音频添加器 + */ +class MuteAudioMaker : public FrameDispatcher { +public: + using Ptr = std::shared_ptr; + bool inputFrame(const Frame::Ptr &frame) override; + +private: + int _track_index = -1; + uint64_t _audio_idx = 0; +}; + +/** + * 该类的作用是等待Track ready()返回true也就是就绪后再通知派生类进行下一步的操作 + * 目的是输入Frame前由Track截取处理下,以便获取有效的信息(譬如sps pps aa_cfg) + */ +class MediaSink : public MediaSinkInterface, public TrackSource{ +public: + using Ptr = std::shared_ptr; + /** + * 输入frame + * @param frame + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 添加track,内部会调用Track的clone方法 + * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 + * @param track + */ + bool addTrack(const Track::Ptr & track) override; + + /** + * 添加Track完毕,如果是单Track,会最多等待3秒才会触发onAllTrackReady + * 这样会增加生成流的延时,如果添加了音视频双Track,那么可以不调用此方法 + * 否则为了降低流注册延时,请手动调用此方法 + */ + void addTrackCompleted() override; + + /** + * 设置最大track数,取值范围>=1;该方法与addTrackCompleted类型; + * 在设置单track时,可以加快媒体注册速度 + */ + void setMaxTrackCount(size_t i); + + /** + * 重置track + */ + void resetTracks() override; + + /** + * 获取所有Track + * @param trackReady 是否获取已经准备好的Track + */ + std::vector getTracks(bool trackReady = true) const override; + + /** + * 判断是否已经触发onAllTrackReady事件 + */ + bool isAllTrackReady() const; + + /** + * 设置是否开启音频 + */ + void enableAudio(bool flag); + + /** + * 设置单音频 + */ + void setOnlyAudio(); + + /** + * 设置是否开启添加静音音频 + */ + void enableMuteAudio(bool flag); + + /** + * 是否有视频track + */ + bool haveVideo() const; + +protected: + /** + * 某track已经准备好,其ready()状态返回true, + * 此时代表可以获取其例如sps pps等相关信息了 + * @param track + */ + virtual bool onTrackReady(const Track::Ptr & track) { return false; }; + + /** + * 所有Track已经准备好, + */ + virtual void onAllTrackReady() {}; + + /** + * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 + * @param frame + */ + virtual bool onTrackFrame(const Frame::Ptr &frame) { return false; }; + +private: + /** + * 触发onAllTrackReady事件 + */ + void emitAllTrackReady(); + + /** + * 检查track是否准备完毕 + */ + void checkTrackIfReady(); + void onAllTrackReady_l(); + /** + * 添加aac静音轨道 + */ + bool addMuteAudioTrack(); + +private: + bool _audio_add = false; + bool _have_video = false; + bool _enable_audio = true; + bool _only_audio = false; + bool _add_mute_audio = true; + bool _all_track_ready = false; + size_t _max_track_size = 2; + + toolkit::Ticker _ticker; + MuteAudioMaker::Ptr _mute_audio_maker; + + std::unordered_map > _frame_unread; + std::unordered_map > _track_ready_callback; + std::unordered_map > _track_map; +}; + + +class MediaSinkDelegate : public MediaSink { +public: + /** + * 设置track监听器 + */ + void setTrackListener(TrackListener *listener); + +protected: + void resetTracks() override; + bool onTrackReady(const Track::Ptr & track) override; + void onAllTrackReady() override; + +private: + TrackListener *_listener = nullptr; +}; + +class Demuxer : protected TrackListener, public TrackSource { +public: + void setTrackListener(TrackListener *listener, bool wait_track_ready = false); + std::vector getTracks(bool trackReady = true) const override; + +protected: + bool addTrack(const Track::Ptr &track) override; + void addTrackCompleted() override; + void resetTracks() override; + +private: + MediaSink::Ptr _sink; + TrackListener *_listener = nullptr; + std::vector _origin_track; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_MEDIASINK_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSource.h new file mode 100644 index 00000000..49f16dd4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/MediaSource.h @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MEDIASOURCE_H +#define ZLMEDIAKIT_MEDIASOURCE_H + +#include +#include +#include +#include +#include "Network/Socket.h" +#include "Extension/Track.h" +#include "Record/Recorder.h" + +namespace toolkit { +class Session; +} // namespace toolkit + +namespace mediakit { + +enum class MediaOriginType : uint8_t { + unknown = 0, + rtmp_push , + rtsp_push, + rtp_push, + pull, + ffmpeg_pull, + mp4_vod, + device_chn, + rtc_push, + srt_push +}; + +std::string getOriginTypeString(MediaOriginType type); + +class MediaSource; +class MultiMediaSourceMuxer; +class MediaSourceEvent { +public: + friend class MediaSource; + + class NotImplemented : public std::runtime_error { + public: + template + NotImplemented(T && ...args) : std::runtime_error(std::forward(args)...) {} + }; + + virtual ~MediaSourceEvent() = default; + + // 获取媒体源类型 + virtual MediaOriginType getOriginType(MediaSource &sender) const { return MediaOriginType::unknown; } + // 获取媒体源url或者文件路径 + virtual std::string getOriginUrl(MediaSource &sender) const; + // 获取媒体源客户端相关信息 + virtual std::shared_ptr getOriginSock(MediaSource &sender) const { return nullptr; } + + // 通知拖动进度条 + virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; } + // 通知暂停或恢复 + virtual bool pause(MediaSource &sender, bool pause) { return false; } + // 通知倍数 + virtual bool speed(MediaSource &sender, float speed) { return false; } + // 通知其停止产生流 + virtual bool close(MediaSource &sender) { return false; } + // 获取观看总人数,此函数一般强制重载 + virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::totalReaderCount not implemented"); } + // 通知观看人数变化 + virtual void onReaderChanged(MediaSource &sender, int size); + //流注册或注销事件 + virtual void onRegist(MediaSource &sender, bool regist) {} + // 获取丢包率 + virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; } + // 获取所在线程, 此函数一般强制重载 + virtual toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller not implemented"); } + + ////////////////////////仅供MultiMediaSourceMuxer对象继承//////////////////////// + // 开启或关闭录制 + virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) { return false; }; + // 获取录制状态 + virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; } + // 获取所有track相关信息 + virtual std::vector getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector(); }; + // 获取MultiMediaSourceMuxer对象 + virtual std::shared_ptr getMuxer(MediaSource &sender) { return nullptr; } + + class SendRtpArgs { + public: + enum Type { kRtpRAW = 0, kRtpPS = 1, kRtpTS = 2 }; + // 是否采用udp方式发送rtp + bool is_udp = true; + // rtp类型 + Type type = kRtpPS; + //发送es流时指定是否只发送纯音频流 + bool only_audio = false; + //tcp被动方式 + bool passive = false; + // rtp payload type + uint8_t pt = 96; + //是否支持同ssrc多服务器发送 + bool ssrc_multi_send = false; + // 指定rtp ssrc + std::string ssrc; + // 指定本地发送端口 + uint16_t src_port = 0; + // 发送目标端口 + uint16_t dst_port; + // 发送目标主机地址,可以是ip或域名 + std::string dst_url; + + //udp发送时,是否开启rr rtcp接收超时判断 + bool udp_rtcp_timeout = false; + //tcp被动发送服务器延时关闭事件,单位毫秒;设置为0时,则使用默认值5000ms + uint32_t tcp_passive_close_delay_ms = 0; + //udp 发送时,rr rtcp包接收超时时间,单位毫秒 + uint32_t rtcp_timeout_ms = 30 * 1000; + //udp 发送时,发送sr rtcp包间隔,单位毫秒 + uint32_t rtcp_send_interval_ms = 5 * 1000; + + //发送rtp同时接收,一般用于双向语言对讲, 如果不为空,说明开启接收 + std::string recv_stream_id; + }; + + // 开始发送ps-rtp + virtual void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function cb) { cb(0, toolkit::SockException(toolkit::Err_other, "not implemented"));}; + // 停止发送ps-rtp + virtual bool stopSendRtp(MediaSource &sender, const std::string &ssrc) {return false; } + +private: + toolkit::Timer::Ptr _async_close_timer; +}; + + +template +static void getArgsValue(const MAP &allArgs, const KEY &key, TYPE &value) { + auto val = ((MAP &)allArgs)[key]; + if (!val.empty()) { + value = (TYPE)val; + } +} + +class ProtocolOption { +public: + ProtocolOption(); + + enum { + kModifyStampOff = 0, // 采用源视频流绝对时间戳,不做任何改变 + kModifyStampSystem = 1, // 采用zlmediakit接收数据时的系统时间戳(有平滑处理) + kModifyStampRelative = 2 // 采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正 + }; + // 时间戳类型 + int modify_stamp; + + //转协议是否开启音频 + bool enable_audio; + //添加静音音频,在关闭音频时,此开关无效 + bool add_mute_audio; + // 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close) + // 此配置置1时,此流如果无人观看,将不触发on_none_reader hook回调, + // 而是将直接关闭流 + bool auto_close; + + //断连续推延时,单位毫秒,默认采用配置文件 + uint32_t continue_push_ms; + + // 平滑发送定时器间隔,单位毫秒,置0则关闭;开启后影响cpu性能同时增加内存 + // 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题 + uint32_t paced_sender_ms; + + //是否开启转换为hls(mpegts) + bool enable_hls; + //是否开启转换为hls(fmp4) + bool enable_hls_fmp4; + //是否开启MP4录制 + bool enable_mp4; + //是否开启转换为rtsp/webrtc + bool enable_rtsp; + //是否开启转换为rtmp/flv + bool enable_rtmp; + //是否开启转换为http-ts/ws-ts + bool enable_ts; + //是否开启转换为http-fmp4/ws-fmp4 + bool enable_fmp4; + + // hls协议是否按需生成,如果hls.segNum配置为0(意味着hls录制),那么hls将一直生成(不管此开关) + bool hls_demand; + // rtsp[s]协议是否按需生成 + bool rtsp_demand; + // rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成 + bool rtmp_demand; + // http[s]-ts协议是否按需生成 + bool ts_demand; + // http[s]-fmp4、ws[s]-fmp4协议是否按需生成 + bool fmp4_demand; + + //是否将mp4录制当做观看者 + bool mp4_as_player; + //mp4切片大小,单位秒 + size_t mp4_max_second; + //mp4录制保存路径 + std::string mp4_save_path; + + //hls录制保存路径 + std::string hls_save_path; + + // 支持通过on_publish返回值替换stream_id + std::string stream_replace; + + // 最大track数 + size_t max_track = 2; + + template + ProtocolOption(const MAP &allArgs) : ProtocolOption() { + load(allArgs); + } + + template + void load(const MAP &allArgs) { +#define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key) + GET_OPT_VALUE(modify_stamp); + GET_OPT_VALUE(enable_audio); + GET_OPT_VALUE(add_mute_audio); + GET_OPT_VALUE(auto_close); + GET_OPT_VALUE(continue_push_ms); + GET_OPT_VALUE(paced_sender_ms); + + GET_OPT_VALUE(enable_hls); + GET_OPT_VALUE(enable_hls_fmp4); + GET_OPT_VALUE(enable_mp4); + GET_OPT_VALUE(enable_rtsp); + GET_OPT_VALUE(enable_rtmp); + GET_OPT_VALUE(enable_ts); + GET_OPT_VALUE(enable_fmp4); + + GET_OPT_VALUE(hls_demand); + GET_OPT_VALUE(rtsp_demand); + GET_OPT_VALUE(rtmp_demand); + GET_OPT_VALUE(ts_demand); + GET_OPT_VALUE(fmp4_demand); + + GET_OPT_VALUE(mp4_max_second); + GET_OPT_VALUE(mp4_as_player); + GET_OPT_VALUE(mp4_save_path); + + GET_OPT_VALUE(hls_save_path); + GET_OPT_VALUE(stream_replace); + GET_OPT_VALUE(max_track); + } +}; + +//该对象用于拦截感兴趣的MediaSourceEvent事件 +class MediaSourceEventInterceptor : public MediaSourceEvent { +public: + void setDelegate(const std::weak_ptr &listener); + std::shared_ptr getDelegate() const; + + MediaOriginType getOriginType(MediaSource &sender) const override; + std::string getOriginUrl(MediaSource &sender) const override; + std::shared_ptr getOriginSock(MediaSource &sender) const override; + + bool seekTo(MediaSource &sender, uint32_t stamp) override; + bool pause(MediaSource &sender, bool pause) override; + bool speed(MediaSource &sender, float speed) override; + bool close(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; + void onReaderChanged(MediaSource &sender, int size) override; + void onRegist(MediaSource &sender, bool regist) override; + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override; + bool isRecording(MediaSource &sender, Recorder::type type) override; + std::vector getMediaTracks(MediaSource &sender, bool trackReady = true) const override; + void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function cb) override; + bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override; + float getLossRate(MediaSource &sender, TrackType type) override; + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + std::shared_ptr getMuxer(MediaSource &sender) override; + +private: + std::weak_ptr _listener; +}; + +/** + * 解析url获取媒体相关信息 + */ +class MediaInfo: public MediaTuple { +public: + MediaInfo() = default; + MediaInfo(const std::string &url) { parse(url); } + void parse(const std::string &url); + std::string getUrl() const { return schema + "://" + shortUrl(); } + +public: + uint16_t port = 0; + std::string full_url; + std::string schema; + std::string host; +}; + +bool equalMediaTuple(const MediaTuple& a, const MediaTuple& b); + +/** + * 媒体源,任何rtsp/rtmp的直播流都源自该对象 + */ +class MediaSource: public TrackSource, public std::enable_shared_from_this { +public: + static MediaSource& NullMediaSource(); + using Ptr = std::shared_ptr; + + MediaSource(const std::string &schema, const MediaTuple& tuple); + virtual ~MediaSource(); + + ////////////////获取MediaSource相关信息//////////////// + + // 获取协议类型 + const std::string& getSchema() const { + return _schema; + } + + const MediaTuple& getMediaTuple() const { + return _tuple; + } + + std::string getUrl() const { return _schema + "://" + _tuple.shortUrl(); } + + //获取对象所有权 + std::shared_ptr getOwnership(); + + // 获取所有Track + std::vector getTracks(bool ready = true) const override; + + // 获取流当前时间戳 + virtual uint32_t getTimeStamp(TrackType type) { return 0; }; + // 设置时间戳 + virtual void setTimeStamp(uint32_t stamp) {}; + + // 获取数据速率,单位bytes/s + int getBytesSpeed(TrackType type = TrackInvalid); + // 获取流创建GMT unix时间戳,单位秒 + uint64_t getCreateStamp() const { return _create_stamp; } + // 获取流上线时间,单位秒 + uint64_t getAliveSecond() const; + + ////////////////MediaSourceEvent相关接口实现//////////////// + + // 设置监听者 + virtual void setListener(const std::weak_ptr &listener); + // 获取监听者 + std::weak_ptr getListener() const; + + // 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数 + virtual int readerCount() = 0; + // 观看者个数,包括(hls/rtsp/rtmp) + virtual int totalReaderCount(); + // 获取播放器列表 + virtual void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) { + assert(cb); + cb(std::list()); + } + + virtual bool broadcastMessage(const toolkit::Any &data) { return false; } + + // 获取媒体源类型 + MediaOriginType getOriginType() const; + // 获取媒体源url或者文件路径 + std::string getOriginUrl() const; + // 获取媒体源客户端相关信息 + std::shared_ptr getOriginSock() const; + + // 拖动进度条 + bool seekTo(uint32_t stamp); + // 暂停 + bool pause(bool pause); + // 倍数播放 + bool speed(float speed); + // 关闭该流 + bool close(bool force); + // 该流观看人数变化 + void onReaderChanged(int size); + // 开启或关闭录制 + bool setupRecord(Recorder::type type, bool start, const std::string &custom_path, size_t max_second); + // 获取录制状态 + bool isRecording(Recorder::type type); + // 开始发送ps-rtp + void startSendRtp(const MediaSourceEvent::SendRtpArgs &args, const std::function cb); + // 停止发送ps-rtp + bool stopSendRtp(const std::string &ssrc); + // 获取丢包率 + float getLossRate(mediakit::TrackType type); + // 获取所在线程 + toolkit::EventPoller::Ptr getOwnerPoller(); + // 获取MultiMediaSourceMuxer对象 + std::shared_ptr getMuxer(); + + ////////////////static方法,查找或生成MediaSource//////////////// + + // 同步查找流 + static Ptr find(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &id, bool from_mp4 = false); + static Ptr find(const MediaInfo &info, bool from_mp4 = false) { + return find(info.schema, info.vhost, info.app, info.stream, from_mp4); + } + + // 忽略schema,同步查找流,可能返回rtmp/rtsp/hls类型 + static Ptr find(const std::string &vhost, const std::string &app, const std::string &stream_id, bool from_mp4 = false); + + // 异步查找流 + static void findAsync(const MediaInfo &info, const std::shared_ptr &session, const std::function &cb); + // 遍历所有流 + static void for_each_media(const std::function &cb, const std::string &schema = "", const std::string &vhost = "", const std::string &app = "", const std::string &stream = ""); + // 从mp4文件生成MediaSource + static MediaSource::Ptr createFromMP4(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream, const std::string &file_path = "", bool check_app = true); + +protected: + //媒体注册 + void regist(); + +private: + // 媒体注销 + bool unregist(); + // 触发媒体事件 + void emitEvent(bool regist); + +protected: + toolkit::BytesSpeed _speed[TrackMax]; + MediaTuple _tuple; + +private: + std::atomic_flag _owned { false }; + time_t _create_stamp; + toolkit::Ticker _ticker; + std::string _schema; + std::weak_ptr _listener; + // 对象个数统计 + toolkit::ObjectStatistic _statistic; +}; + +} /* namespace mediakit */ +#endif //ZLMEDIAKIT_MEDIASOURCE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/MultiMediaSourceMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/MultiMediaSourceMuxer.h new file mode 100644 index 00000000..9ca34370 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/MultiMediaSourceMuxer.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H +#define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H + +#include "Common/Stamp.h" +#include "Common/MediaSource.h" +#include "Common/MediaSink.h" +#include "Record/Recorder.h" +#include "Rtp/RtpSender.h" +#include "Record/HlsRecorder.h" +#include "Record/HlsMediaSource.h" +#include "Rtsp/RtspMediaSourceMuxer.h" +#include "Rtmp/RtmpMediaSourceMuxer.h" +#include "TS/TSMediaSourceMuxer.h" +#include "FMP4/FMP4MediaSourceMuxer.h" + +namespace mediakit { + +class MultiMediaSourceMuxer : public MediaSourceEventInterceptor, public MediaSink, public std::enable_shared_from_this{ +public: + using Ptr = std::shared_ptr; + using RingType = toolkit::RingBuffer; + + class Listener { + public: + virtual ~Listener() = default; + virtual void onAllTrackReady() = 0; + }; + + MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec = 0.0,const ProtocolOption &option = ProtocolOption()); + + /** + * 设置事件监听器 + * @param listener 监听器 + */ + void setMediaListener(const std::weak_ptr &listener); + + /** + * 设置Track就绪事件监听器 + * @param listener 事件监听器 + */ + void setTrackListener(const std::weak_ptr &listener); + + /** + * 返回总的消费者个数 + */ + int totalReaderCount() const; + + /** + * 判断是否生效(是否正在转其他协议) + */ + bool isEnabled(); + + /** + * 设置MediaSource时间戳 + * @param stamp 时间戳 + */ + void setTimeStamp(uint32_t stamp); + + /** + * 重置track + */ + void resetTracks() override; + + /////////////////////////////////MediaSourceEvent override///////////////////////////////// + + /** + * 观看总人数 + * @param sender 事件发送者 + * @return 观看总人数 + */ + int totalReaderCount(MediaSource &sender) override; + + /** + * 设置录制状态 + * @param type 录制类型 + * @param start 开始或停止 + * @param custom_path 开启录制时,指定自定义路径 + * @return 是否设置成功 + */ + bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override; + + /** + * 获取录制状态 + * @param type 录制类型 + * @return 录制状态 + */ + bool isRecording(MediaSource &sender, Recorder::type type) override; + + /** + * 开始发送ps-rtp流 + * @param dst_url 目标ip或域名 + * @param dst_port 目标端口 + * @param ssrc rtp的ssrc + * @param is_udp 是否为udp + * @param cb 启动成功或失败回调 + */ + void startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function cb) override; + + /** + * 停止ps-rtp发送 + * @return 是否成功 + */ + bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override; + + /** + * 获取所有Track + * @param trackReady 是否筛选过滤未就绪的track + * @return 所有Track + */ + std::vector getMediaTracks(MediaSource &sender, bool trackReady = true) const override; + + /** + * 获取所属线程 + */ + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + + /** + * 获取本对象 + */ + std::shared_ptr getMuxer(MediaSource &sender) override; + + const ProtocolOption &getOption() const; + const MediaTuple &getMediaTuple() const; + std::string shortUrl() const; + +protected: + /////////////////////////////////MediaSink override///////////////////////////////// + + /** + * 某track已经准备好,其ready()状态返回true, + * 此时代表可以获取其例如sps pps等相关信息了 + * @param track + */ + bool onTrackReady(const Track::Ptr & track) override; + + /** + * 所有Track已经准备好, + */ + void onAllTrackReady() override; + + /** + * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 + * @param frame + */ + bool onTrackFrame(const Frame::Ptr &frame) override; + bool onTrackFrame_l(const Frame::Ptr &frame); + +private: + void createGopCacheIfNeed(); + +private: + bool _is_enable = false; + bool _create_in_poller = false; + bool _video_key_pos = false; + float _dur_sec; + std::shared_ptr _paced_sender; + MediaTuple _tuple; + ProtocolOption _option; + toolkit::Ticker _last_check; + std::unordered_map _stamps; + std::weak_ptr _track_listener; + std::unordered_multimap _rtp_sender; + FMP4MediaSourceMuxer::Ptr _fmp4; + RtmpMediaSourceMuxer::Ptr _rtmp; + RtspMediaSourceMuxer::Ptr _rtsp; + TSMediaSourceMuxer::Ptr _ts; + MediaSinkInterface::Ptr _mp4; + HlsRecorder::Ptr _hls; + HlsFMP4Recorder::Ptr _hls_fmp4; + toolkit::EventPoller::Ptr _poller; + RingType::Ptr _ring; + + //对象个数统计 + toolkit::ObjectStatistic _statistic; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/PacketCache.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/PacketCache.h new file mode 100644 index 00000000..5e930a5f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/PacketCache.h @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. +* +* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). +* +* Use of this source code is governed by MIT-like license that can be found in the +* LICENSE file in the root of the source tree. All contributing project authors +* may be found in the AUTHORS file in the root of the source tree. +*/ + +#ifndef ZLMEDIAKIT_PACKET_CACHE_H_ +#define ZLMEDIAKIT_PACKET_CACHE_H_ + +#include "Common/config.h" +#include "Util/List.h" + +namespace mediakit { +/// 缓存刷新策略类 +class FlushPolicy { +public: + bool isFlushAble(bool is_video, bool is_key, uint64_t new_stamp, size_t cache_size); + +private: + // 音视频的最后时间戳 + uint64_t _last_stamp[2] = { 0, 0 }; +}; + +/// 合并写缓存模板 +/// \tparam packet 包类型 +/// \tparam policy 刷新缓存策略 +/// \tparam packet_list 包缓存类型 +template > > +class PacketCache { +public: + PacketCache() { _cache = std::make_shared(); } + + virtual ~PacketCache() = default; + + void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr pkt, bool key_pos) { + bool flag = flushImmediatelyWhenCloseMerge(); + if (!flag && _policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) { + flush(); + } + + //追加数据到最后 + _cache->emplace_back(std::move(pkt)); + if (key_pos) { + _key_pos = key_pos; + } + + if (flag) { + flush(); + } + } + + void flush() { + if (_cache->empty()) { + return; + } + onFlush(std::move(_cache), _key_pos); + _cache = std::make_shared(); + _key_pos = false; + } + + virtual void clearCache() { + _cache->clear(); + } + + virtual void onFlush(std::shared_ptr, bool key_pos) = 0; + +private: + bool flushImmediatelyWhenCloseMerge() { + // 一般的协议关闭合并写时,立即刷新缓存,这样可以减少一帧的延时,但是rtp例外 + // 因为rtp的包很小,一个RtpPacket包中也不是完整的一帧图像,所以在关闭合并写时, + // 还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时 + // 但是却对性能提升很大,这样做还是比较划算的 + + GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); + GET_CONFIG(int, rtspLowLatency, Rtsp::kLowLatency); + return std::is_same::value ? rtspLowLatency : (mergeWriteMS <= 0); + } + +private: + bool _key_pos = false; + policy _policy; + std::shared_ptr _cache; +}; +} + +#endif //ZLMEDIAKIT_PACKET_CACHE_H_ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/Parser.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/Parser.h new file mode 100644 index 00000000..324658ba --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/Parser.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_PARSER_H +#define ZLMEDIAKIT_PARSER_H + +#include +#include +#include "Util/util.h" + +namespace mediakit { + +// 从字符串中提取子字符串 +std::string findSubString(const char *buf, const char *start, const char *end, size_t buf_size = 0); +// 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns +void splitUrl(const std::string &url, std::string &host, uint16_t &port); +// 解析proxy url,仅支持http +void parseProxyUrl(const std::string &proxy_url, std::string &proxy_host, uint16_t &proxy_port, std::string &proxy_auth); + +struct StrCaseCompare { + bool operator()(const std::string &__x, const std::string &__y) const { return strcasecmp(__x.data(), __y.data()) < 0; } +}; + +class StrCaseMap : public std::multimap { +public: + using Super = std::multimap; + + std::string &operator[](const std::string &k) { + auto it = find(k); + if (it == end()) { + it = Super::emplace(k, ""); + } + return it->second; + } + + template + void emplace(K &&k, V &&v) { + auto it = find(k); + if (it != end()) { + return; + } + Super::emplace(std::forward(k), std::forward(v)); + } + + template + void emplace_force(K &&k, V &&v) { + Super::emplace(std::forward(k), std::forward(v)); + } +}; + +// rtsp/http/sip解析类 +class Parser { +public: + // 解析http/rtsp/sip请求,需要确保buf以\0结尾 + void parse(const char *buf, size_t size); + + // 获取命令字,如GET/POST + const std::string &method() const; + + // 请求时,获取中间url,不包含?后面的参数 + const std::string &url() const; + // 回复时,获取状态码,如200/404 + const std::string &status() const; + + // 获取中间url,包含?后面的参数 + std::string fullUrl() const; + + // 请求时,获取协议名,如HTTP/1.1 + const std::string &protocol() const; + // 回复时,获取状态字符串,如 OK/Not Found + const std::string &statusStr() const; + + // 根据header key名,获取请求header value值 + const std::string &operator[](const char *name) const; + + // 获取http body或sdp + const std::string &content() const; + + // 清空,为了重用 + void clear(); + + // 获取?后面的参数 + const std::string ¶ms() const; + + // 重新设置url + void setUrl(std::string url); + + // 重新设置content + void setContent(std::string content); + + // 获取header列表 + StrCaseMap &getHeader() const; + + // 获取url参数列表 + StrCaseMap &getUrlArgs() const; + + // 解析?后面的参数 + static StrCaseMap parseArgs(const std::string &str, const char *pair_delim = "&", const char *key_delim = "="); + + static std::string mergeUrl(const std::string &base_url, const std::string &path); + +private: + std::string _method; + std::string _url; + std::string _protocol; + std::string _content; + std::string _params; + mutable StrCaseMap _headers; + mutable StrCaseMap _url_args; +}; + +// 解析rtsp url的工具类 +class RtspUrl { +public: + bool _is_ssl; + uint16_t _port; + std::string _url; + std::string _user; + std::string _passwd; + std::string _host; + +public: + void parse(const std::string &url); + +private: + void setup(bool, const std::string &, const std::string &, const std::string &); +}; + +} // namespace mediakit + +#endif // ZLMEDIAKIT_PARSER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/Stamp.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/Stamp.h new file mode 100644 index 00000000..0b525748 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/Stamp.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_STAMP_H +#define ZLMEDIAKIT_STAMP_H + +#include +#include +#include "Util/TimeTicker.h" + +namespace mediakit { + +class DeltaStamp { +public: + DeltaStamp(); + virtual ~DeltaStamp() = default; + + /** + * 计算时间戳增量 + * @param stamp 绝对时间戳 + * @param enable_rollback 是否允许相当时间戳回退 + * @return 时间戳增量 + */ + int64_t deltaStamp(int64_t stamp, bool enable_rollback = true); + int64_t relativeStamp(int64_t stamp, bool enable_rollback = true); + int64_t relativeStamp(); + + // 设置最大允许回退或跳跃幅度 + void setMaxDelta(size_t max_delta); + +protected: + virtual void needSync() {} + +protected: + int _max_delta; + int64_t _last_stamp = 0; + int64_t _relative_stamp = 0; +}; + +//该类解决时间戳回环、回退问题 +//计算相对时间戳或者产生平滑时间戳 +class Stamp : public DeltaStamp{ +public: + /** + * 求取相对时间戳,同时实现了音视频同步、限制dts回退等功能 + * @param dts 输入dts,如果为0则根据系统时间戳生成 + * @param pts 输入pts,如果为0则等于dts + * @param dts_out 输出dts + * @param pts_out 输出pts + * @param modifyStamp 是否用系统时间戳覆盖 + */ + void revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); + + /** + * 再设置相对时间戳,用于seek用 + * @param relativeStamp 相对时间戳 + */ + void setRelativeStamp(int64_t relativeStamp); + + /** + * 获取当前相对时间戳 + * @return + */ + int64_t getRelativeStamp() const ; + + /** + * 设置是否为回放模式,回放模式运行时间戳回退 + * @param playback 是否为回放模式 + */ + void setPlayBack(bool playback = true); + + /** + * 音视频同步用,音频应该同步于视频(只修改音频时间戳) + * 因为音频时间戳修改后不影响播放速度 + */ + void syncTo(Stamp &other); + + /** + * 是否允许时间戳回退 + */ + void enableRollback(bool flag); + +private: + //主要实现音视频时间戳同步功能 + void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); + + //主要实现获取相对时间戳功能 + void revise_l2(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); + + void needSync() override; + +private: + bool _playback = false; + bool _need_sync = false; + // 默认不允许时间戳回滚 + bool _enable_rollback = false; + int64_t _relative_stamp = 0; + int64_t _last_dts_in = 0; + int64_t _last_dts_out = 0; + int64_t _last_pts_out = 0; + toolkit::SmoothTicker _ticker; + Stamp *_sync_master = nullptr; +}; + +//dts生成器, +//pts排序后就是dts +class DtsGenerator{ +public: + bool getDts(uint64_t pts, uint64_t &dts); + +private: + bool getDts_l(uint64_t pts, uint64_t &dts); + +private: + uint64_t _dts_pts_offset = 0; + uint64_t _last_dts = 0; + uint64_t _last_pts = 0; + uint64_t _last_max_pts = 0; + size_t _frames_since_last_max_pts = 0; + size_t _sorter_max_size = 0; + size_t _count_sorter_max_size = 0; + std::set _pts_sorter; +}; + +class NtpStamp { +public: + void setNtpStamp(uint32_t rtp_stamp, uint64_t ntp_stamp_ms); + uint64_t getNtpStamp(uint32_t rtp_stamp, uint32_t sample_rate); + +private: + void update(uint32_t rtp_stamp, uint64_t ntp_stamp_us); + uint64_t getNtpStampUS(uint32_t rtp_stamp, uint32_t sample_rate); + +private: + uint32_t _last_rtp_stamp = 0; + uint64_t _last_ntp_stamp_us = 0; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_STAMP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/config.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/config.h new file mode 100644 index 00000000..98ab289c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/config.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef COMMON_CONFIG_H +#define COMMON_CONFIG_H + +#include "Util/NoticeCenter.h" +#include "Util/mini.h" +#include "Util/onceToken.h" +#include "macros.h" +#include + +namespace mediakit { + +class ProtocolOption; + +// 加载配置文件,如果配置文件不存在,那么会导出默认配置并生成配置文件 +// 加载配置文件成功后会触发kBroadcastUpdateConfig广播 +// 如果指定的文件名(ini_path)为空,那么会加载默认配置文件 +// 默认配置文件名为 /path/to/your/exe.ini +// 加载配置文件成功后返回true,否则返回false +bool loadIniConfig(const char *ini_path = nullptr); + +////////////广播名称/////////// +namespace Broadcast { + +// 注册或反注册MediaSource事件广播 +extern const std::string kBroadcastMediaChanged; +#define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender + +// 录制mp4文件成功后广播 +extern const std::string kBroadcastRecordMP4; +#define BroadcastRecordMP4Args const RecordInfo &info + +// 录制 ts 文件后广播 +extern const std::string kBroadcastRecordTs; +#define BroadcastRecordTsArgs const RecordInfo &info + +// 收到http api请求广播 +extern const std::string kBroadcastHttpRequest; +#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender + +// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 +extern const std::string kBroadcastHttpAccess; +#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender + +// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 +// 在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 +extern const std::string kBroadcastHttpBeforeAccess; +#define BroadcastHttpBeforeAccessArgs const Parser &parser, std::string &path, SockInfo &sender + +// 该流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证 +extern const std::string kBroadcastOnGetRtspRealm; +#define BroadcastOnGetRtspRealmArgs const MediaInfo &args, const RtspSession::onGetRealm &invoker, SockInfo &sender + +// 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 +// 获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码 +extern const std::string kBroadcastOnRtspAuth; +#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, SockInfo &sender + +// 推流鉴权结果回调对象 +// 如果err为空则代表鉴权成功 +using PublishAuthInvoker = std::function; + +// 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 +extern const std::string kBroadcastMediaPublish; +#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender + +// 播放鉴权结果回调对象 +// 如果err为空则代表鉴权成功 +using AuthInvoker = std::function; + +// 播放rtsp/rtmp/http-flv事件广播,通过该事件控制播放鉴权 +extern const std::string kBroadcastMediaPlayed; +#define BroadcastMediaPlayedArgs const MediaInfo &args, const Broadcast::AuthInvoker &invoker, SockInfo &sender + +// shell登录鉴权 +extern const std::string kBroadcastShellLogin; +#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender + +// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 +extern const std::string kBroadcastFlowReport; +#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, SockInfo &sender + +// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 +extern const std::string kBroadcastNotFoundStream; +#define BroadcastNotFoundStreamArgs const MediaInfo &args, SockInfo &sender, const std::function &closePlayer + +// 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 +extern const std::string kBroadcastStreamNoneReader; +#define BroadcastStreamNoneReaderArgs MediaSource &sender + +// rtp推流被动停止时触发 +extern const std::string kBroadcastSendRtpStopped; +#define BroadcastSendRtpStoppedArgs MultiMediaSourceMuxer &sender, const std::string &ssrc, const SockException &ex + +// 更新配置文件事件广播,执行loadIniConfig函数加载配置文件成功后会触发该广播 +extern const std::string kBroadcastReloadConfig; +#define BroadcastReloadConfigArgs void + +// rtp server 超时 +extern const std::string kBroadcastRtpServerTimeout; +#define BroadcastRtpServerTimeoutArgs uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc + +// rtc transport sctp 连接状态 +extern const std::string kBroadcastRtcSctpConnecting; +extern const std::string kBroadcastRtcSctpConnected; +extern const std::string kBroadcastRtcSctpFailed; +extern const std::string kBroadcastRtcSctpClosed; +#define BroadcastRtcSctpConnectArgs WebRtcTransport& sender + +// rtc transport sctp 发送数据 +extern const std::string kBroadcastRtcSctpSend; +#define BroadcastRtcSctpSendArgs WebRtcTransport& sender, const uint8_t *&data, size_t& len + +// rtc transport sctp 接收数据 +extern const std::string kBroadcastRtcSctpReceived; +#define BroadcastRtcSctpReceivedArgs WebRtcTransport& sender, uint16_t &streamId, uint32_t &ppid, const uint8_t *&msg, size_t &len + +#define ReloadConfigTag ((void *)(0xFF)) +#define RELOAD_KEY(arg, key) \ + do { \ + decltype(arg) arg##_tmp = ::toolkit::mINI::Instance()[key]; \ + if (arg == arg##_tmp) { \ + return; \ + } \ + arg = arg##_tmp; \ + InfoL << "reload config:" << key << "=" << arg; \ + } while (0) + +// 监听某个配置发送变更 +#define LISTEN_RELOAD_KEY(arg, key, ...) \ + do { \ + static ::toolkit::onceToken s_token_listen([]() { \ + ::toolkit::NoticeCenter::Instance().addListener( \ + ReloadConfigTag, Broadcast::kBroadcastReloadConfig, [](BroadcastReloadConfigArgs) { __VA_ARGS__; }); \ + }); \ + } while (0) + +#define GET_CONFIG(type, arg, key) \ + static type arg = ::toolkit::mINI::Instance()[key]; \ + LISTEN_RELOAD_KEY(arg, key, { RELOAD_KEY(arg, key); }); + +#define GET_CONFIG_FUNC(type, arg, key, ...) \ + static type arg; \ + do { \ + static ::toolkit::onceToken s_token_set([]() { \ + static auto lam = __VA_ARGS__; \ + static auto arg##_str = ::toolkit::mINI::Instance()[key]; \ + arg = lam(arg##_str); \ + LISTEN_RELOAD_KEY(arg, key, { \ + RELOAD_KEY(arg##_str, key); \ + arg = lam(arg##_str); \ + }); \ + }); \ + } while (0) + +} // namespace Broadcast + +////////////通用配置/////////// +namespace General { +// 每个流媒体服务器的ID(GUID) +extern const std::string kMediaServerId; +// 流量汇报事件流量阈值,单位KB,默认1MB +extern const std::string kFlowThreshold; +// 流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件 +// 默认连续5秒无人观看然后触发kBroadcastStreamNoneReader事件 +extern const std::string kStreamNoneReaderDelayMS; +// 等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间, +// 如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功, +// 否则会最多等待kMaxStreamWaitTimeMS毫秒,然后响应播放器播放失败 +extern const std::string kMaxStreamWaitTimeMS; +// 是否启动虚拟主机 +extern const std::string kEnableVhost; +// 拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, +// 如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) +extern const std::string kResetWhenRePlay; +// 合并写缓存大小(单位毫秒),合并写指服务器缓存一定的数据后才会一次性写入socket,这样能提高性能,但是会提高延时 +// 开启后会同时关闭TCP_NODELAY并开启MSG_MORE +extern const std::string kMergeWriteMS; +// 在docker环境下,不能通过英伟达驱动是否存在来判断是否支持硬件转码 +extern const std::string kCheckNvidiaDev; +// 是否开启ffmpeg日志 +extern const std::string kEnableFFmpegLog; +// 最多等待未初始化的Track 10秒,超时之后会忽略未初始化的Track +extern const std::string kWaitTrackReadyMS; +// 如果直播流只有单Track,最多等待3秒,超时后未收到其他Track的数据,则认为是单Track +// 如果协议元数据有声明特定track数,那么无此等待时间 +extern const std::string kWaitAddTrackMS; +// 如果track未就绪,我们先缓存帧数据,但是有最大个数限制(100帧时大约4秒),防止内存溢出 +extern const std::string kUnreadyFrameCache; +} // namespace General + +namespace Protocol { +//时间戳修复这一路流标志位 +extern const std::string kModifyStamp; +//转协议是否开启音频 +extern const std::string kEnableAudio; +//添加静音音频,在关闭音频时,此开关无效 +extern const std::string kAddMuteAudio; +// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close) +// 此配置置1时,此流如果无人观看,将不触发on_none_reader hook回调, +// 而是将直接关闭流 +extern const std::string kAutoClose; +//断连续推延时,单位毫秒,默认采用配置文件 +extern const std::string kContinuePushMS; +// 平滑发送定时器间隔,单位毫秒,置0则关闭;开启后影响cpu性能同时增加内存 +// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题 +extern const std::string kPacedSenderMS; + +//是否开启转换为hls(mpegts) +extern const std::string kEnableHls; +//是否开启转换为hls(fmp4) +extern const std::string kEnableHlsFmp4; +//是否开启MP4录制 +extern const std::string kEnableMP4; +//是否开启转换为rtsp/webrtc +extern const std::string kEnableRtsp; +//是否开启转换为rtmp/flv +extern const std::string kEnableRtmp; +//是否开启转换为http-ts/ws-ts +extern const std::string kEnableTS; +//是否开启转换为http-fmp4/ws-fmp4 +extern const std::string kEnableFMP4; + +//是否将mp4录制当做观看者 +extern const std::string kMP4AsPlayer; +//mp4切片大小,单位秒 +extern const std::string kMP4MaxSecond; +//mp4录制保存路径 +extern const std::string kMP4SavePath; + +//hls录制保存路径 +extern const std::string kHlsSavePath; + +// 按需转协议的开关 +extern const std::string kHlsDemand; +extern const std::string kRtspDemand; +extern const std::string kRtmpDemand; +extern const std::string kTSDemand; +extern const std::string kFMP4Demand; +} // !Protocol + +////////////HTTP配置/////////// +namespace Http { +// http 文件发送缓存大小 +extern const std::string kSendBufSize; +// http 最大请求字节数 +extern const std::string kMaxReqSize; +// http keep-alive秒数 +extern const std::string kKeepAliveSecond; +// http 字符编码 +extern const std::string kCharSet; +// http 服务器根目录 +extern const std::string kRootPath; +// http 服务器虚拟目录 虚拟目录名和文件路径使用","隔开,多个配置路径间用";"隔开,例如 path_d,d:/record;path_e,e:/record +extern const std::string kVirtualPath; +// http 404错误提示内容 +extern const std::string kNotFound; +// 是否显示文件夹菜单 +extern const std::string kDirMenu; +// 禁止缓存文件的后缀 +extern const std::string kForbidCacheSuffix; +// 可以把http代理前真实客户端ip放在http头中:https://github.com/ZLMediaKit/ZLMediaKit/issues/1388 +extern const std::string kForwardedIpHeader; +// 是否允许所有跨域请求 +extern const std::string kAllowCrossDomains; +// 允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制 +extern const std::string kAllowIPRange; +} // namespace Http + +////////////SHELL配置/////////// +namespace Shell { +extern const std::string kMaxReqSize; +} // namespace Shell + +////////////RTSP服务器配置/////////// +namespace Rtsp { +// 是否优先base64方式认证?默认Md5方式认证 +extern const std::string kAuthBasic; +// 握手超时时间,默认15秒 +extern const std::string kHandshakeSecond; +// 维持链接超时时间,默认15秒 +extern const std::string kKeepAliveSecond; + +// rtsp拉流代理是否直接代理 +// 直接代理后支持任意编码格式,但是会导致GOP缓存无法定位到I帧,可能会导致开播花屏 +// 并且如果是tcp方式拉流,如果rtp大于mtu会导致无法使用udp方式代理 +// 假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理 +// 默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的 +extern const std::string kDirectProxy; + +// rtsp 转发是否使用低延迟模式,当开启时,不会缓存rtp包,来提高并发,可以降低一帧的延迟 +extern const std::string kLowLatency; + +//强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制) +//当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupport Transport +//迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC +extern const std::string kRtpTransportType; +} // namespace Rtsp + +////////////RTMP服务器配置/////////// +namespace Rtmp { +// 握手超时时间,默认15秒 +extern const std::string kHandshakeSecond; +// 维持链接超时时间,默认15秒 +extern const std::string kKeepAliveSecond; +// 是否直接代理 +extern const std::string kDirectProxy; +// h265-rtmp是否采用增强型(或者国内扩展) +extern const std::string kEnhanced; +} // namespace Rtmp + +////////////RTP配置/////////// +namespace Rtp { +// RTP打包最大MTU,公网情况下更小 +extern const std::string kVideoMtuSize; +// RTP打包最大MTU,公网情况下更小 +extern const std::string kAudioMtuSize; +// rtp包最大长度限制, 单位KB +extern const std::string kRtpMaxSize; +// rtp 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏 +extern const std::string kLowLatency; +//H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式 +extern const std::string kH264StapA; +} // namespace Rtp + +////////////组播配置/////////// +namespace MultiCast { +// 组播分配起始地址 +extern const std::string kAddrMin; +// 组播分配截止地址 +extern const std::string kAddrMax; +// 组播TTL +extern const std::string kUdpTTL; +} // namespace MultiCast + +////////////录像配置/////////// +namespace Record { +// 查看录像的应用名称 +extern const std::string kAppName; +// 每次流化MP4文件的时长,单位毫秒 +extern const std::string kSampleMS; +// mp4文件写缓存大小 +extern const std::string kFileBufSize; +// mp4录制完成后是否进行二次关键帧索引写入头部 +extern const std::string kFastStart; +// mp4文件是否重头循环读取 +extern const std::string kFileRepeat; +// mp4录制文件是否采用fmp4格式 +extern const std::string kEnableFmp4; +} // namespace Record + +////////////HLS相关配置/////////// +namespace Hls { +// HLS切片时长,单位秒 +extern const std::string kSegmentDuration; +// m3u8文件中HLS切片个数,如果设置为0,则不删除切片,而是保存为点播 +extern const std::string kSegmentNum; +// 如果设置为0,则不保留切片,设置为1则一直保留切片 +extern const std::string kSegmentKeep; +// HLS切片延迟个数,大于0将生成hls_delay.m3u8文件,0则不生成 +extern const std::string kSegmentDelay; +// HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 +extern const std::string kSegmentRetain; +// HLS文件写缓存大小 +extern const std::string kFileBufSize; +// 是否广播 ts 切片完成通知 +extern const std::string kBroadcastRecordTs; +// hls直播文件删除延时,单位秒 +extern const std::string kDeleteDelaySec; +// 如果设置为1,则第一个切片长度强制设置为1个GOP +extern const std::string kFastRegister; +} // namespace Hls + +////////////Rtp代理相关配置/////////// +namespace RtpProxy { +// rtp调试数据保存目录,置空则不生成 +extern const std::string kDumpDir; +// rtp接收超时时间 +extern const std::string kTimeoutSec; +// 随机端口范围,最少确保36个端口 +// 该范围同时限制rtsp服务器udp端口范围 +extern const std::string kPortRange; +// rtp server h264的pt +extern const std::string kH264PT; +// rtp server h265的pt +extern const std::string kH265PT; +// rtp server ps 的pt +extern const std::string kPSPT; +// rtp server opus 的pt +extern const std::string kOpusPT; +// RtpSender相关功能是否提前开启gop缓存优化级联秒开体验,默认开启 +extern const std::string kGopCache; +//国标发送g711 rtp 打包时,每个包的语音时长是多少,默认是100 ms,范围为20~180ms (gb28181-2016,c.2.4规定), +//最好为20 的倍数,程序自动向20的倍数取整 +extern const std::string kRtpG711DurMs; +// udp recv socket buffer size +extern const std::string kUdpRecvSocketBuffer; +} // namespace RtpProxy + +/** + * rtsp/rtmp播放器、推流器相关设置名, + * 这些设置项都不是配置文件用 + * 只用于设置某个播放器或推流器实例用 + */ +namespace Client { +// 指定网卡ip +extern const std::string kNetAdapter; +// 设置rtp传输类型,可选项有0(tcp,默认)、1(udp)、2(组播) +// 设置方法:player[PlayerBase::kRtpType] = 0/1/2; +extern const std::string kRtpType; +// rtsp认证用户名 +extern const std::string kRtspUser; +// rtsp认证用用户密码,可以是明文也可以是md5,md5密码生成方式 md5(username:realm:password) +extern const std::string kRtspPwd; +// rtsp认证用用户密码是否为md5类型 +extern const std::string kRtspPwdIsMD5; +// 握手超时时间,默认10,000 毫秒 +extern const std::string kTimeoutMS; +// rtp/rtmp包接收超时时间,默认5000秒 +extern const std::string kMediaTimeoutMS; +// rtsp/rtmp心跳时间,默认5000毫秒 +extern const std::string kBeatIntervalMS; +// 是否为性能测试模式,性能测试模式开启后不会解析rtp或rtmp包 +extern const std::string kBenchmarkMode; +// 播放器在触发播放成功事件时,是否等待所有track ready时再回调 +extern const std::string kWaitTrackReady; +// rtsp播放指定track,可选项有0(不指定,默认)、1(视频)、2(音频) +// 设置方法:player[Client::kPlayTrack] = 0/1/2; +extern const std::string kPlayTrack; +//设置代理url,目前只支持http协议 +extern const std::string kProxyUrl; +} // namespace Client +} // namespace mediakit + +#endif /* COMMON_CONFIG_H */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/macros.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/macros.h new file mode 100644 index 00000000..b1187810 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/macros.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MACROS_H +#define ZLMEDIAKIT_MACROS_H + +#include "Util/logger.h" +#include +#include +#if defined(__MACH__) +#include +#include +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#elif defined(__linux__) +#include +#include +#elif defined(_WIN32) +#define BIG_ENDIAN 1 +#define LITTLE_ENDIAN 0 +#define BYTE_ORDER LITTLE_ENDIAN +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +#ifndef CHECK +#define CHECK(exp, ...) ::mediakit::Assert_ThrowCpp(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__) +#endif // CHECK + +#ifndef CHECK_RET +#define CHECK_RET(...) \ + try { \ + CHECK(__VA_ARGS__); \ + } catch (AssertFailedException & ex) { \ + WarnL << ex.what(); \ + return; \ + } +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif // MAX + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif // MIN + +#ifndef CLEAR_ARR +#define CLEAR_ARR(arr) \ + for (auto &item : arr) { \ + item = 0; \ + } +#endif // CLEAR_ARR + +#define RTSP_SCHEMA "rtsp" +#define RTMP_SCHEMA "rtmp" +#define TS_SCHEMA "ts" +#define FMP4_SCHEMA "fmp4" +#define HLS_SCHEMA "hls" +#define HLS_FMP4_SCHEMA "hls.fmp4" + +#define VHOST_KEY "vhost" +#define DEFAULT_VHOST "__defaultVhost__" + +#ifdef __cplusplus +extern "C" { +#endif +extern void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str); +#ifdef __cplusplus +} +#endif + +namespace mediakit { + +class AssertFailedException : public std::runtime_error { +public: + template + AssertFailedException(T && ...args) : std::runtime_error(std::forward(args)...) {} +}; + +extern const char kServerName[]; + +template +void Assert_ThrowCpp(int failed, const char *exp, const char *func, const char *file, int line, ARGS &&...args) { + if (failed) { + std::stringstream ss; + toolkit::LoggerWrapper::appendLog(ss, std::forward(args)...); + Assert_Throw(failed, exp, func, file, line, ss.str().data()); + } +} + +} // namespace mediakit +#endif // ZLMEDIAKIT_MACROS_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Common/strCoding.h b/webrtc_player/android/zlm/src/main/cpp/include/Common/strCoding.h new file mode 100644 index 00000000..e715e74d --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Common/strCoding.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_HTTP_STRCODING_H_ +#define SRC_HTTP_STRCODING_H_ + +#include +#include +#include +namespace mediakit { + +class strCoding { +public: + static std::string UrlEncodePath(const std::string &str); //url路径 utf8编码 + static std::string UrlEncodeComponent(const std::string &str); // url参数 utf8编码 + static std::string UrlDecodePath(const std::string &str); //url路径 utf8解码 + static std::string UrlDecodeComponent(const std::string &str); // url参数 utf8解码 +#if defined(_WIN32) + static std::string UTF8ToGB2312(const std::string &str);//utf_8转为gb2312 + static std::string GB2312ToUTF8(const std::string &str); //gb2312 转utf_8 +#endif//defined(_WIN32) +private: + strCoding(void); + virtual ~strCoding(void); +}; + +} /* namespace mediakit */ + +#endif /* SRC_HTTP_STRCODING_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtmp.h b/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtmp.h new file mode 100644 index 00000000..636e9fb8 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtmp.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_COMMONRTMP_H +#define ZLMEDIAKIT_COMMONRTMP_H + +#include "Frame.h" +#include "Rtmp/RtmpCodec.h" + +namespace mediakit{ + +/** + * 通用 rtmp解码类 + */ +class CommonRtmpDecoder : public RtmpCodec { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + */ + CommonRtmpDecoder(const Track::Ptr &track) : RtmpCodec(track) {} + + /** + * 输入Rtmp并解码 + * @param rtmp Rtmp数据包 + */ + void inputRtmp(const RtmpPacket::Ptr &rtmp) override; +}; + +/** + * 通用 rtmp编码类 + */ +class CommonRtmpEncoder : public RtmpCodec { +public: + using Ptr = std::shared_ptr; + + CommonRtmpEncoder(const Track::Ptr &track) : RtmpCodec(track) {} + + /** + * 输入帧数据 + */ + bool inputFrame(const Frame::Ptr &frame) override; + +private: + uint8_t _audio_flv_flags { 0 }; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_COMMONRTMP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtp.h b/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtp.h new file mode 100644 index 00000000..96cb5457 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Extension/CommonRtp.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_COMMONRTP_H +#define ZLMEDIAKIT_COMMONRTP_H + +#include "Frame.h" +#include "Rtsp/RtpCodec.h" + +namespace mediakit{ + +/** + * 通用 rtp解码类 + */ +class CommonRtpDecoder : public RtpCodec { +public: + using Ptr = std::shared_ptr ; + + /** + * 构造函数 + * @param codec 编码id + * @param max_frame_size 允许的最大帧大小 + */ + CommonRtpDecoder(CodecId codec, size_t max_frame_size = 2 * 1024); + + /** + * 输入rtp并解码 + * @param rtp rtp数据包 + * @param key_pos 此参数内部强制转换为false,请忽略之 + */ + bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; + +private: + void obtainFrame(); + +private: + bool _drop_flag = false; + uint16_t _last_seq = 0; + uint64_t _last_stamp = 0; + size_t _max_frame_size; + CodecId _codec; + FrameImp::Ptr _frame; +}; + +/** + * 通用 rtp编码类 + */ +class CommonRtpEncoder : public RtpCodec { +public: + using Ptr = std::shared_ptr ; + + /** + * 输入帧数据并编码成rtp + */ + bool inputFrame(const Frame::Ptr &frame) override; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_COMMONRTP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Extension/Factory.h b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Factory.h new file mode 100644 index 00000000..8ec446bc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Factory.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FACTORY_H +#define ZLMEDIAKIT_FACTORY_H + +#include +#include "Rtmp/amf.h" +#include "Extension/Track.h" +#include "Extension/Frame.h" +#include "Rtsp/RtpCodec.h" +#include "Rtmp/RtmpCodec.h" +#include "Util/onceToken.h" + +#define REGISTER_STATIC_VAR_INNER(var_name, line) var_name##_##line##__ +#define REGISTER_STATIC_VAR(var_name, line) REGISTER_STATIC_VAR_INNER(var_name, line) + +#define REGISTER_CODEC(plugin) \ +static toolkit::onceToken REGISTER_STATIC_VAR(s_token, __LINE__) ([]() { \ + Factory::registerPlugin(plugin); \ +}); + +namespace mediakit { + +struct CodecPlugin { + CodecId (*getCodec)(); + Track::Ptr (*getTrackByCodecId)(int sample_rate, int channels, int sample_bit); + Track::Ptr (*getTrackBySdp)(const SdpTrack::Ptr &track); + RtpCodec::Ptr (*getRtpEncoderByCodecId)(uint8_t pt); + RtpCodec::Ptr (*getRtpDecoderByCodecId)(); + RtmpCodec::Ptr (*getRtmpEncoderByTrack)(const Track::Ptr &track); + RtmpCodec::Ptr (*getRtmpDecoderByTrack)(const Track::Ptr &track); + Frame::Ptr (*getFrameFromPtr)(const char *data, size_t bytes, uint64_t dts, uint64_t pts); +}; + +class Factory { +public: + /** + * 注册插件,非线程安全的 + */ + static void registerPlugin(const CodecPlugin &plugin); + + /** + * 根据codec_id 获取track + * @param codecId 编码id + * @param sample_rate 采样率,视频固定为90000 + * @param channels 音频通道数 + * @param sample_bit 音频采样位数 + */ + static Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0); + + ////////////////////////////////rtsp相关////////////////////////////////// + /** + * 根据sdp生成Track对象 + */ + static Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track); + + /** + * 根据c api 抽象的Track生成具体Track对象 + */ + static Track::Ptr getTrackByAbstractTrack(const Track::Ptr& track); + + /** + * 根据codec id生成rtp编码器 + * @param codec_id 编码id + * @param pt rtp payload type + */ + static RtpCodec::Ptr getRtpEncoderByCodecId(CodecId codec_id, uint8_t pt); + + /** + * 根据Track生成Rtp解包器 + */ + static RtpCodec::Ptr getRtpDecoderByCodecId(CodecId codec); + + + ////////////////////////////////rtmp相关////////////////////////////////// + + /** + * 根据amf对象获取视频相应的Track + * @param amf rtmp metadata中的videocodecid的值 + */ + static Track::Ptr getVideoTrackByAmf(const AMFValue &amf); + + /** + * 根据amf对象获取音频相应的Track + * @param amf rtmp metadata中的audiocodecid的值 + */ + static Track::Ptr getAudioTrackByAmf(const AMFValue& amf, int sample_rate, int channels, int sample_bit); + + /** + * 根据Track获取Rtmp的编码器 + * @param track 媒体描述对象 + */ + static RtmpCodec::Ptr getRtmpEncoderByTrack(const Track::Ptr &track); + + /** + * 根据Track获取Rtmp的解码器 + * @param track 媒体描述对象 + */ + static RtmpCodec::Ptr getRtmpDecoderByTrack(const Track::Ptr &track); + + /** + * 根据codecId获取rtmp的codec描述 + */ + static AMFValue getAmfByCodecId(CodecId codecId); + + static Frame::Ptr getFrameFromPtr(CodecId codec, const char *data, size_t size, uint64_t dts, uint64_t pts); + static Frame::Ptr getFrameFromBuffer(CodecId codec, toolkit::Buffer::Ptr data, uint64_t dts, uint64_t pts); +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_FACTORY_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Extension/Frame.h b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Frame.h new file mode 100644 index 00000000..da55e8fe --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Frame.h @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FRAME_H +#define ZLMEDIAKIT_FRAME_H + +#include +#include +#include +#include "Util/List.h" +#include "Util/TimeTicker.h" +#include "Common/Stamp.h" +#include "Network/Buffer.h" + +namespace mediakit { + +class Stamp; + +typedef enum { + TrackInvalid = -1, + TrackVideo = 0, + TrackAudio, + TrackTitle, + TrackApplication, + TrackMax +} TrackType; + +#define CODEC_MAP(XX) \ + XX(CodecH264, TrackVideo, 0, "H264", PSI_STREAM_H264, MOV_OBJECT_H264) \ + XX(CodecH265, TrackVideo, 1, "H265", PSI_STREAM_H265, MOV_OBJECT_HEVC) \ + XX(CodecAAC, TrackAudio, 2, "mpeg4-generic", PSI_STREAM_AAC, MOV_OBJECT_AAC) \ + XX(CodecG711A, TrackAudio, 3, "PCMA", PSI_STREAM_AUDIO_G711A, MOV_OBJECT_G711a) \ + XX(CodecG711U, TrackAudio, 4, "PCMU", PSI_STREAM_AUDIO_G711U, MOV_OBJECT_G711u) \ + XX(CodecOpus, TrackAudio, 5, "opus", PSI_STREAM_AUDIO_OPUS, MOV_OBJECT_OPUS) \ + XX(CodecL16, TrackAudio, 6, "L16", PSI_STREAM_RESERVED, MOV_OBJECT_NONE) \ + XX(CodecVP8, TrackVideo, 7, "VP8", PSI_STREAM_VP8, MOV_OBJECT_VP8) \ + XX(CodecVP9, TrackVideo, 8, "VP9", PSI_STREAM_VP9, MOV_OBJECT_VP9) \ + XX(CodecAV1, TrackVideo, 9, "AV1", PSI_STREAM_AV1, MOV_OBJECT_AV1) \ + XX(CodecJPEG, TrackVideo, 10, "JPEG", PSI_STREAM_JPEG_2000, MOV_OBJECT_JPEG) + +typedef enum { + CodecInvalid = -1, +#define XX(name, type, value, str, mpeg_id, mp4_id) name = value, + CODEC_MAP(XX) +#undef XX + CodecMax +} CodecId; + +/** + * 字符串转媒体类型转 + */ +TrackType getTrackType(const std::string &str); + +/** + * 媒体类型转字符串 + */ +const char* getTrackString(TrackType type); + +/** + * 根据SDP中描述获取codec_id + * @param str + * @return + */ +CodecId getCodecId(const std::string &str); + +/** + * 获取编码器名称 + */ +const char *getCodecName(CodecId codecId); + +/** + * 获取音视频类型 + */ +TrackType getTrackType(CodecId codecId); + +/** + * 根据codecid获取mov object id + */ +int getMovIdByCodec(CodecId codecId); + +/** + * 根据mov object id获取CodecId + */ +CodecId getCodecByMovId(int object_id); + +/** + * 根据codecid获取mpeg id + */ +int getMpegIdByCodec(CodecId codec); + +/** + * 根据mpeg id获取CodecId + */ +CodecId getCodecByMpegId(int mpeg_id); + +/** + * 编码信息的抽象接口 + */ +class CodecInfo { +public: + using Ptr = std::shared_ptr; + + virtual ~CodecInfo() = default; + + /** + * 获取编解码器类型 + */ + virtual CodecId getCodecId() const = 0; + + /** + * 获取编码器名称 + */ + const char *getCodecName() const; + + /** + * 获取音视频类型 + */ + TrackType getTrackType() const; + + /** + * 获取音视频类型描述 + */ + std::string getTrackTypeStr() const; + + /** + * 设置track index, 用于支持多track + */ + void setIndex(int index) { _index = index; } + + /** + * 获取track index, 用于支持多track + */ + int getIndex() const { return _index < 0 ? (int)getTrackType() : _index; } + +private: + int _index = -1; +}; + +/** + * 帧类型的抽象接口 + */ +class Frame : public toolkit::Buffer, public CodecInfo { +public: + using Ptr = std::shared_ptr; + + /** + * 返回解码时间戳,单位毫秒 + */ + virtual uint64_t dts() const = 0; + + /** + * 返回显示时间戳,单位毫秒 + */ + virtual uint64_t pts() const { return dts(); } + + /** + * 前缀长度,譬如264前缀为0x00 00 00 01,那么前缀长度就是4 + * aac前缀则为7个字节 + */ + virtual size_t prefixSize() const = 0; + + /** + * 返回是否为关键帧 + */ + virtual bool keyFrame() const = 0; + + /** + * 是否为配置帧,譬如sps pps vps + */ + virtual bool configFrame() const = 0; + + /** + * 是否可以缓存 + */ + virtual bool cacheAble() const { return true; } + + /** + * 该帧是否可以丢弃 + * SEI/AUD帧可以丢弃 + * 默认都不能丢帧 + */ + virtual bool dropAble() const { return false; } + + /** + * 是否为可解码帧 + * sps pps等帧不能解码 + */ + virtual bool decodeAble() const { + if (getTrackType() != TrackVideo) { + //非视频帧都可以解码 + return true; + } + //默认非sps pps帧都可以解码 + return !configFrame(); + } + + /** + * 返回可缓存的frame + */ + static Ptr getCacheAbleFrame(const Ptr &frame); + +private: + //对象个数统计 + toolkit::ObjectStatistic _statistic; +}; + +class FrameImp : public Frame { +public: + using Ptr = std::shared_ptr; + + template + static std::shared_ptr create() { +#if 0 + static ResourcePool packet_pool; + static onceToken token([]() { + packet_pool.setSize(1024); + }); + auto ret = packet_pool.obtain2(); + ret->_buffer.clear(); + ret->_prefix_size = 0; + ret->_dts = 0; + ret->_pts = 0; + return ret; +#else + return std::shared_ptr(new C()); +#endif + } + + char *data() const override { return (char *)_buffer.data(); } + size_t size() const override { return _buffer.size(); } + uint64_t dts() const override { return _dts; } + uint64_t pts() const override { return _pts ? _pts : _dts; } + size_t prefixSize() const override { return _prefix_size; } + CodecId getCodecId() const override { return _codec_id; } + bool keyFrame() const override { return false; } + bool configFrame() const override { return false; } + +public: + CodecId _codec_id = CodecInvalid; + uint64_t _dts = 0; + uint64_t _pts = 0; + size_t _prefix_size = 0; + toolkit::BufferLikeString _buffer; + +private: + //对象个数统计 + toolkit::ObjectStatistic _statistic; + +protected: + friend class toolkit::ResourcePool_l; + FrameImp() = default; +}; + +// 包装一个指针成不可缓存的frame +class FrameFromPtr : public Frame { +public: + using Ptr = std::shared_ptr; + + FrameFromPtr(CodecId codec_id, char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0, bool is_key = false) + : FrameFromPtr(ptr, size, dts, pts, prefix_size, is_key) { + _codec_id = codec_id; + } + + char *data() const override { return _ptr; } + size_t size() const override { return _size; } + uint64_t dts() const override { return _dts; } + uint64_t pts() const override { return _pts ? _pts : dts(); } + size_t prefixSize() const override { return _prefix_size; } + bool cacheAble() const override { return false; } + bool keyFrame() const override { return _is_key; } + bool configFrame() const override { return false; } + + CodecId getCodecId() const override { + if (_codec_id == CodecInvalid) { + throw std::invalid_argument("Invalid codec type of FrameFromPtr"); + } + return _codec_id; + } + +protected: + FrameFromPtr() = default; + + FrameFromPtr(char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0, bool is_key = false) { + _ptr = ptr; + _size = size; + _dts = dts; + _pts = pts; + _prefix_size = prefix_size; + _is_key = is_key; + } + +protected: + bool _is_key; + char *_ptr; + uint64_t _dts; + uint64_t _pts = 0; + size_t _size; + size_t _prefix_size; + CodecId _codec_id = CodecInvalid; +}; + +/** + * 一个Frame类中可以有多个帧(AAC),时间戳会变化 + * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 + * 一个复合帧可以通过无内存拷贝的方式切割成多个子Frame + * 提供该类的目的是切割复合帧时防止内存拷贝,提高性能 + */ +template +class FrameInternalBase : public Parent { +public: + using Ptr = std::shared_ptr; + FrameInternalBase(Frame::Ptr parent_frame, char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0) + : Parent(parent_frame->getCodecId(), ptr, size, dts, pts, prefix_size) { + _parent_frame = std::move(parent_frame); + this->setIndex(_parent_frame->getIndex()); + } + + bool cacheAble() const override { return _parent_frame->cacheAble(); } + +private: + Frame::Ptr _parent_frame; +}; + +/** + * 一个Frame类中可以有多个帧,他们通过 0x 00 00 01 分隔 + * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 + * 一个复合帧可以通过无内存拷贝的方式切割成多个子Frame + * 提供该类的目的是切割复合帧时防止内存拷贝,提高性能 + */ +template +class FrameInternal : public FrameInternalBase { +public: + using Ptr = std::shared_ptr; + FrameInternal(const Frame::Ptr &parent_frame, char *ptr, size_t size, size_t prefix_size) + : FrameInternalBase(parent_frame, ptr, size, parent_frame->dts(), parent_frame->pts(), prefix_size) {} +}; + +// 管理一个指针生命周期并生产一个frame +class FrameAutoDelete : public FrameFromPtr { +public: + template + FrameAutoDelete(ARGS &&...args) : FrameFromPtr(std::forward(args)...) {} + + ~FrameAutoDelete() override { delete[] _ptr; }; + + bool cacheAble() const override { return true; } +}; + +// 把一个不可缓存的frame声明为可缓存的 +template +class FrameToCache : public Parent { +public: + template + FrameToCache(ARGS &&...args) : Parent(std::forward(args)...) {}; + + bool cacheAble() const override { + return true; + } +}; + +// 该对象的功能是把一个不可缓存的帧转换成可缓存的帧 +class FrameCacheAble : public FrameFromPtr { +public: + using Ptr = std::shared_ptr; + + FrameCacheAble(const Frame::Ptr &frame, bool force_key_frame = false, toolkit::Buffer::Ptr buf = nullptr) { + setIndex(frame->getIndex()); + if (frame->cacheAble()) { + _ptr = frame->data(); + _buffer = frame; + } else if (buf) { + _ptr = frame->data(); + _buffer = std::move(buf); + } else { + auto buffer = std::make_shared(); + buffer->assign(frame->data(), frame->size()); + _ptr = buffer->data(); + _buffer = std::move(buffer); + } + _size = frame->size(); + _dts = frame->dts(); + _pts = frame->pts(); + _prefix_size = frame->prefixSize(); + _codec_id = frame->getCodecId(); + _key = force_key_frame ? true : frame->keyFrame(); + _config = frame->configFrame(); + _drop_able = frame->dropAble(); + _decode_able = frame->decodeAble(); + } + + /** + * 可以被缓存 + */ + bool cacheAble() const override { return true; } + bool keyFrame() const override { return _key; } + bool configFrame() const override { return _config; } + bool dropAble() const override { return _drop_able; } + bool decodeAble() const override { return _decode_able; } + +private: + bool _key; + bool _config; + bool _drop_able; + bool _decode_able; + toolkit::Buffer::Ptr _buffer; +}; + +//该类实现frame级别的时间戳覆盖 +class FrameStamp : public Frame { +public: + using Ptr = std::shared_ptr; + FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp); + ~FrameStamp() override {} + + uint64_t dts() const override { return (uint64_t)_dts; } + uint64_t pts() const override { return (uint64_t)_pts; } + size_t prefixSize() const override { return _frame->prefixSize(); } + bool keyFrame() const override { return _frame->keyFrame(); } + bool configFrame() const override { return _frame->configFrame(); } + bool cacheAble() const override { return _frame->cacheAble(); } + bool dropAble() const override { return _frame->dropAble(); } + bool decodeAble() const override { return _frame->decodeAble(); } + char *data() const override { return _frame->data(); } + size_t size() const override { return _frame->size(); } + CodecId getCodecId() const override { return _frame->getCodecId(); } + +private: + int64_t _dts; + int64_t _pts; + Frame::Ptr _frame; +}; + +/** + * 该对象可以把Buffer对象转换成可缓存的Frame对象 + */ +template +class FrameFromBuffer : public Parent { +public: + /** + * 构造frame + * @param buf 数据缓存 + * @param dts 解码时间戳 + * @param pts 显示时间戳 + * @param prefix 帧前缀长度 + * @param offset buffer有效数据偏移量 + */ + FrameFromBuffer(toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0) + : Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix) { + _buf = std::move(buf); + } + + /** + * 构造frame + * @param buf 数据缓存 + * @param dts 解码时间戳 + * @param pts 显示时间戳 + * @param prefix 帧前缀长度 + * @param offset buffer有效数据偏移量 + * @param codec 帧类型 + */ + FrameFromBuffer(CodecId codec, toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0) + : Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix) { + _buf = std::move(buf); + } + + /** + * 该帧可缓存 + */ + bool cacheAble() const override { return true; } + +private: + toolkit::Buffer::Ptr _buf; +}; + +/** + * 合并一些时间戳相同的frame + */ +class FrameMerger { +public: + using onOutput = std::function; + using Ptr = std::shared_ptr; + enum { + none = 0, + h264_prefix, + mp4_nal_size, + }; + + FrameMerger(int type); + + /** + * 刷新输出缓冲,注意此时会调用FrameMerger::inputFrame传入的onOutput回调 + * 请注意回调捕获参数此时是否有效 + */ + void flush(); + void clear(); + bool inputFrame(const Frame::Ptr &frame, onOutput cb, toolkit::BufferLikeString *buffer = nullptr); + +private: + bool willFlush(const Frame::Ptr &frame) const; + void doMerge(toolkit::BufferLikeString &buffer, const Frame::Ptr &frame) const; + +private: + int _type; + bool _have_decode_able_frame = false; + onOutput _cb; + toolkit::List _frame_cache; +}; + +/** + * 写帧接口的抽象接口类 + */ +class FrameWriterInterface { +public: + using Ptr = std::shared_ptr; + virtual ~FrameWriterInterface() = default; + + /** + * 写入帧数据 + */ + virtual bool inputFrame(const Frame::Ptr &frame) = 0; + + /** + * 刷新输出所有frame缓存 + */ + virtual void flush() {}; +}; + +/** + * 支持代理转发的帧环形缓存 + */ +class FrameDispatcher : public FrameWriterInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 添加代理 + */ + FrameWriterInterface* addDelegate(FrameWriterInterface::Ptr delegate) { + std::lock_guard lck(_mtx); + return _delegates.emplace(delegate.get(), std::move(delegate)).first->second.get(); + } + + FrameWriterInterface* addDelegate(std::function cb); + + /** + * 删除代理 + */ + void delDelegate(FrameWriterInterface *ptr) { + std::lock_guard lck(_mtx); + _delegates.erase(ptr); + } + + /** + * 写入帧并派发 + */ + bool inputFrame(const Frame::Ptr &frame) override { + std::lock_guard lck(_mtx); + doStatistics(frame); + bool ret = false; + for (auto &pr : _delegates) { + if (pr.second->inputFrame(frame)) { + ret = true; + } + } + return ret; + } + + /** + * 返回代理个数 + */ + size_t size() const { + std::lock_guard lck(_mtx); + return _delegates.size(); + } + + void clear() { + std::lock_guard lck(_mtx); + _delegates.clear(); + } + + /** + * 获取累计关键帧数 + */ + uint64_t getVideoKeyFrames() const { + std::lock_guard lck(_mtx); + return _video_key_frames; + } + + /** + * 获取帧数 + */ + uint64_t getFrames() const { + std::lock_guard lck(_mtx); + return _frames; + } + + size_t getVideoGopSize() const { + std::lock_guard lck(_mtx); + return _gop_size; + } + + size_t getVideoGopInterval() const { + std::lock_guard lck(_mtx); + return _gop_interval_ms; + } + + int64_t getDuration() const { + std::lock_guard lck(_mtx); + return _stamp.getRelativeStamp(); + } + +private: + void doStatistics(const Frame::Ptr &frame) { + if (!frame->configFrame() && !frame->dropAble()) { + // 忽略配置帧与可丢弃的帧 + ++_frames; + int64_t out; + _stamp.revise(frame->dts(), frame->pts(), out, out); + if (frame->keyFrame() && frame->getTrackType() == TrackVideo) { + // 遇视频关键帧时统计 + ++_video_key_frames; + _gop_size = _frames - _last_frames; + _gop_interval_ms = _ticker.elapsedTime(); + _last_frames = _frames; + _ticker.resetTime(); + } + } + } + +private: + toolkit::Ticker _ticker; + size_t _gop_interval_ms = 0; + size_t _gop_size = 0; + uint64_t _last_frames = 0; + uint64_t _frames = 0; + uint64_t _video_key_frames = 0; + Stamp _stamp; + mutable std::recursive_mutex _mtx; + std::map _delegates; +}; + +} // namespace mediakit +#endif // ZLMEDIAKIT_FRAME_H \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Extension/Track.h b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Track.h new file mode 100644 index 00000000..ba101282 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Extension/Track.h @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TRACK_H +#define ZLMEDIAKIT_TRACK_H + +#include +#include +#include "Frame.h" +#include "Rtsp/Rtsp.h" + +namespace mediakit{ + +/** + * 媒体通道描述类,也支持帧输入输出 + */ +class Track : public FrameDispatcher, public CodecInfo { +public: + using Ptr = std::shared_ptr; + + /** + * 默认构造 + */ + Track() = default; + + /** + * 复制拷贝,只能拷贝派生类的信息, + * 环形缓存和代理关系不能拷贝,否则会关系紊乱 + */ + Track(const Track &that) { + _bit_rate = that._bit_rate; + setIndex(that.getIndex()); + } + + /** + * 是否准备好,准备好才能获取譬如sps pps等信息 + */ + virtual bool ready() const = 0; + + /** + * 克隆接口,用于复制本对象用 + * 在调用该接口时只会复制派生类的信息 + * 环形缓存和代理关系不能拷贝,否则会关系紊乱 + */ + virtual Track::Ptr clone() const = 0; + + /** + * 更新track信息,比如触发sps/pps解析 + */ + virtual bool update() { return false; } + + /** + * 生成sdp + * @return sdp对象 + */ + virtual Sdp::Ptr getSdp(uint8_t payload_type) const = 0; + + /** + * 获取extra data, 一般用于rtmp/mp4生成 + */ + virtual toolkit::Buffer::Ptr getExtraData() const { return nullptr; } + + /** + * 设置extra data, + */ + virtual void setExtraData(const uint8_t *data, size_t size) {} + + /** + * 返回比特率 + * @return 比特率 + */ + virtual int getBitRate() const { return _bit_rate; } + + /** + * 设置比特率 + * @param bit_rate 比特率 + */ + virtual void setBitRate(int bit_rate) { _bit_rate = bit_rate; } + +private: + int _bit_rate = 0; +}; + +/** + * 视频通道描述Track类,支持获取宽高fps信息 + */ +class VideoTrack : public Track { +public: + using Ptr = std::shared_ptr; + + /** + * 返回视频高度 + */ + virtual int getVideoHeight() const { return 0; } + + /** + * 返回视频宽度 + */ + virtual int getVideoWidth() const { return 0; } + + /** + * 返回视频fps + */ + virtual float getVideoFps() const { return 0; } +}; + +class VideoTrackImp : public VideoTrack { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param codec_id 编码类型 + * @param width 宽 + * @param height 高 + * @param fps 帧率 + */ + VideoTrackImp(CodecId codec_id, int width, int height, int fps) { + _codec_id = codec_id; + _width = width; + _height = height; + _fps = fps; + } + + int getVideoWidth() const override { return _width; } + int getVideoHeight() const override { return _height; } + float getVideoFps() const override { return _fps; } + bool ready() const override { return true; } + + Track::Ptr clone() const override { return std::make_shared(*this); } + Sdp::Ptr getSdp(uint8_t payload_type) const override { return nullptr; } + CodecId getCodecId() const override { return _codec_id; } + +private: + CodecId _codec_id; + int _width = 0; + int _height = 0; + float _fps = 0; +}; + +/** + * 音频Track派生类,支持采样率通道数,采用位数信息 + */ +class AudioTrack : public Track { +public: + using Ptr = std::shared_ptr; + + /** + * 返回音频采样率 + */ + virtual int getAudioSampleRate() const {return 0;}; + + /** + * 返回音频采样位数,一般为16或8 + */ + virtual int getAudioSampleBit() const {return 0;}; + + /** + * 返回音频通道数 + */ + virtual int getAudioChannel() const {return 0;}; +}; + +class AudioTrackImp : public AudioTrack{ +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param codecId 编码类型 + * @param sample_rate 采样率(HZ) + * @param channels 通道数 + * @param sample_bit 采样位数,一般为16 + */ + AudioTrackImp(CodecId codecId, int sample_rate, int channels, int sample_bit){ + _codecid = codecId; + _sample_rate = sample_rate; + _channels = channels; + _sample_bit = sample_bit; + } + + /** + * 返回编码类型 + */ + CodecId getCodecId() const override{ + return _codecid; + } + + /** + * 是否已经初始化 + */ + bool ready() const override { + return true; + } + + /** + * 返回音频采样率 + */ + int getAudioSampleRate() const override{ + return _sample_rate; + } + + /** + * 返回音频采样位数,一般为16或8 + */ + int getAudioSampleBit() const override{ + return _sample_bit; + } + + /** + * 返回音频通道数 + */ + int getAudioChannel() const override{ + return _channels; + } + + Track::Ptr clone() const override { return std::make_shared(*this); } + Sdp::Ptr getSdp(uint8_t payload_type) const override { return nullptr; } + +private: + CodecId _codecid; + int _sample_rate; + int _channels; + int _sample_bit; +}; + +class TrackSource { +public: + virtual ~TrackSource() = default; + + /** + * 获取全部的Track + * @param trackReady 是否获取全部已经准备好的Track + */ + virtual std::vector getTracks(bool trackReady = true) const = 0; + + /** + * 获取特定Track + * @param type track类型 + * @param trackReady 是否获取全部已经准备好的Track + */ + Track::Ptr getTrack(TrackType type , bool trackReady = true) const { + auto tracks = getTracks(trackReady); + for(auto &track : tracks){ + if(track->getTrackType() == type){ + return track; + } + } + return nullptr; + } +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_TRACK_H \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSource.h new file mode 100644 index 00000000..55ab1a70 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSource.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FMP4MEDIASOURCE_H +#define ZLMEDIAKIT_FMP4MEDIASOURCE_H + +#include "Common/MediaSource.h" +#include "Common/PacketCache.h" +#include "Util/RingBuffer.h" + +#define FMP4_GOP_SIZE 512 + +namespace mediakit { + +//FMP4直播数据包 +class FMP4Packet : public toolkit::BufferString{ +public: + using Ptr = std::shared_ptr; + + template + FMP4Packet(ARGS && ...args) : toolkit::BufferString(std::forward(args)...) {}; + +public: + uint64_t time_stamp = 0; +}; + +//FMP4直播源 +class FMP4MediaSource final : public MediaSource, public toolkit::RingDelegate, private PacketCache{ +public: + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = toolkit::RingBuffer; + + FMP4MediaSource(const MediaTuple& tuple, + int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, tuple), _ring_size(ring_size) {} + + ~FMP4MediaSource() override { flush(); } + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) override { + _ring->getInfoList(cb, on_change); + } + + /** + * 获取fmp4 init segment + */ + const std::string &getInitSegment() const{ + return _init_segment; + } + + /** + * 设置fmp4 init segment + * @param str init segment + */ + void setInitSegment(std::string str) { + _init_segment = std::move(str); + createRing(); + } + + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 输入FMP4包 + * @param packet FMP4包 + * @param key 是否为关键帧第一个包 + */ + void onWrite(FMP4Packet::Ptr packet, bool key) override { + if (!_ring) { + createRing(); + } + if (key) { + _have_video = true; + } + _speed[TrackVideo] += packet->size(); + auto stamp = packet->time_stamp; + PacketCache::inputPacket(stamp, true, std::move(packet), key); + } + + /** + * 情况GOP缓存 + */ + void clearCache() override { + PacketCache::clearCache(); + _ring->clearCache(); + } + +private: + void createRing(){ + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _ring = std::make_shared(_ring_size, [weak_self](int size) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->onReaderChanged(size); + }); + if (!_init_segment.empty()) { + regist(); + } + } + + /** + * 合并写回调 + * @param packet_list 合并写缓存列队 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > packet_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + _ring->write(std::move(packet_list), _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + int _ring_size; + std::string _init_segment; + RingType::Ptr _ring; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_FMP4MEDIASOURCE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSourceMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSourceMuxer.h new file mode 100644 index 00000000..2725531e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/FMP4/FMP4MediaSourceMuxer.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H +#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H + +#include "FMP4MediaSource.h" +#include "Record/MP4Muxer.h" + +namespace mediakit { + +class FMP4MediaSourceMuxer final : public MP4MuxerMemory, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + FMP4MediaSourceMuxer(const MediaTuple& tuple, const ProtocolOption &option) { + _option = option; + _media_src = std::make_shared(tuple); + } + + ~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); }; + + void setListener(const std::weak_ptr &listener){ + setDelegate(listener); + _media_src->setListener(shared_from_this()); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = _option.fmp4_demand ? size : true; + if (!size && _option.fmp4_demand) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache && _option.fmp4_demand) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled || !_option.fmp4_demand) { + return MP4MuxerMemory::inputFrame(frame); + } + return false; + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true; + } + + void addTrackCompleted() override { + MP4MuxerMemory::addTrackCompleted(); + _media_src->setInitSegment(getInitSegment()); + } + +protected: + void onSegmentData(std::string string, uint64_t stamp, bool key_frame) override { + if (string.empty()) { + return; + } + FMP4Packet::Ptr packet = std::make_shared(std::move(string)); + packet->time_stamp = stamp; + _media_src->onWrite(std::move(packet), key_frame); + } + +private: + bool _enabled = true; + bool _clear_cache = false; + ProtocolOption _option; + FMP4MediaSource::Ptr _media_src; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsParser.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsParser.h new file mode 100644 index 00000000..e61540e7 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsParser.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HLSPARSER_H +#define HTTP_HLSPARSER_H + +#include +#include +#include + +namespace mediakit { + +typedef struct{ + //url地址 + std::string url; + //ts切片长度 + float duration; + + //////内嵌m3u8////// + //节目id + int program_id; + //带宽 + int bandwidth; + //宽度 + int width; + //高度 + int height; +} ts_segment; + +class HlsParser { +public: + bool parse(const std::string &http_url,const std::string &m3u8); + + /** + * 是否存在#EXTM3U字段,是否为m3u8文件 + */ + bool isM3u8() const; + + /** + * #EXT-X-ALLOW-CACHE值,是否允许cache + */ + bool allowCache() const; + + /** + * 是否存在#EXT-X-ENDLIST字段,是否为直播 + */ + bool isLive() const ; + + /** + * #EXT-X-VERSION值,版本号 + */ + int getVersion() const; + + /** + * #EXT-X-TARGETDURATION字段值 + */ + int getTargetDur() const; + + /** + * #EXT-X-MEDIA-SEQUENCE字段值,该m3u8序号 + */ + int64_t getSequence() const; + + /** + * 内部是否含有子m3u8 + */ + bool isM3u8Inner() const; + + /** + * 得到总时间 + */ + float getTotalDuration() const; + +protected: + /** + * 解析m3u8文件回调 + * @param is_m3u8_inner 该m3u8文件中是否包含多个hls地址 + * @param sequence ts序号 + * @param ts_list ts地址列表 + * @return 是否解析成功,返回false时,将导致HlsParser::parse返回false + */ + virtual bool onParsed(bool is_m3u8_inner, int64_t sequence, const std::map &ts_list) = 0; + +private: + bool _is_m3u8 = false; + bool _allow_cache = false; + bool _is_live = true; + int _version = 0; + int _target_dur = 0; + float _total_dur = 0; + int64_t _sequence = 0; + //每部是否有m3u8 + bool _is_m3u8_inner = false; +}; + +}//namespace mediakit +#endif //HTTP_HLSPARSER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsPlayer.h new file mode 100644 index 00000000..8f4a84b0 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HlsPlayer.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HLSPLAYER_H +#define HTTP_HLSPLAYER_H + +#include "Player/PlayerBase.h" +#include "HttpTSPlayer.h" +#include "HlsParser.h" +#include "Rtp/TSDecoder.h" + +#define MIN_TIMEOUT_MULTIPLE 2 +#define MAX_TIMEOUT_MULTIPLE 5 +#define MAX_TRY_FETCH_INDEX_TIMES 5 +#define MAX_TS_DOWNLOAD_FAILED_COUNT 10 + +namespace mediakit { + +class HlsDemuxer : public MediaSinkInterface , public TrackSource, public std::enable_shared_from_this { +public: + ~HlsDemuxer() override { _timer = nullptr; } + + void start(const toolkit::EventPoller::Ptr &poller, TrackListener *listener); + bool inputFrame(const Frame::Ptr &frame) override; + bool addTrack(const Track::Ptr &track) override { return _delegate.addTrack(track); } + void addTrackCompleted() override { _delegate.addTrackCompleted(); } + void resetTracks() override { ((MediaSink &)_delegate).resetTracks(); } + std::vector getTracks(bool ready = true) const override { return _delegate.getTracks(ready); } + void pushTask(std::function task); + +private: + void onTick(); + int64_t getBufferMS(); + int64_t getPlayPosition(); + void setPlayPosition(int64_t pos); + +private: + int64_t _ticker_offset = 0; + toolkit::Ticker _ticker; + toolkit::Timer::Ptr _timer; + MediaSinkDelegate _delegate; + std::deque > > _frame_cache; +}; + +class HlsPlayer : public HttpClientImp , public PlayerBase , public HlsParser{ +public: + HlsPlayer(const toolkit::EventPoller::Ptr &poller); + + /** + * 开始播放 + * start play + */ + void play(const std::string &url) override; + + /** + * 停止播放 + * stop play + */ + void teardown() override; + +protected: + /** + * 收到ts包 + * Received ts package + * @param data ts数据负载 ts data payload + * @param len ts包长度 ts package length + */ + virtual void onPacket(const char *data, size_t len) = 0; + +private: + bool onParsed(bool is_m3u8_inner, int64_t sequence, const map &ts_map) override; + void onResponseHeader(const std::string &status, const HttpHeader &headers) override; + void onResponseBody(const char *buf, size_t size) override; + void onResponseCompleted(const toolkit::SockException &e) override; + bool onRedirectUrl(const std::string &url, bool temporary) override; + +private: + void playDelay(float delay_sec = 0); + float delaySecond(); + void fetchSegment(); + void teardown_l(const toolkit::SockException &ex); + void fetchIndexFile(); + +private: + struct UrlComp { + // url忽略?后面的参数 + // Ignore the parameters after the url? + bool operator()(const std::string& __x, const std::string& __y) const { + return toolkit::split(__x,"?")[0] < toolkit::split(__y,"?")[0]; + } + }; + +private: + bool _play_result = false; + int64_t _last_sequence = -1; + std::string _m3u8; + std::string _play_url; + toolkit::Timer::Ptr _timer; + toolkit::Timer::Ptr _timer_ts; + toolkit::Ticker _wait_index_update_ticker; + std::list _ts_list; + std::list _ts_url_sort; + std::set _ts_url_cache; + HttpTSPlayer::Ptr _http_ts_player; + int _timeout_multiple = MIN_TIMEOUT_MULTIPLE; + int _try_fetch_index_times = 0; + int _ts_download_failed_count = 0; +}; + +class HlsPlayerImp : public PlayerImp, private TrackListener { +public: + using Ptr = std::shared_ptr; + HlsPlayerImp(const toolkit::EventPoller::Ptr &poller = nullptr); + +private: + //// HlsPlayer override//// + void onPacket(const char *data, size_t len) override; + +private: + //// PlayerBase override//// + void onPlayResult(const toolkit::SockException &ex) override; + std::vector getTracks(bool ready = true) const override; + void onShutdown(const toolkit::SockException &ex) override; + +private: + //// TrackListener override//// + bool addTrack(const Track::Ptr &track) override { return true; }; + void addTrackCompleted() override; + +private: + DecoderImp::Ptr _decoder; + MediaSinkInterface::Ptr _demuxer; +}; + +}//namespace mediakit +#endif //HTTP_HLSPLAYER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpBody.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpBody.h new file mode 100644 index 00000000..3ba60565 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpBody.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FILEREADER_H +#define ZLMEDIAKIT_FILEREADER_H + +#include +#include +#include "Network/Buffer.h" +#include "Util/ResourcePool.h" +#include "Util/logger.h" +#include "Thread/WorkThreadPool.h" + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b) ) +#endif //MIN + +namespace mediakit { + +/** + * http content部分基类定义 + */ +class HttpBody : public std::enable_shared_from_this{ +public: + using Ptr = std::shared_ptr; + virtual ~HttpBody() = default; + + /** + * 剩余数据大小,如果返回-1, 那么就不设置content-length + */ + virtual int64_t remainSize() { return 0;}; + + /** + * 读取一定字节数,返回大小可能小于size + * @param size 请求大小 + * @return 字节对象,如果读完了,那么请返回nullptr + */ + virtual toolkit::Buffer::Ptr readData(size_t size) { return nullptr;}; + + /** + * 异步请求读取一定字节数,返回大小可能小于size + * @param size 请求大小 + * @param cb 回调函数 + */ + virtual void readDataAsync(size_t size,const std::function &cb){ + //由于unix和linux是通过mmap的方式读取文件,所以把读文件操作放在后台线程并不能提高性能 + //反而会由于频繁的线程切换导致性能降低以及延时增加,所以我们默认同步获取文件内容 + //(其实并没有读,拷贝文件数据时在内核态完成文件读) + cb(readData(size)); + } + + /** + * 使用sendfile优化文件发送 + * @param fd socket fd + * @return 0成功,其他为错误代码 + */ + virtual int sendFile(int fd) { + return -1; + } +}; + +/** + * std::string类型的content + */ +class HttpStringBody : public HttpBody{ +public: + using Ptr = std::shared_ptr; + HttpStringBody(std::string str); + + int64_t remainSize() override; + toolkit::Buffer::Ptr readData(size_t size) override ; + +private: + size_t _offset = 0; + mutable std::string _str; +}; + +/** + * Buffer类型的content + */ +class HttpBufferBody : public HttpBody{ +public: + using Ptr = std::shared_ptr; + HttpBufferBody(toolkit::Buffer::Ptr buffer); + + int64_t remainSize() override; + toolkit::Buffer::Ptr readData(size_t size) override; + +private: + toolkit::Buffer::Ptr _buffer; +}; + +/** + * 文件类型的content + */ +class HttpFileBody : public HttpBody { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param file_path 文件路径 + * @param use_mmap 是否使用mmap方式访问文件 + */ + HttpFileBody(const std::string &file_path, bool use_mmap = true); + + /** + * 设置读取范围 + * @param offset 相对文件头的偏移量 + * @param max_size 最大读取字节数 + */ + void setRange(uint64_t offset, uint64_t max_size); + + int64_t remainSize() override; + toolkit::Buffer::Ptr readData(size_t size) override; + int sendFile(int fd) override; + +private: + int64_t _read_to = 0; + uint64_t _file_offset = 0; + std::shared_ptr _fp; + std::shared_ptr _map_addr; + toolkit::ResourcePool _pool; +}; + +class HttpArgs; + +/** + * http MultiForm 方式提交的http content + */ +class HttpMultiFormBody : public HttpBody { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param args http提交参数列表 + * @param filePath 文件路径 + * @param boundary boundary字符串 + */ + HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY"); + int64_t remainSize() override ; + toolkit::Buffer::Ptr readData(size_t size) override; + +public: + static std::string multiFormBodyPrefix(const HttpArgs &args,const std::string &boundary,const std::string &fileName); + static std::string multiFormBodySuffix(const std::string &boundary); + static std::string multiFormContentType(const std::string &boundary); + +private: + uint64_t _offset = 0; + int64_t _totalSize; + std::string _bodyPrefix; + std::string _bodySuffix; + HttpFileBody::Ptr _fileBody; +}; + +}//namespace mediakit + +#endif //ZLMEDIAKIT_FILEREADER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpChunkedSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpChunkedSplitter.h new file mode 100644 index 00000000..52a58053 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpChunkedSplitter.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H +#define ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H + +#include +#include "HttpRequestSplitter.h" + +namespace mediakit{ + +class HttpChunkedSplitter : public HttpRequestSplitter { +public: + /** + * len == 0时代表结束 + */ + using onChunkData = std::function; + + HttpChunkedSplitter(const onChunkData &cb) { _onChunkData = cb; }; + ~HttpChunkedSplitter() override { _onChunkData = nullptr; }; + +protected: + ssize_t onRecvHeader(const char *data,size_t len) override; + void onRecvContent(const char *data,size_t len) override; + const char *onSearchPacketTail(const char *data,size_t len) override; + +protected: + virtual void onRecvChunk(const char *data,size_t len){ + if(_onChunkData){ + _onChunkData(data,len); + } + }; + +private: + onChunkData _onChunkData; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClient.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClient.h new file mode 100644 index 00000000..06a0cdb8 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClient.h @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Http_HttpClient_h +#define Http_HttpClient_h + +#include +#include +#include +#include +#include "Util/util.h" +#include "Util/mini.h" +#include "Network/TcpClient.h" +#include "Common/Parser.h" +#include "HttpRequestSplitter.h" +#include "HttpCookie.h" +#include "HttpChunkedSplitter.h" +#include "Common/strCoding.h" +#include "HttpBody.h" + +namespace mediakit { + +class HttpArgs : public std::map { +public: + std::string make() const { + std::string ret; + for (auto &pr : *this) { + ret.append(pr.first); + ret.append("="); + ret.append(strCoding::UrlEncodeComponent(pr.second)); + ret.append("&"); + } + if (ret.size()) { + ret.pop_back(); + } + return ret; + } +}; + +class HttpClient : public toolkit::TcpClient, public HttpRequestSplitter { +public: + using HttpHeader = StrCaseMap; + using Ptr = std::shared_ptr; + + /** + * 发送http[s]请求 + * @param url 请求url + */ + virtual void sendRequest(const std::string &url); + + /** + * 重置对象 + */ + virtual void clear(); + + /** + * 设置http方法 + * @param method GET/POST等 + */ + void setMethod(std::string method); + + /** + * 覆盖http头 + * @param header + */ + void setHeader(HttpHeader header); + + HttpClient &addHeader(std::string key, std::string val, bool force = false); + + /** + * 设置http content + * @param body http content + */ + void setBody(std::string body); + + /** + * 设置http content + * @param body http content + */ + void setBody(HttpBody::Ptr body); + + /** + * 获取回复,在收到完整回复后有效 + */ + const Parser &response() const; + + /** + * 获取回复header声明的body大小 + */ + ssize_t responseBodyTotalSize() const; + + /** + * 获取已经下载body的大小 + */ + size_t responseBodySize() const; + + /** + * 获取请求url + */ + const std::string &getUrl() const; + + /** + * 判断是否正在等待响应 + */ + bool waitResponse() const; + + /** + * 判断是否为https + */ + bool isHttps() const; + + /** + * 设置从发起连接到接收header完毕的延时,默认10秒 + * 此参数必须大于0 + */ + void setHeaderTimeout(size_t timeout_ms); + + /** + * 设置接收body数据超时时间, 默认5秒 + * 此参数可以用于处理超大body回复的超时问题 + * 此参数可以等于0 + */ + void setBodyTimeout(size_t timeout_ms); + + /** + * 设置整个链接超时超时时间, 默认0 + * 该值设置不为0后,HeaderTimeout和BodyTimeout无效 + */ + void setCompleteTimeout(size_t timeout_ms); + + /** + * 设置http代理url + */ + void setProxyUrl(std::string proxy_url); + + /** + * 当重用连接失败时, 是否允许重新发起请求 + * If the reuse connection fails, whether to allow the request to be resent + * @param allow true:允许重新发起请求 / true: allow the request to be resent + */ + void setAllowResendRequest(bool allow); + +protected: + /** + * 收到http回复头 + * @param status 状态码,譬如:200 OK + * @param headers http头 + */ + virtual void onResponseHeader(const std::string &status, const HttpHeader &headers) = 0; + + /** + * 收到http conten数据 + * @param buf 数据指针 + * @param size 数据大小 + */ + virtual void onResponseBody(const char *buf, size_t size) = 0; + + /** + * 接收http回复完毕, + */ + virtual void onResponseCompleted(const toolkit::SockException &ex) = 0; + + /** + * 重定向事件 + * @param url 重定向url + * @param temporary 是否为临时重定向 + * @return 是否继续 + */ + virtual bool onRedirectUrl(const std::string &url, bool temporary) { return true; }; + +protected: + //// HttpRequestSplitter override //// + ssize_t onRecvHeader(const char *data, size_t len) override; + void onRecvContent(const char *data, size_t len) override; + + //// TcpClient override //// + void onConnect(const toolkit::SockException &ex) override; + void onRecv(const toolkit::Buffer::Ptr &pBuf) override; + void onError(const toolkit::SockException &ex) override; + void onFlush() override; + void onManager() override; + + void clearResponse(); + + bool checkProxyConnected(const char *data, size_t len); + bool isUsedProxy() const; + bool isProxyConnected() const; + +private: + void onResponseCompleted_l(const toolkit::SockException &ex); + void onConnect_l(const toolkit::SockException &ex); + void checkCookie(HttpHeader &headers); + +private: + //for http response + bool _complete = false; + bool _header_recved = false; + bool _http_persistent = true; + bool _allow_resend_request = false; + size_t _recved_body_size; + ssize_t _total_body_size; + Parser _parser; + std::shared_ptr _chunked_splitter; + + //for request args + bool _is_https; + std::string _url; + HttpHeader _user_set_header; + HttpBody::Ptr _body; + std::string _method; + std::string _last_host; + + //for this request + std::string _path; + HttpHeader _header; + + //for timeout + size_t _wait_header_ms = 10 * 1000; + size_t _wait_body_ms = 10 * 1000; + size_t _wait_complete_ms = 0; + toolkit::Ticker _wait_header; + toolkit::Ticker _wait_body; + toolkit::Ticker _wait_complete; + + bool _used_proxy = false; + bool _proxy_connected = false; + uint16_t _proxy_port; + std::string _proxy_url; + std::string _proxy_host; + std::string _proxy_auth; +}; + +} /* namespace mediakit */ + +#endif /* Http_HttpClient_h */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClientImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClientImp.h new file mode 100644 index 00000000..d1862541 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpClientImp.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_HTTP_HTTPCLIENTIMP_H_ +#define SRC_HTTP_HTTPCLIENTIMP_H_ + +#include "HttpClient.h" +#include "Util/SSLBox.h" + +namespace mediakit { + +class HttpClientImp : public toolkit::TcpClientWithSSL { +public: + using Ptr = std::shared_ptr; + +protected: + void onConnect(const toolkit::SockException &ex) override; + ssize_t onRecvHeader(const char *data, size_t len) override; +}; + +} /* namespace mediakit */ +#endif /* SRC_HTTP_HTTPCLIENTIMP_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpConst.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpConst.h new file mode 100644 index 00000000..a737306e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpConst.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HTTPCONST_H +#define ZLMEDIAKIT_HTTPCONST_H + +#include + +namespace mediakit{ + +class HttpConst { +public: + HttpConst() = delete; + ~HttpConst() = delete; + + /** + * 根据http错误代码获取字符说明 + * @param status 譬如404 + * @return 错误代码字符说明,譬如Not Found + */ + static const char *getHttpStatusMessage(int status); + + /** + * 根据文件后缀返回http mime + * @param name 文件后缀,譬如html + * @return mime值,譬如text/html + */ + static const std::string &getHttpContentType(const char *name); +}; + +}//mediakit + +#endif //ZLMEDIAKIT_HTTPCONST_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookie.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookie.h new file mode 100644 index 00000000..171a7edc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookie.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HTTPCOOKIE_H +#define ZLMEDIAKIT_HTTPCOOKIE_H + +#include +#include +#include +#include +#include +#include + +namespace mediakit { + +/** + * http客户端cookie对象 + */ +class HttpCookie { +public: + using Ptr = std::shared_ptr; + friend class HttpCookieStorage; + + void setPath(const std::string &path); + void setHost(const std::string &host); + void setExpires(const std::string &expires,const std::string &server_date); + void setKeyVal(const std::string &key,const std::string &val); + operator bool (); + + const std::string &getKey() const ; + const std::string &getVal() const ; +private: + std::string _host; + std::string _path = "/"; + std::string _key; + std::string _val; + time_t _expire = 0; +}; + + +/** + * http客户端cookie全局保存器 + */ +class HttpCookieStorage{ +public: + static HttpCookieStorage &Instance(); + void set(const HttpCookie::Ptr &cookie); + std::vector get(const std::string &host,const std::string &path); + +private: + HttpCookieStorage() = default; + +private: + std::unordered_map > > _all_cookie; + std::mutex _mtx_cookie; +}; + + +} /* namespace mediakit */ + +#endif //ZLMEDIAKIT_HTTPCOOKIE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookieManager.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookieManager.h new file mode 100644 index 00000000..0a375e3c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpCookieManager.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_HTTP_COOKIEMANAGER_H +#define SRC_HTTP_COOKIEMANAGER_H + +#include "Common/Parser.h" +#include "Network/Socket.h" +#include "Util/TimeTicker.h" +#include "Util/mini.h" +#include "Util/util.h" +#include +#include + +#define COOKIE_DEFAULT_LIFE (7 * 24 * 60 * 60) + +namespace mediakit { + +class HttpCookieManager; + +/** + * cookie对象,用于保存cookie的一些相关属性 + */ +class HttpServerCookie : public toolkit::noncopyable { +public: + using Ptr = std::shared_ptr; + /** + * 构建cookie + * @param manager cookie管理者对象 + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户唯一id + * @param cookie cookie随机字符串 + * @param max_elapsed 最大过期时间,单位秒 + */ + + HttpServerCookie( + const std::shared_ptr &manager, const std::string &cookie_name, const std::string &uid, + const std::string &cookie, uint64_t max_elapsed); + ~HttpServerCookie(); + + /** + * 获取uid + * @return uid + */ + const std::string &getUid() const; + + /** + * 获取http中Set-Cookie字段的值 + * @param cookie_name 该cookie的名称,譬如 MY_SESSION + * @param path http访问路径 + * @return 例如 MY_SESSION=XXXXXX;expires=Wed, Jun 12 2019 06:30:48 GMT;path=/index/files/ + */ + std::string getCookie(const std::string &path) const; + + /** + * 获取cookie随机字符串 + * @return cookie随机字符串 + */ + const std::string &getCookie() const; + + /** + * 获取该cookie名 + * @return + */ + const std::string &getCookieName() const; + + /** + * 更新该cookie的过期时间,可以让此cookie不失效 + */ + void updateTime(); + + /** + * 判断该cookie是否过期 + * @return + */ + bool isExpired(); + + /** + * 设置附加数据 + */ + void setAttach(toolkit::Any attach); + + /* + * 获取附加数据 + */ + template + T& getAttach() { + return _attach.get(); + } + +private: + std::string cookieExpireTime() const; + +private: + std::string _uid; + std::string _cookie_name; + std::string _cookie_uuid; + uint64_t _max_elapsed; + toolkit::Ticker _ticker; + toolkit::Any _attach; + std::weak_ptr _manager; +}; + +/** + * cookie随机字符串生成器 + */ +class RandStrGenerator { +public: + + /** + * 获取不碰撞的随机字符串 + * @return 随机字符串 + */ + std::string obtain(); + + /** + * 释放随机字符串 + * @param str 随机字符串 + */ + void release(const std::string &str); + +private: + std::string obtain_l(); + +private: + //碰撞库 + std::unordered_set _obtained; + //增长index,防止碰撞用 + int _index = 0; +}; + +/** + * cookie管理器,用于管理cookie的生成以及过期管理,同时实现了同账号异地挤占登录功能 + * 该对象实现了同账号最多登录若干个设备 + */ +class HttpCookieManager : public std::enable_shared_from_this { +public: + friend class HttpServerCookie; + using Ptr = std::shared_ptr; + ~HttpCookieManager(); + + /** + * 获取单例 + */ + static HttpCookieManager &Instance(); + + /** + * 添加cookie + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户id,如果为空则为匿名登录 + * @param max_client 该账号最多登录多少个设备 + * @param max_elapsed 该cookie过期时间,单位秒 + * @return cookie对象 + */ + HttpServerCookie::Ptr addCookie( + const std::string &cookie_name, const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE, + toolkit::Any = toolkit::Any{}, + int max_client = 1); + + /** + * 根据cookie随机字符串查找cookie对象 + * @param cookie_name cookie名,例如MY_SESSION + * @param cookie cookie随机字符串 + * @return cookie对象,可以为nullptr + */ + HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const std::string &cookie); + + /** + * 从http头中获取cookie对象 + * @param cookie_name cookie名,例如MY_SESSION + * @param http_header http头 + * @return cookie对象 + */ + HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const StrCaseMap &http_header); + + /** + * 根据uid获取cookie + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户id + * @return cookie对象 + */ + HttpServerCookie::Ptr getCookieByUid(const std::string &cookie_name, const std::string &uid); + + /** + * 删除cookie,用户登出时使用 + * @param cookie cookie对象,可以为nullptr + * @return + */ + bool delCookie(const HttpServerCookie::Ptr &cookie); + +private: + HttpCookieManager(); + + void onManager(); + /** + * 构造cookie对象时触发,目的是记录某账号下多个cookie + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户id + * @param cookie cookie随机字符串 + */ + void onAddCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); + + /** + * 析构cookie对象时触发 + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户id + * @param cookie cookie随机字符串 + */ + void onDelCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); + + /** + * 获取某用户名下最先登录时的cookie,目的是实现某用户下最多登录若干个设备 + * @param cookie_name cookie名,例如MY_SESSION + * @param uid 用户id + * @param max_client 最多登录的设备个数 + * @return 最早的cookie随机字符串 + */ + std::string getOldestCookie(const std::string &cookie_name, const std::string &uid, int max_client = 1); + + /** + * 删除cookie + * @param cookie_name cookie名,例如MY_SESSION + * @param cookie cookie随机字符串 + * @return 成功true + */ + bool delCookie(const std::string &cookie_name, const std::string &cookie); + +private: + std::unordered_map< + std::string /*cookie_name*/, std::unordered_map> + _map_cookie; + std::unordered_map< + std::string /*cookie_name*/, + std::unordered_map>> + _map_uid_to_cookie; + std::recursive_mutex _mtx_cookie; + toolkit::Timer::Ptr _timer; + RandStrGenerator _generator; +}; + +} // namespace mediakit + +#endif // SRC_HTTP_COOKIEMANAGER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpDownloader.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpDownloader.h new file mode 100644 index 00000000..6c618aa6 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpDownloader.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_HTTP_HTTPDOWNLOADER_H_ +#define SRC_HTTP_HTTPDOWNLOADER_H_ + +#include "HttpClientImp.h" + +namespace mediakit { + +class HttpDownloader : public HttpClientImp { +public: + using Ptr = std::shared_ptr; + using onDownloadResult = std::function; + + ~HttpDownloader() override; + + /** + * 开始下载文件,默认断点续传方式下载 + * @param url 下载http url + * @param file_path 文件保存地址,置空则选择默认文件路径 + * @param append 如果文件已经存在,是否断点续传方式下载 + */ + void startDownload(const std::string &url, const std::string &file_path = "", bool append = false); + + void startDownload(const std::string &url, const onDownloadResult &cb) { + setOnResult(cb); + startDownload(url, "", false); + } + + void setOnResult(const onDownloadResult &cb) { _on_result = cb; } + +protected: + void onResponseBody(const char *buf, size_t size) override; + void onResponseHeader(const std::string &status, const HttpHeader &headers) override; + void onResponseCompleted(const toolkit::SockException &ex) override; + +private: + void closeFile(); + +private: + FILE *_save_file = nullptr; + std::string _file_path; + onDownloadResult _on_result; +}; + +} /* namespace mediakit */ + +#endif /* SRC_HTTP_HTTPDOWNLOADER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpFileManager.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpFileManager.h new file mode 100644 index 00000000..737da943 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpFileManager.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HTTPFILEMANAGER_H +#define ZLMEDIAKIT_HTTPFILEMANAGER_H + +#include "HttpBody.h" +#include "HttpCookie.h" +#include "Common/Parser.h" +#include "Network/Session.h" +#include "Util/function_traits.h" + +namespace mediakit { + +class HttpResponseInvokerImp{ +public: + typedef std::function HttpResponseInvokerLambda0; + typedef std::function HttpResponseInvokerLambda1; + + template + HttpResponseInvokerImp(const C &c):HttpResponseInvokerImp(typename toolkit::function_traits::stl_function_type(c)) {} + HttpResponseInvokerImp(const HttpResponseInvokerLambda0 &lambda); + HttpResponseInvokerImp(const HttpResponseInvokerLambda1 &lambda); + + void operator()(int code, const StrCaseMap &headerOut, const toolkit::Buffer::Ptr &body) const; + void operator()(int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const; + void operator()(int code, const StrCaseMap &headerOut, const std::string &body) const; + + void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const std::string &file, bool use_mmap = true, bool is_path = true) const; + operator bool(); +private: + HttpResponseInvokerLambda0 _lambad; +}; + +/** + * 该对象用于控制http静态文件夹服务器的访问权限 + */ +class HttpFileManager { +public: + typedef std::function invoker; + + /** + * 访问文件或文件夹 + * @param sender 事件触发者 + * @param parser http请求 + * @param cb 回调对象 + */ + static void onAccessPath(toolkit::Session &sender, Parser &parser, const invoker &cb); + + /** + * 获取mime值 + * @param name 文件后缀 + * @return mime值 + */ + static const std::string &getContentType(const char *name); + + /** + * 该ip是否再白名单中 + * @param ip 支持ipv4和ipv6 + */ + static bool isIPAllowed(const std::string &ip); + +private: + HttpFileManager() = delete; + ~HttpFileManager() = delete; +}; + +} + + +#endif //ZLMEDIAKIT_HTTPFILEMANAGER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequestSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequestSplitter.h new file mode 100644 index 00000000..c4345c9f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequestSplitter.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HTTPREQUESTSPLITTER_H +#define ZLMEDIAKIT_HTTPREQUESTSPLITTER_H + +#include +#include "Network/Buffer.h" + +namespace mediakit { + +class HttpRequestSplitter { +public: + HttpRequestSplitter(); + virtual ~HttpRequestSplitter() = default; + + /** + * 添加数据 + * @param data 需要添加的数据 + * @param len 数据长度 + * @warning 实际内存需保证不小于 len + 1, 内部使用 strstr 进行查找, 为防止查找越界, 会在 @p len + 1 的位置设置 '\0' 结束符. + */ + virtual void input(const char *data, size_t len); + + /** + * 恢复初始设置 + */ + void reset(); + + /** + * 剩余数据大小 + */ + size_t remainDataSize(); + + /** + * 获取剩余数据指针 + */ + const char *remainData() const; + + /** + * 设置最大缓存大小 + */ + void setMaxCacheSize(size_t max_cache_size); + +protected: + /** + * 收到请求头 + * @param data 请求头数据 + * @param len 请求头长度 + * + * @return 请求头后的content长度, + * <0 : 代表后面所有数据都是content,此时后面的content将分段通过onRecvContent函数回调出去 + * 0 : 代表为后面数据还是请求头, + * >0 : 代表后面数据为固定长度content,此时将缓存content并等到所有content接收完毕一次性通过onRecvContent函数回调出去 + */ + virtual ssize_t onRecvHeader(const char *data,size_t len) = 0; + + /** + * 收到content分片或全部数据 + * onRecvHeader函数返回>0,则为全部数据 + * @param data content分片或全部数据 + * @param len 数据长度 + */ + virtual void onRecvContent(const char *data,size_t len) {}; + + /** + * 判断数据中是否有包尾 + * @param data 数据指针 + * @param len 数据长度 + * @return nullptr代表未找到包位,否则返回包尾指针 + */ + virtual const char *onSearchPacketTail(const char *data, size_t len); + + /** + * 设置content len + */ + void setContentLen(ssize_t content_len); + +private: + ssize_t _content_len = 0; + size_t _max_cache_size = 0; + size_t _remain_data_size = 0; + toolkit::BufferLikeString _remain_data; +}; + +} /* namespace mediakit */ + +#endif //ZLMEDIAKIT_HTTPREQUESTSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequester.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequester.h new file mode 100644 index 00000000..f5a5b45b --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpRequester.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Htt_HttpRequester_h +#define Htt_HttpRequester_h + +#include "HttpClientImp.h" + +namespace mediakit { + +class HttpRequester : public HttpClientImp { +public: + using Ptr = std::shared_ptr; + using HttpRequesterResult = std::function; + + void setOnResult(const HttpRequesterResult &onResult); + void startRequester(const std::string &url, const HttpRequesterResult &on_result, float timeout_sec = 10); + void setRetry(size_t count, size_t delay); + size_t getRetry() const { return _retry; } + size_t getRetryDelay() const { return _retry_delay; } + size_t getMaxRetry() const { return _max_retry; } + void clear() override; + +private: + void onResponseHeader(const std::string &status, const HttpHeader &headers) override; + void onResponseBody(const char *buf, size_t size) override; + void onResponseCompleted(const toolkit::SockException &ex) override; + +private: + size_t _retry = 0; + size_t _max_retry = 0; + size_t _retry_delay = 2000; // ms + std::string _res_body; + HttpRequesterResult _on_result; +}; + +}//namespace mediakit + +#endif /* Htt_HttpRequester_h */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpSession.h new file mode 100644 index 00000000..0ffbf137 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpSession.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_HTTP_HTTPSESSION_H_ +#define SRC_HTTP_HTTPSESSION_H_ + +#include +#include "Network/Session.h" +#include "Rtmp/FlvMuxer.h" +#include "HttpRequestSplitter.h" +#include "WebSocketSplitter.h" +#include "HttpCookieManager.h" +#include "HttpFileManager.h" +#include "TS/TSMediaSource.h" +#include "FMP4/FMP4MediaSource.h" + +namespace mediakit { + +class HttpSession: public toolkit::Session, + public FlvMuxer, + public HttpRequestSplitter, + public WebSocketSplitter { +public: + using Ptr = std::shared_ptr; + using KeyValue = StrCaseMap; + using HttpResponseInvoker = HttpResponseInvokerImp ; + friend class AsyncSender; + /** + * @param errMsg 如果为空,则代表鉴权通过,否则为错误提示 + * @param accessPath 运行或禁止访问的根目录 + * @param cookieLifeSecond 鉴权cookie有效期 + **/ + using HttpAccessPathInvoker = std::function; + + HttpSession(const toolkit::Socket::Ptr &pSock); + + void onRecv(const toolkit::Buffer::Ptr &) override; + void onError(const toolkit::SockException &err) override; + void onManager() override; + static std::string urlDecodePath(const std::string &str); + static std::string urlDecodeComponent(const std::string &str); + void setTimeoutSec(size_t second); + void setMaxReqSize(size_t max_req_size); + +protected: + //FlvMuxer override + void onWrite(const toolkit::Buffer::Ptr &data, bool flush) override ; + void onDetach() override; + std::shared_ptr getSharedPtr() override; + + //HttpRequestSplitter override + ssize_t onRecvHeader(const char *data,size_t len) override; + void onRecvContent(const char *data,size_t len) override; + + /** + * 重载之用于处理不定长度的content + * 这个函数可用于处理大文件上传、http-flv推流 + * @param header http请求头 + * @param data content分片数据 + * @param len content分片数据大小 + * @param totalSize content总大小,如果为0则是不限长度content + * @param recvedSize 已收数据大小 + */ + virtual void onRecvUnlimitedContent(const Parser &header, + const char *data, + size_t len, + size_t totalSize, + size_t recvedSize){ + shutdown(toolkit::SockException(toolkit::Err_shutdown,"http post content is too huge,default closed")); + } + + /** + * websocket客户端连接上事件 + * @param header http头 + * @return true代表允许websocket连接,否则拒绝 + */ + virtual bool onWebSocketConnect(const Parser &header){ + WarnP(this) << "http server do not support websocket default"; + return false; + } + + //WebSocketSplitter override + /** + * 发送数据进行websocket协议打包后回调 + * @param buffer websocket协议数据 + */ + void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override; + + /** + * 接收到完整的一个webSocket数据包后回调 + * @param header 数据包包头 + */ + void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override; + + //重载获取客户端ip + std::string get_peer_ip() override; + +private: + void onHttpRequest_GET(); + void onHttpRequest_POST(); + void onHttpRequest_HEAD(); + void onHttpRequest_OPTIONS(); + + bool checkLiveStream(const std::string &schema, const std::string &url_suffix, const std::function &cb); + + bool checkLiveStreamFlv(const std::function &cb = nullptr); + bool checkLiveStreamTS(const std::function &cb = nullptr); + bool checkLiveStreamFMP4(const std::function &fmp4_list = nullptr); + + bool checkWebSocket(); + bool emitHttpEvent(bool doInvoke); + void urlDecode(Parser &parser); + void sendNotFound(bool bClose); + void sendResponse(int code, bool bClose, const char *pcContentType = nullptr, + const HttpSession::KeyValue &header = HttpSession::KeyValue(), + const HttpBody::Ptr &body = nullptr, bool no_content_length = false); + + //设置socket标志 + void setSocketFlags(); + +protected: + MediaInfo _media_info; + +private: + bool _is_live_stream = false; + bool _live_over_websocket = false; + //超时时间 + size_t _keep_alive_sec = 0; + //最大http请求字节大小 + size_t _max_req_size = 0; + //消耗的总流量 + uint64_t _total_bytes_usage = 0; + // http请求中的 Origin字段 + std::string _origin; + Parser _parser; + toolkit::Ticker _ticker; + TSMediaSource::RingType::RingReader::Ptr _ts_reader; + FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader; + //处理content数据的callback + std::function _on_recv_body; +}; + +using HttpsSession = toolkit::SessionWithSSL; + +} /* namespace mediakit */ + +#endif /* SRC_HTTP_HTTPSESSION_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpTSPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpTSPlayer.h new file mode 100644 index 00000000..219cdcc8 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/HttpTSPlayer.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HTTPTSPLAYER_H +#define HTTP_HTTPTSPLAYER_H + +#include "Http/HttpDownloader.h" +#include "Player/MediaPlayer.h" +#include "Rtp/TSDecoder.h" + +namespace mediakit { + +//http-ts播发器,未实现ts解复用 +class HttpTSPlayer : public HttpClientImp { +public: + using Ptr = std::shared_ptr; + using onComplete = std::function; + + HttpTSPlayer(const toolkit::EventPoller::Ptr &poller = nullptr); + + /** + * 设置下载完毕或异常断开回调 + */ + void setOnComplete(onComplete cb); + + /** + * 设置接收ts包回调 + */ + void setOnPacket(TSSegment::onSegment cb); + +protected: + ///HttpClient override/// + void onResponseHeader(const std::string &status, const HttpHeader &header) override; + void onResponseBody(const char *buf, size_t size) override; + void onResponseCompleted(const toolkit::SockException &ex) override; + +private: + void emitOnComplete(const toolkit::SockException &ex); + +private: + onComplete _on_complete; + TSSegment::onSegment _on_segment; +}; + +}//namespace mediakit +#endif //HTTP_HTTPTSPLAYER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayer.h new file mode 100644 index 00000000..5ecd73da --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * Created by alex on 2021/4/6. + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_TSPLAYER_H +#define HTTP_TSPLAYER_H + +#include "HttpTSPlayer.h" +#include "Player/PlayerBase.h" + +namespace mediakit { + +class TsPlayer : public HttpTSPlayer, public PlayerBase { +public: + TsPlayer(const toolkit::EventPoller::Ptr &poller); + + /** + * 开始播放 + */ + void play(const std::string &url) override; + + /** + * 停止播放 + */ + void teardown() override; + +protected: + void onResponseBody(const char *buf, size_t size) override; + void onResponseCompleted(const toolkit::SockException &ex) override; + +private: + bool _play_result = true; + bool _benchmark_mode = false; +}; + +} // namespace mediakit +#endif // HTTP_TSPLAYER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayerImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayerImp.h new file mode 100644 index 00000000..821fffb3 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/TsPlayerImp.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * Created by alex on 2021/4/6. + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_TSPLAYERIMP_H +#define HTTP_TSPLAYERIMP_H + +#include +#include "TsPlayer.h" + +namespace mediakit { + +class TsPlayerImp : public PlayerImp, private TrackListener { +public: + using Ptr = std::shared_ptr; + + TsPlayerImp(const toolkit::EventPoller::Ptr &poller = nullptr); + +private: + //// TsPlayer override//// + void onResponseBody(const char *buf, size_t size) override; + +private: + //// PlayerBase override//// + void onPlayResult(const toolkit::SockException &ex) override; + std::vector getTracks(bool ready = true) const override; + void onShutdown(const toolkit::SockException &ex) override; + +private: + //// TrackListener override//// + bool addTrack(const Track::Ptr &track) override { return true; }; + void addTrackCompleted() override; + +private: + DecoderImp::Ptr _decoder; + MediaSinkInterface::Ptr _demuxer; +}; + +}//namespace mediakit +#endif //HTTP_TSPLAYERIMP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketClient.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketClient.h new file mode 100644 index 00000000..9578a9d4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketClient.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_WebSocketClient_H +#define ZLMEDIAKIT_WebSocketClient_H + +#include "Util/util.h" +#include "Util/base64.h" +#include "Util/SHA1.h" +#include "Network/TcpClient.h" +#include "HttpClientImp.h" +#include "WebSocketSplitter.h" + +namespace mediakit { + +template +class HttpWsClient; + +/** + * 辅助类,用于拦截TcpClient数据发送前的拦截 + * @tparam ClientType TcpClient派生类 + * @tparam DataType 这里无用,为了声明友元用 + */ +template +class ClientTypeImp : public ClientType { +public: + friend class HttpWsClient; + + using onBeforeSendCB = std::function; + + template + ClientTypeImp(ArgsType &&...args) : ClientType(std::forward(args)...) {} + + /** + * 发送前拦截并打包为websocket协议 + */ + ssize_t send(toolkit::Buffer::Ptr buf) override { + if (_beforeSendCB) { + return _beforeSendCB(buf); + } + return ClientType::send(std::move(buf)); + } + +protected: + /** + * 设置发送数据截取回调函数 + * @param cb 截取回调函数 + */ + void setOnBeforeSendCB(const onBeforeSendCB &cb) { _beforeSendCB = cb; } + +private: + onBeforeSendCB _beforeSendCB; +}; + +/** + * 此对象完成了weksocket 客户端握手协议,以及到TcpClient派生类事件的桥接 + * @tparam ClientType TcpClient派生类 + * @tparam DataType websocket负载类型,是TEXT还是BINARY类型 + */ +template +class HttpWsClient : public HttpClientImp, public WebSocketSplitter { +public: + using Ptr = std::shared_ptr; + + HttpWsClient(const std::shared_ptr> &delegate) : _weak_delegate(delegate) { + _Sec_WebSocket_Key = encodeBase64(toolkit::makeRandStr(16, false)); + setPoller(delegate->getPoller()); + } + + /** + * 发起ws握手 + * @param ws_url ws连接url + * @param fTimeOutSec 超时时间 + */ + void startWsClient(const std::string &ws_url, float fTimeOutSec) { + std::string http_url = ws_url; + toolkit::replace(http_url, "ws://", "http://"); + toolkit::replace(http_url, "wss://", "https://"); + setMethod("GET"); + addHeader("Upgrade", "websocket"); + addHeader("Connection", "Upgrade"); + addHeader("Sec-WebSocket-Version", "13"); + addHeader("Sec-WebSocket-Key", _Sec_WebSocket_Key); + _onRecv = nullptr; + setHeaderTimeout(fTimeOutSec * 1000); + sendRequest(http_url); + } + + void closeWsClient() { + if (!_onRecv) { + // 未连接 + return; + } + WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = CLOSE; + // 客户端需要加密 + header._mask_flag = true; + WebSocketSplitter::encode(header, nullptr); + } + +protected: + // HttpClientImp override + + /** + * 收到http回复头 + * @param status 状态码,譬如:200 OK + * @param headers http头 + */ + void onResponseHeader(const std::string &status, const HttpHeader &headers) override { + if (status == "101") { + auto Sec_WebSocket_Accept = encodeBase64(toolkit::SHA1::encode_bin(_Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); + if (Sec_WebSocket_Accept == const_cast(headers)["Sec-WebSocket-Accept"]) { + // success + onWebSocketException(toolkit::SockException()); + // 防止ws服务器返回Content-Length + const_cast(headers).erase("Content-Length"); + return; + } + shutdown(toolkit::SockException(toolkit::Err_shutdown, StrPrinter << "Sec-WebSocket-Accept mismatch")); + return; + } + + shutdown(toolkit::SockException(toolkit::Err_shutdown, StrPrinter << "bad http status code:" << status)); + }; + + /** + * 接收http回复完毕, + */ + void onResponseCompleted(const toolkit::SockException &ex) override {} + + /** + * 接收websocket负载数据 + */ + void onResponseBody(const char *buf, size_t size) override { + if (_onRecv) { + // 完成websocket握手后,拦截websocket数据并解析 + _onRecv(buf, size); + } + }; + + // TcpClient override + + void onRecv(const toolkit::Buffer::Ptr &buf) override { + HttpClientImp::onRecv(buf); + } + + /** + * 定时触发 + */ + void onManager() override { + if (_onRecv) { + // websocket连接成功了 + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onManager(); + } + } else { + // websocket连接中... + HttpClientImp::onManager(); + } + + if (!_onRecv) { + // websocket尚未链接 + return; + } + + if (_recv_ticker.elapsedTime() > 30 * 1000) { + shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout")); + } else if (_recv_ticker.elapsedTime() > 10 * 1000) { + // 没收到回复,每10秒发送次ping 包 + WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = PING; + header._mask_flag = true; + WebSocketSplitter::encode(header, nullptr); + } + } + + /** + * 数据全部发送完毕后回调 + */ + void onFlush() override { + if (_onRecv) { + // websocket连接成功了 + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onFlush(); + } + } else { + // websocket连接中... + HttpClientImp::onFlush(); + } + } + + /** + * tcp连接结果 + */ + void onConnect(const toolkit::SockException &ex) override { + if (ex) { + // tcp连接失败,直接返回失败 + onWebSocketException(ex); + return; + } + // 开始websocket握手 + HttpClientImp::onConnect(ex); + } + + /** + * tcp连接断开 + */ + void onError(const toolkit::SockException &ex) override { + // tcp断开或者shutdown导致的断开 + onWebSocketException(ex); + } + + // WebSocketSplitter override + + /** + * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 + * @param header 数据包头 + */ + void onWebSocketDecodeHeader(const WebSocketHeader &header) override { _payload_section.clear(); } + + /** + * 收到webSocket数据包负载 + * @param header 数据包包头 + * @param ptr 负载数据指针 + * @param len 负载数据长度 + * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 + */ + void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) override { + _payload_section.append((char *)ptr, len); + } + + /** + * 接收到完整的一个webSocket数据包后回调 + * @param header 数据包包头 + */ + void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override { + WebSocketHeader &header = const_cast(header_in); + auto flag = header._mask_flag; + // websocket客户端发送数据需要加密 + header._mask_flag = true; + _recv_ticker.resetTime(); + switch (header._opcode) { + case WebSocketHeader::CLOSE: { + // 服务器主动关闭 + WebSocketSplitter::encode(header, nullptr); + shutdown(toolkit::SockException(toolkit::Err_eof, "websocket server close the connection")); + break; + } + + case WebSocketHeader::PING: { + // 心跳包 + header._opcode = WebSocketHeader::PONG; + WebSocketSplitter::encode(header, std::make_shared(std::move(_payload_section))); + break; + } + + case WebSocketHeader::CONTINUATION: + case WebSocketHeader::TEXT: + case WebSocketHeader::BINARY: { + if (!header._fin) { + // 还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + _payload_cache.append(std::move(_payload_section)); + if (_payload_cache.size() < MAX_WS_PACKET) { + // 还有内存容量缓存分片数据 + break; + } + // 分片缓存太大,需要清空 + } + + // 最后一个包 + if (_payload_cache.empty()) { + // 这个包是唯一个分片 + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); + } + break; + } + + // 这个包由多个分片组成 + _payload_cache.append(std::move(_payload_section)); + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); + } + _payload_cache.clear(); + break; + } + + default: break; + } + _payload_section.clear(); + header._mask_flag = flag; + } + + /** + * websocket数据编码回调 + * @param ptr 数据指针 + * @param len 数据指针长度 + */ + void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override { HttpClientImp::send(std::move(buffer)); } + +private: + void onWebSocketException(const toolkit::SockException &ex) { + if (!ex) { + // websocket握手成功 + // 此处截取TcpClient派生类发送的数据并进行websocket协议打包 + std::weak_ptr weakSelf = std::static_pointer_cast(shared_from_this()); + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->setOnBeforeSendCB([weakSelf](const toolkit::Buffer::Ptr &buf) { + auto strong_self = weakSelf.lock(); + if (strong_self) { + WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = DataType; + // 客户端需要加密 + header._mask_flag = true; + strong_self->WebSocketSplitter::encode(header, buf); + } + return buf->size(); + }); + // 设置sock,否则shutdown等接口都无效 + strong_ref->setSock(HttpClientImp::getSock()); + // 触发连接成功事件 + strong_ref->onConnect(ex); + } + + // 拦截websocket数据接收 + _onRecv = [this](const char *data, size_t len) { + // 解析websocket数据包 + this->WebSocketSplitter::decode((uint8_t *)data, len); + }; + return; + } + + // websocket握手失败或者tcp连接失败或者中途断开 + if (_onRecv) { + // 握手成功之后的中途断开 + _onRecv = nullptr; + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onError(ex); + } + return; + } + + // websocket握手失败或者tcp连接失败 + if (auto strong_ref = _weak_delegate.lock()) { + strong_ref->onConnect(ex); + } + } + +private: + std::string _Sec_WebSocket_Key; + std::function _onRecv; + std::weak_ptr> _weak_delegate; + std::string _payload_section; + std::string _payload_cache; + toolkit::Ticker _recv_ticker; +}; + +/** + * Tcp客户端转WebSocket客户端模板, + * 通过该模板,开发者再不修改TcpClient派生类任何代码的情况下快速实现WebSocket协议的包装 + * @tparam ClientType TcpClient派生类 + * @tparam DataType websocket负载类型,是TEXT还是BINARY类型 + * @tparam useWSS 是否使用ws还是wss连接 + */ +template +class WebSocketClient : public ClientTypeImp { +public: + using Ptr = std::shared_ptr; + + template + WebSocketClient(ArgsType &&...args) : ClientTypeImp(std::forward(args)...) {} + ~WebSocketClient() override { _wsClient->closeWsClient(); } + + /** + * 重载startConnect方法, + * 目的是替换TcpClient的连接服务器行为,使之先完成WebSocket握手 + * @param host websocket服务器ip或域名 + * @param iPort websocket服务器端口 + * @param timeout_sec 超时时间 + * @param local_port 本地监听端口,此处不起作用 + */ + void startConnect(const std::string &host, uint16_t port, float timeout_sec = 3, uint16_t local_port = 0) override { + std::string ws_url; + if (useWSS) { + // 加密的ws + ws_url = StrPrinter << "wss://" + host << ":" << port << "/"; + } else { + // 明文ws + ws_url = StrPrinter << "ws://" + host << ":" << port << "/"; + } + startWebSocket(ws_url, timeout_sec); + } + + void startWebSocket(const std::string &ws_url, float fTimeOutSec = 3) { + _wsClient = std::make_shared>(std::static_pointer_cast(this->shared_from_this())); + _wsClient->setOnCreateSocket([this](const toolkit::EventPoller::Ptr &) { return this->createSocket(); }); + _wsClient->startWsClient(ws_url, fTimeOutSec); + } + + HttpClient &getHttpClient() { return *_wsClient; } + +private: + typename HttpWsClient::Ptr _wsClient; +}; + +} // namespace mediakit +#endif // ZLMEDIAKIT_WebSocketClient_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSession.h new file mode 100644 index 00000000..93bb820e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSession.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_WEBSOCKETSESSION_H +#define ZLMEDIAKIT_WEBSOCKETSESSION_H + +#include "HttpSession.h" +#include "Network/TcpServer.h" + +/** + * 数据发送拦截器 + */ +class SendInterceptor{ +public: + using onBeforeSendCB =std::function; + + virtual ~SendInterceptor() = default; + virtual void setOnBeforeSendCB(const onBeforeSendCB &cb) = 0; +}; + +/** + * 该类实现了Session派生类发送数据的截取 + * 目的是发送业务数据前进行websocket协议的打包 + */ +template +class SessionTypeImp : public SessionType, public SendInterceptor{ +public: + using Ptr = std::shared_ptr; + + SessionTypeImp(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock) : + SessionType(pSock) {} + + /** + * 设置发送数据截取回调函数 + * @param cb 截取回调函数 + */ + void setOnBeforeSendCB(const onBeforeSendCB &cb) override { + _beforeSendCB = cb; + } + +protected: + /** + * 重载send函数截取数据 + * @param buf 需要截取的数据 + * @return 数据字节数 + */ + ssize_t send(toolkit::Buffer::Ptr buf) override { + if (_beforeSendCB) { + return _beforeSendCB(buf); + } + return SessionType::send(std::move(buf)); + } + +private: + onBeforeSendCB _beforeSendCB; +}; + +template +class SessionCreator { +public: + //返回的Session必须派生于SendInterceptor,可以返回null + toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &data_type){ + return std::make_shared >(header,parent,pSock); + } +}; + +/** +* 通过该模板类可以透明化WebSocket协议, +* 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等 +*/ +template +class WebSocketSessionBase : public HttpSessionType { +public: + WebSocketSessionBase(const toolkit::Socket::Ptr &pSock) : HttpSessionType(pSock){} + + //收到eof或其他导致脱离TcpServer事件的回调 + void onError(const toolkit::SockException &err) override{ + HttpSessionType::onError(err); + if(_session){ + _session->onError(err); + } + } + //每隔一段时间触发,用来做超时管理 + void onManager() override{ + if (_session) { + _session->onManager(); + } else { + HttpSessionType::onManager(); + } + if (!_session) { + // websocket尚未链接 + return; + } + if (_recv_ticker.elapsedTime() > 30 * 1000) { + HttpSessionType::shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout")); + } else if (_recv_ticker.elapsedTime() > 10 * 1000) { + // 没收到回复,每10秒发送次ping 包 + mediakit::WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = mediakit::WebSocketHeader::PING; + header._mask_flag = false; + HttpSessionType::encode(header, nullptr); + } + } + + void attachServer(const toolkit::Server &server) override{ + HttpSessionType::attachServer(server); + _weak_server = const_cast(server).shared_from_this(); + } + +protected: + /** + * websocket客户端连接上事件 + * @param header http头 + * @return true代表允许websocket连接,否则拒绝 + */ + bool onWebSocketConnect(const mediakit::Parser &header) override{ + //创建websocket session类 + auto data_type = DataType; + _session = _creator(header, *this, HttpSessionType::getSock(), data_type); + if (!_session) { + // 此url不允许创建websocket连接 + return false; + } + auto strongServer = _weak_server.lock(); + if (strongServer) { + _session->attachServer(*strongServer); + } + + //此处截取数据并进行websocket协议打包 + std::weak_ptr weakSelf = std::static_pointer_cast(HttpSessionType::shared_from_this()); + std::dynamic_pointer_cast(_session)->setOnBeforeSendCB([weakSelf, data_type](const toolkit::Buffer::Ptr &buf) { + auto strongSelf = weakSelf.lock(); + if (strongSelf) { + mediakit::WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = data_type; + header._mask_flag = false; + strongSelf->HttpSessionType::encode(header, buf); + } + return buf->size(); + }); + + //允许websocket客户端 + return true; + } + + /** + * 开始收到一个webSocket数据包 + */ + void onWebSocketDecodeHeader(const mediakit::WebSocketHeader &packet) override{ + //新包,原来的包残余数据清空掉 + _payload_section.clear(); + } + + /** + * 收到websocket数据包负载 + */ + void onWebSocketDecodePayload(const mediakit::WebSocketHeader &packet,const uint8_t *ptr,size_t len,size_t recved) override { + _payload_section.append((char *)ptr,len); + } + + /** + * 接收到完整的一个webSocket数据包后回调 + * @param header 数据包包头 + */ + void onWebSocketDecodeComplete(const mediakit::WebSocketHeader &header_in) override { + auto header = const_cast(header_in); + auto flag = header._mask_flag; + header._mask_flag = false; + _recv_ticker.resetTime(); + switch (header._opcode){ + case mediakit::WebSocketHeader::CLOSE:{ + HttpSessionType::encode(header,nullptr); + HttpSessionType::shutdown(toolkit::SockException(toolkit::Err_shutdown, "recv close request from client")); + break; + } + + case mediakit::WebSocketHeader::PING:{ + header._opcode = mediakit::WebSocketHeader::PONG; + HttpSessionType::encode(header,std::make_shared(_payload_section)); + break; + } + + case mediakit::WebSocketHeader::CONTINUATION: + case mediakit::WebSocketHeader::TEXT: + case mediakit::WebSocketHeader::BINARY:{ + if (!header._fin) { + //还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出 + _payload_cache.append(std::move(_payload_section)); + if (_payload_cache.size() < MAX_WS_PACKET) { + //还有内存容量缓存分片数据 + break; + } + //分片缓存太大,需要清空 + } + + //最后一个包 + if (_payload_cache.empty()) { + //这个包是唯一个分片 + _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_section))); + break; + } + + //这个包由多个分片组成 + _payload_cache.append(std::move(_payload_section)); + _session->onRecv(std::make_shared(header._opcode, header._fin, std::move(_payload_cache))); + _payload_cache.clear(); + break; + } + + default: break; + } + _payload_section.clear(); + header._mask_flag = flag; + } + + /** + * 发送数据进行websocket协议打包后回调 + */ + void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override{ + HttpSessionType::send(std::move(buffer)); + } + +private: + std::string _payload_cache; + std::string _payload_section; + std::weak_ptr _weak_server; + toolkit::Session::Ptr _session; + Creator _creator; + toolkit::Ticker _recv_ticker; +}; + + +template +class WebSocketSession : public WebSocketSessionBase,HttpSessionType,DataType>{ +public: + WebSocketSession(const toolkit::Socket::Ptr &pSock) : WebSocketSessionBase,HttpSessionType,DataType>(pSock){} +}; + +#endif //ZLMEDIAKIT_WEBSOCKETSESSION_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSplitter.h new file mode 100644 index 00000000..96ea2c0c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Http/WebSocketSplitter.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_WEBSOCKETSPLITTER_H +#define ZLMEDIAKIT_WEBSOCKETSPLITTER_H + +#include +#include +#include +#include +#include "Network/Buffer.h" + +//websocket组合包最大不得超过4MB(防止内存爆炸) +#define MAX_WS_PACKET (4 * 1024 * 1024) + +namespace mediakit { + +class WebSocketHeader { +public: + using Ptr = std::shared_ptr; + typedef enum { + CONTINUATION = 0x0, + TEXT = 0x1, + BINARY = 0x2, + RSV3 = 0x3, + RSV4 = 0x4, + RSV5 = 0x5, + RSV6 = 0x6, + RSV7 = 0x7, + CLOSE = 0x8, + PING = 0x9, + PONG = 0xA, + CONTROL_RSVB = 0xB, + CONTROL_RSVC = 0xC, + CONTROL_RSVD = 0xD, + CONTROL_RSVE = 0xE, + CONTROL_RSVF = 0xF + } Type; +public: + + WebSocketHeader() : _mask(4){ + //获取_mask内部buffer的内存地址,该内存是malloc开辟的,地址为随机 + uint64_t ptr = (uint64_t)(&_mask[0]); + //根据内存地址设置掩码随机数 + _mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4); + } + + virtual ~WebSocketHeader() = default; + +public: + bool _fin; + uint8_t _reserved; + Type _opcode; + bool _mask_flag; + size_t _payload_len; + std::vector _mask; +}; + +//websocket协议收到的字符串类型缓存,用户协议层获取该数据传输的方式 +class WebSocketBuffer : public toolkit::BufferString { +public: + using Ptr = std::shared_ptr; + + template + WebSocketBuffer(WebSocketHeader::Type headType, bool fin, ARGS &&...args) + : toolkit::BufferString(std::forward(args)...), _fin(fin), _head_type(headType){} + + WebSocketHeader::Type headType() const { return _head_type; } + + bool isFinished() const { return _fin; }; + +private: + bool _fin; + WebSocketHeader::Type _head_type; +}; + +class WebSocketSplitter : public WebSocketHeader{ +public: + /** + * 输入数据以便解包webSocket数据以及处理粘包问题 + * 可能触发onWebSocketDecodeHeader和onWebSocketDecodePayload回调 + * @param data 需要解包的数据,可能是不完整的包或多个包 + * @param len 数据长度 + */ + void decode(uint8_t *data, size_t len); + + /** + * 编码一个数据包 + * 将触发2次onWebSocketEncodeData回调 + * @param header 数据头 + * @param buffer 负载数据 + */ + void encode(const WebSocketHeader &header,const toolkit::Buffer::Ptr &buffer); + +protected: + /** + * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 + * @param header 数据包头 + */ + virtual void onWebSocketDecodeHeader(const WebSocketHeader &header) {}; + + /** + * 收到webSocket数据包负载 + * @param header 数据包包头 + * @param ptr 负载数据指针 + * @param len 负载数据长度 + * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 + */ + virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) {}; + + /** + * 接收到完整的一个webSocket数据包后回调 + * @param header 数据包包头 + */ + virtual void onWebSocketDecodeComplete(const WebSocketHeader &header) {}; + + /** + * websocket数据编码回调 + * @param ptr 数据指针 + * @param len 数据指针长度 + */ + virtual void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer){}; + +private: + void onPayloadData(uint8_t *data, size_t len); + +private: + bool _got_header = false; + int _mask_offset = 0; + size_t _payload_offset = 0; + std::string _remain_data; +}; + +} /* namespace mediakit */ + + +#endif //ZLMEDIAKIT_WEBSOCKETSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/Buffer.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/Buffer.h new file mode 100644 index 00000000..67245199 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/Buffer.h @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_BUFFER_H +#define ZLTOOLKIT_BUFFER_H + +#include +#include +#include +#include +#include +#include +#include "Util/util.h" +#include "Util/ResourcePool.h" + +namespace toolkit { + +template struct is_pointer : public std::false_type {}; +template struct is_pointer > : public std::true_type {}; +template struct is_pointer > : public std::true_type {}; +template struct is_pointer : public std::true_type {}; +template struct is_pointer : public std::true_type {}; + +//缓存基类 +class Buffer : public noncopyable { +public: + using Ptr = std::shared_ptr; + + Buffer() = default; + virtual ~Buffer() = default; + + //返回数据长度 + virtual char *data() const = 0; + virtual size_t size() const = 0; + + virtual std::string toString() const { + return std::string(data(), size()); + } + + virtual size_t getCapacity() const { + return size(); + } + +private: + //对象个数统计 + ObjectStatistic _statistic; +}; + +template +class BufferOffset : public Buffer { +public: + using Ptr = std::shared_ptr; + + BufferOffset(C data, size_t offset = 0, size_t len = 0) : _data(std::move(data)) { + setup(offset, len); + } + + ~BufferOffset() override = default; + + char *data() const override { + return const_cast(getPointer(_data)->data()) + _offset; + } + + size_t size() const override { + return _size; + } + + std::string toString() const override { + return std::string(data(), size()); + } + +private: + void setup(size_t offset = 0, size_t size = 0) { + auto max_size = getPointer(_data)->size(); + assert(offset + size <= max_size); + if (!size) { + size = max_size - offset; + } + _size = size; + _offset = offset; + } + + template + static typename std::enable_if<::toolkit::is_pointer::value, const T &>::type + getPointer(const T &data) { + return data; + } + + template + static typename std::enable_if::value, const T *>::type + getPointer(const T &data) { + return &data; + } + +private: + C _data; + size_t _size; + size_t _offset; +}; + +using BufferString = BufferOffset; + +//指针式缓存对象, +class BufferRaw : public Buffer { +public: + using Ptr = std::shared_ptr; + + static Ptr create(); + + ~BufferRaw() override { + if (_data) { + delete[] _data; + } + } + + //在写入数据时请确保内存是否越界 + char *data() const override { + return _data; + } + + //有效数据大小 + size_t size() const override { + return _size; + } + + //分配内存大小 + void setCapacity(size_t capacity) { + if (_data) { + do { + if (capacity > _capacity) { + //请求的内存大于当前内存,那么重新分配 + break; + } + + if (_capacity < 2 * 1024) { + //2K以下,不重复开辟内存,直接复用 + return; + } + + if (2 * capacity > _capacity) { + //如果请求的内存大于当前内存的一半,那么也复用 + return; + } + } while (false); + + delete[] _data; + } + _data = new char[capacity]; + _capacity = capacity; + } + + //设置有效数据大小 + virtual void setSize(size_t size) { + if (size > _capacity) { + throw std::invalid_argument("Buffer::setSize out of range"); + } + _size = size; + } + + //赋值数据 + void assign(const char *data, size_t size = 0) { + if (size <= 0) { + size = strlen(data); + } + setCapacity(size + 1); + memcpy(_data, data, size); + _data[size] = '\0'; + setSize(size); + } + + size_t getCapacity() const override { + return _capacity; + } + +protected: + friend class ResourcePool_l; + + BufferRaw(size_t capacity = 0) { + if (capacity) { + setCapacity(capacity); + } + } + + BufferRaw(const char *data, size_t size = 0) { + assign(data, size); + } + +private: + size_t _size = 0; + size_t _capacity = 0; + char *_data = nullptr; + //对象个数统计 + ObjectStatistic _statistic; +}; + +class BufferLikeString : public Buffer { +public: + ~BufferLikeString() override = default; + + BufferLikeString() { + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + } + + BufferLikeString &operator=(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + return *this; + } + + BufferLikeString(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + } + + BufferLikeString &operator=(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + return *this; + } + + char *data() const override { + return (char *) _str.data() + _erase_head; + } + + size_t size() const override { + return _str.size() - _erase_tail - _erase_head; + } + + BufferLikeString &erase(size_t pos = 0, size_t n = std::string::npos) { + if (pos == 0) { + //移除前面的数据 + if (n != std::string::npos) { + //移除部分 + if (n > size()) { + //移除太多数据了 + throw std::out_of_range("BufferLikeString::erase out_of_range in head"); + } + //设置起始便宜量 + _erase_head += n; + data()[size()] = '\0'; + return *this; + } + //移除全部数据 + _erase_head = 0; + _erase_tail = _str.size(); + data()[0] = '\0'; + return *this; + } + + if (n == std::string::npos || pos + n >= size()) { + //移除末尾所有数据 + if (pos >= size()) { + //移除太多数据 + throw std::out_of_range("BufferLikeString::erase out_of_range in tail"); + } + _erase_tail += size() - pos; + data()[size()] = '\0'; + return *this; + } + + //移除中间的 + if (pos + n > size()) { + //超过长度限制 + throw std::out_of_range("BufferLikeString::erase out_of_range in middle"); + } + _str.erase(_erase_head + pos, n); + return *this; + } + + BufferLikeString &append(const BufferLikeString &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const std::string &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const char *data) { + return append(data, strlen(data)); + } + + BufferLikeString &append(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (_erase_head > _str.capacity() / 2) { + moveData(); + } + if (_erase_tail == 0) { + _str.append(data, len); + return *this; + } + _str.insert(_erase_head + size(), data, len); + return *this; + } + + void push_back(char c) { + if (_erase_tail == 0) { + _str.push_back(c); + return; + } + data()[size()] = c; + --_erase_tail; + data()[size()] = '\0'; + } + + BufferLikeString &insert(size_t pos, const char *s, size_t n) { + _str.insert(_erase_head + pos, s, n); + return *this; + } + + BufferLikeString &assign(const char *data) { + return assign(data, strlen(data)); + } + + BufferLikeString &assign(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (data >= _str.data() && data < _str.data() + _str.size()) { + _erase_head = data - _str.data(); + if (data + len > _str.data() + _str.size()) { + throw std::out_of_range("BufferLikeString::assign out_of_range"); + } + _erase_tail = _str.data() + _str.size() - (data + len); + return *this; + } + _str.assign(data, len); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + void clear() { + _erase_head = 0; + _erase_tail = 0; + _str.clear(); + } + + char &operator[](size_t pos) { + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::operator[] out_of_range"); + } + return data()[pos]; + } + + const char &operator[](size_t pos) const { + return (*const_cast(this))[pos]; + } + + size_t capacity() const { + return _str.capacity(); + } + + void reserve(size_t size) { + _str.reserve(size); + } + + void resize(size_t size, char c = '\0') { + _str.resize(size, c); + _erase_head = 0; + _erase_tail = 0; + } + + bool empty() const { + return size() <= 0; + } + + std::string substr(size_t pos, size_t n = std::string::npos) const { + if (n == std::string::npos) { + //获取末尾所有的 + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, size() - pos); + } + + //获取部分 + if (pos + n > size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, n); + } + +private: + void moveData() { + if (_erase_head) { + _str.erase(0, _erase_head); + _erase_head = 0; + } + } + +private: + size_t _erase_head; + size_t _erase_tail; + std::string _str; + //对象个数统计 + ObjectStatistic _statistic; +}; + +}//namespace toolkit +#endif //ZLTOOLKIT_BUFFER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/BufferSock.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/BufferSock.h new file mode 100644 index 00000000..0ee264d1 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/BufferSock.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_BUFFERSOCK_H +#define ZLTOOLKIT_BUFFERSOCK_H + +#if !defined(_WIN32) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "Util/util.h" +#include "Util/List.h" +#include "Util/ResourcePool.h" +#include "sockutil.h" +#include "Buffer.h" + +namespace toolkit { + +#if !defined(IOV_MAX) +#define IOV_MAX 1024 +#endif + +class BufferSock : public Buffer { +public: + using Ptr = std::shared_ptr; + BufferSock(Buffer::Ptr ptr, struct sockaddr *addr = nullptr, int addr_len = 0); + ~BufferSock() override = default; + + char *data() const override; + size_t size() const override; + const struct sockaddr *sockaddr() const; + socklen_t socklen() const; + +private: + int _addr_len = 0; + struct sockaddr_storage _addr; + Buffer::Ptr _buffer; +}; + +class BufferList : public noncopyable { +public: + using Ptr = std::shared_ptr; + using SendResult = std::function; + + BufferList() = default; + virtual ~BufferList() = default; + + virtual bool empty() = 0; + virtual size_t count() = 0; + virtual ssize_t send(int fd, int flags) = 0; + + static Ptr create(List > list, SendResult cb, bool is_udp); + +private: + //对象个数统计 + ObjectStatistic _statistic; +}; + +} +#endif //ZLTOOLKIT_BUFFERSOCK_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/Server.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/Server.h new file mode 100644 index 00000000..12e49c69 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/Server.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_SERVER_H +#define ZLTOOLKIT_SERVER_H + +#include +#include "Util/mini.h" +#include "Session.h" + +namespace toolkit { + +// 全局的 Session 记录对象, 方便后面管理 +// 线程安全的 +class SessionMap : public std::enable_shared_from_this { +public: + friend class SessionHelper; + using Ptr = std::shared_ptr; + + //单例 + static SessionMap &Instance(); + ~SessionMap() = default; + + //获取Session + Session::Ptr get(const std::string &tag); + void for_each_session(const std::function &cb); + +private: + SessionMap() = default; + + //移除Session + bool del(const std::string &tag); + //添加Session + bool add(const std::string &tag, const Session::Ptr &session); + +private: + std::mutex _mtx_session; + std::unordered_map > _map_session; +}; + +class Server; + +class SessionHelper { +public: + bool enable = true; + + using Ptr = std::shared_ptr; + + SessionHelper(const std::weak_ptr &server, Session::Ptr session, std::string cls); + ~SessionHelper(); + + const Session::Ptr &session() const; + const std::string &className() const; + +private: + std::string _cls; + std::string _identifier; + Session::Ptr _session; + SessionMap::Ptr _session_map; + std::weak_ptr _server; +}; + +// server 基类, 暂时仅用于剥离 SessionHelper 对 TcpServer 的依赖 +// 后续将 TCP 与 UDP 服务通用部分加到这里. +class Server : public std::enable_shared_from_this, public mINI { +public: + using Ptr = std::shared_ptr; + + explicit Server(EventPoller::Ptr poller = nullptr); + virtual ~Server() = default; + +protected: + EventPoller::Ptr _poller; +}; + +} // namespace toolkit + +#endif // ZLTOOLKIT_SERVER_H \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/Session.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/Session.h new file mode 100644 index 00000000..3b8a971c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/Session.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_SESSION_H +#define ZLTOOLKIT_SESSION_H + +#include +#include "Socket.h" +#include "Util/util.h" +#include "Util/SSLBox.h" + +namespace toolkit { + +// 会话, 用于存储一对客户端与服务端间的关系 +class Server; +class TcpSession; +class UdpSession; + +class Session : public SocketHelper { +public: + using Ptr = std::shared_ptr; + + Session(const Socket::Ptr &sock); + ~Session() override = default; + + /** + * 在创建 Session 后, Server 会把自身的配置参数通过该函数传递给 Session + * @param server, 服务器对象 + */ + virtual void attachServer(const Server &server) {} + + /** + * 作为该 Session 的唯一标识符 + * @return 唯一标识符 + */ + std::string getIdentifier() const override; + +private: + mutable std::string _id; + std::unique_ptr > _statistic_tcp; + std::unique_ptr > _statistic_udp; +}; + +// 通过该模板可以让TCP服务器快速支持TLS +template +class SessionWithSSL : public SessionType { +public: + template + SessionWithSSL(ArgsType &&...args) + : SessionType(std::forward(args)...) { + _ssl_box.setOnEncData([&](const Buffer::Ptr &buf) { public_send(buf); }); + _ssl_box.setOnDecData([&](const Buffer::Ptr &buf) { public_onRecv(buf); }); + } + + ~SessionWithSSL() override { _ssl_box.flush(); } + + void onRecv(const Buffer::Ptr &buf) override { _ssl_box.onRecv(buf); } + + // 添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug + inline void public_onRecv(const Buffer::Ptr &buf) { SessionType::onRecv(buf); } + inline void public_send(const Buffer::Ptr &buf) { SessionType::send(buf); } + + bool overSsl() const override { return true; } + +protected: + ssize_t send(Buffer::Ptr buf) override { + auto size = buf->size(); + _ssl_box.onSend(std::move(buf)); + return size; + } + +private: + SSL_Box _ssl_box; +}; + +} // namespace toolkit + +#endif // ZLTOOLKIT_SESSION_H \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/Socket.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/Socket.h new file mode 100644 index 00000000..d43549aa --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/Socket.h @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_SOCKET_H +#define NETWORK_SOCKET_H + +#include +#include +#include +#include +#include +#include +#include "Util/SpeedStatistic.h" +#include "sockutil.h" +#include "Poller/Timer.h" +#include "Poller/EventPoller.h" +#include "BufferSock.h" + +namespace toolkit { + +#if defined(MSG_NOSIGNAL) +#define FLAG_NOSIGNAL MSG_NOSIGNAL +#else +#define FLAG_NOSIGNAL 0 +#endif //MSG_NOSIGNAL + +#if defined(MSG_MORE) +#define FLAG_MORE MSG_MORE +#else +#define FLAG_MORE 0 +#endif //MSG_MORE + +#if defined(MSG_DONTWAIT) +#define FLAG_DONTWAIT MSG_DONTWAIT +#else +#define FLAG_DONTWAIT 0 +#endif //MSG_DONTWAIT + +//默认的socket flags:不触发SIGPIPE,非阻塞发送 +#define SOCKET_DEFAULE_FLAGS (FLAG_NOSIGNAL | FLAG_DONTWAIT ) + +//发送超时时间,如果在规定时间内一直没有发送数据成功,那么将触发onErr事件 +#define SEND_TIME_OUT_SEC 10 + +//错误类型枚举 +typedef enum { + Err_success = 0, //成功 success + Err_eof, //eof + Err_timeout, //超时 socket timeout + Err_refused,//连接被拒绝 socket refused + Err_reset,//连接被重置 socket reset + Err_dns,//dns解析失败 dns resolve failed + Err_shutdown,//主动关闭 socket shutdown + Err_other = 0xFF,//其他错误 other error +} ErrCode; + +//错误信息类 +class SockException : public std::exception { +public: + SockException(ErrCode code = Err_success, const std::string &msg = "", int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //重置错误 + void reset(ErrCode code, const std::string &msg, int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //错误提示 + const char *what() const noexcept override { + return _msg.c_str(); + } + + //错误代码 + ErrCode getErrCode() const { + return _code; + } + + //用户自定义错误代码 + int getCustomCode() const { + return _custom_code; + } + + //判断是否真的有错 + operator bool() const { + return _code != Err_success; + } + +private: + ErrCode _code; + int _custom_code = 0; + std::string _msg; +}; + +//std::cout等输出流可以直接输出SockException对象 +std::ostream &operator<<(std::ostream &ost, const SockException &err); + +class SockNum { +public: + using Ptr = std::shared_ptr; + + typedef enum { + Sock_Invalid = -1, + Sock_TCP = 0, + Sock_UDP = 1, + Sock_TCP_Server = 2 + } SockType; + + SockNum(int fd, SockType type) { + _fd = fd; + _type = type; + } + + ~SockNum() { +#if defined (OS_IPHONE) + unsetSocketOfIOS(_fd); +#endif //OS_IPHONE + // 停止socket收发能力 + #if defined(_WIN32) + ::shutdown(_fd, SD_BOTH); + #else + ::shutdown(_fd, SHUT_RDWR); + #endif + close(_fd); + } + + int rawFd() const { + return _fd; + } + + SockType type() { + return _type; + } + + void setConnected() { +#if defined (OS_IPHONE) + setSocketOfIOS(_fd); +#endif //OS_IPHONE + } + +#if defined (OS_IPHONE) +private: + void *readStream=nullptr; + void *writeStream=nullptr; + bool setSocketOfIOS(int socket); + void unsetSocketOfIOS(int socket); +#endif //OS_IPHONE + +private: + int _fd; + SockType _type; +}; + +//socket 文件描述符的包装 +//在析构时自动溢出监听并close套接字 +//防止描述符溢出 +class SockFD : public noncopyable { +public: + using Ptr = std::shared_ptr; + + /** + * 创建一个fd对象 + * @param num 文件描述符,int数字 + * @param poller 事件监听器 + */ + SockFD(SockNum::Ptr num, const EventPoller::Ptr &poller) { + _num = std::move(num); + _poller = poller; + } + + /** + * 复制一个fd对象 + * @param that 源对象 + * @param poller 事件监听器 + */ + SockFD(const SockFD &that, const EventPoller::Ptr &poller) { + _num = that._num; + _poller = poller; + if (_poller == that._poller) { + throw std::invalid_argument("Copy a SockFD with same poller"); + } + } + + ~SockFD() { delEvent(); } + + void delEvent() { + if (_poller) { + auto num = _num; + // 移除io事件成功后再close fd + _poller->delEvent(num->rawFd(), [num](bool) {}); + _poller = nullptr; + } + } + + void setConnected() { + _num->setConnected(); + } + + int rawFd() const { + return _num->rawFd(); + } + + const SockNum::Ptr& sockNum() const { + return _num; + } + + SockNum::SockType type() { + return _num->type(); + } + + const EventPoller::Ptr& getPoller() const { + return _poller; + } + +private: + SockNum::Ptr _num; + EventPoller::Ptr _poller; +}; + +template +class MutexWrapper { +public: + MutexWrapper(bool enable) { + _enable = enable; + } + + ~MutexWrapper() = default; + + inline void lock() { + if (_enable) { + _mtx.lock(); + } + } + + inline void unlock() { + if (_enable) { + _mtx.unlock(); + } + } + +private: + bool _enable; + Mtx _mtx; +}; + +class SockInfo { +public: + SockInfo() = default; + virtual ~SockInfo() = default; + + //获取本机ip + virtual std::string get_local_ip() = 0; + //获取本机端口号 + virtual uint16_t get_local_port() = 0; + //获取对方ip + virtual std::string get_peer_ip() = 0; + //获取对方端口号 + virtual uint16_t get_peer_port() = 0; + //获取标识符 + virtual std::string getIdentifier() const { return ""; } +}; + +#define TraceP(ptr) TraceL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define DebugP(ptr) DebugL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define InfoP(ptr) InfoL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define WarnP(ptr) WarnL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define ErrorP(ptr) ErrorL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " + +//异步IO Socket对象,包括tcp客户端、服务器和udp套接字 +class Socket : public std::enable_shared_from_this, public noncopyable, public SockInfo { +public: + using Ptr = std::shared_ptr; + //接收数据回调 + using onReadCB = std::function; + //发生错误回调 + using onErrCB = std::function; + //tcp监听接收到连接请求 + using onAcceptCB = std::function &complete)>; + //socket发送缓存清空事件,返回true代表下次继续监听该事件,否则停止 + using onFlush = std::function; + //在接收到连接请求前,拦截Socket默认生成方式 + using onCreateSocket = std::function; + //发送buffer成功与否回调 + using onSendResult = BufferList::SendResult; + + /** + * 构造socket对象,尚未有实质操作 + * @param poller 绑定的poller线程 + * @param enable_mutex 是否启用互斥锁(接口是否线程安全) + */ + static Ptr createSocket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true); + ~Socket() override; + + /** + * 创建tcp客户端并异步连接服务器 + * @param url 目标服务器ip或域名 + * @param port 目标服务器端口 + * @param con_cb 结果回调 + * @param timeout_sec 超时时间 + * @param local_ip 绑定本地网卡ip + * @param local_port 绑定本地网卡端口号 + */ + void connect(const std::string &url, uint16_t port, const onErrCB &con_cb, float timeout_sec = 5, const std::string &local_ip = "::", uint16_t local_port = 0); + + /** + * 创建tcp监听服务器 + * @param port 监听端口,0则随机 + * @param local_ip 监听的网卡ip + * @param backlog tcp最大积压数 + * @return 是否成功 + */ + bool listen(uint16_t port, const std::string &local_ip = "::", int backlog = 1024); + + /** + * 创建udp套接字,udp是无连接的,所以可以作为服务器和客户端 + * @param port 绑定的端口为0则随机 + * @param local_ip 绑定的网卡ip + * @return 是否成功 + */ + bool bindUdpSock(uint16_t port, const std::string &local_ip = "::", bool enable_reuse = true); + + /** + * 包装外部fd,本对象负责close fd + * 内部会设置fd为NoBlocked,NoSigpipe,CloExec + * 其他设置需要自行使用SockUtil进行设置 + */ + bool fromSock(int fd, SockNum::SockType type); + + /** + * 从另外一个Socket克隆 + * 目的是一个socket可以被多个poller对象监听,提高性能或实现Socket归属线程的迁移 + * @param other 原始的socket对象 + * @return 是否成功 + */ + bool cloneSocket(const Socket &other); + + ////////////设置事件回调//////////// + + /** + * 设置数据接收回调,tcp或udp客户端有效 + * @param cb 回调对象 + */ + void setOnRead(onReadCB cb); + + /** + * 设置异常事件(包括eof等)回调 + * @param cb 回调对象 + */ + void setOnErr(onErrCB cb); + + /** + * 设置tcp监听接收到连接回调 + * @param cb 回调对象 + */ + void setOnAccept(onAcceptCB cb); + + /** + * 设置socket写缓存清空事件回调 + * 通过该回调可以实现发送流控 + * @param cb 回调对象 + */ + void setOnFlush(onFlush cb); + + /** + * 设置accept时,socket构造事件回调 + * @param cb 回调 + */ + void setOnBeforeAccept(onCreateSocket cb); + + /** + * 设置发送buffer结果回调 + * @param cb 回调 + */ + void setOnSendResult(onSendResult cb); + + ////////////发送数据相关接口//////////// + + /** + * 发送数据指针 + * @param buf 数据指针 + * @param size 数据长度 + * @param addr 目标地址 + * @param addr_len 目标地址长度 + * @param try_flush 是否尝试写socket + * @return -1代表失败(socket无效),0代表数据长度为0,否则返回数据长度 + */ + ssize_t send(const char *buf, size_t size = 0, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送string + */ + ssize_t send(std::string buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送Buffer对象,Socket对象发送数据的统一出口 + * socket对象发送数据的统一出口 + */ + ssize_t send(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + */ + int flushAll(); + + /** + * 关闭socket且触发onErr回调,onErr回调将在poller线程中进行 + * @param err 错误原因 + * @return 是否成功触发onErr回调 + */ + bool emitErr(const SockException &err) noexcept; + + /** + * 关闭或开启数据接收 + * @param enabled 是否开启 + */ + void enableRecv(bool enabled); + + /** + * 获取裸文件描述符,请勿进行close操作(因为Socket对象会管理其生命周期) + * @return 文件描述符 + */ + int rawFD() const; + + /** + * tcp客户端是否处于连接状态 + * 支持Sock_TCP类型socket + */ + bool alive() const; + + /** + * 返回socket类型 + */ + SockNum::SockType sockType() const; + + /** + * 设置发送超时主动断开时间;默认10秒 + * @param second 发送超时数据,单位秒 + */ + void setSendTimeOutSecond(uint32_t second); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + * @return 套接字是否忙 + */ + bool isSocketBusy() const; + + /** + * 获取poller线程对象 + * @return poller线程对象 + */ + const EventPoller::Ptr &getPoller() const; + + /** + * 绑定udp 目标地址,后续发送时就不用再单独指定了 + * @param dst_addr 目标地址 + * @param addr_len 目标地址长度 + * @param soft_bind 是否软绑定,软绑定时不调用udp connect接口,只保存目标地址信息,发送时再传递到sendto函数 + * @return 是否成功 + */ + bool bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len = 0, bool soft_bind = false); + + /** + * 设置发送flags + * @param flags 发送的flag + */ + void setSendFlags(int flags = SOCKET_DEFAULE_FLAGS); + + /** + * 关闭套接字 + * @param close_fd 是否关闭fd还是只移除io事件监听 + */ + void closeSock(bool close_fd = true); + + /** + * 获取发送缓存包个数(不是字节数) + */ + size_t getSendBufferCount(); + + /** + * 获取上次socket发送缓存清空至今的毫秒数,单位毫秒 + */ + uint64_t elapsedTimeAfterFlushed(); + + /** + * 获取接收速率,单位bytes/s + */ + int getRecvSpeed(); + + /** + * 获取发送速率,单位bytes/s + */ + int getSendSpeed(); + + ////////////SockInfo override//////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + std::string getIdentifier() const override; + +private: + Socket(EventPoller::Ptr poller, bool enable_mutex = true); + + void setSock(SockNum::Ptr sock); + int onAccept(const SockNum::Ptr &sock, int event) noexcept; + ssize_t onRead(const SockNum::Ptr &sock, const BufferRaw::Ptr &buffer) noexcept; + void onWriteAble(const SockNum::Ptr &sock); + void onConnected(const SockNum::Ptr &sock, const onErrCB &cb); + void onFlushed(); + void startWriteAbleEvent(const SockNum::Ptr &sock); + void stopWriteAbleEvent(const SockNum::Ptr &sock); + bool flushData(const SockNum::Ptr &sock, bool poller_thread); + bool attachEvent(const SockNum::Ptr &sock); + ssize_t send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush = true); + void connect_l(const std::string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const std::string &local_ip, uint16_t local_port); + bool fromSock_l(SockNum::Ptr sock); + +private: + //send socket时的flag + int _sock_flags = SOCKET_DEFAULE_FLAGS; + //最大发送缓存,单位毫秒,距上次发送缓存清空时间不能超过该参数 + uint32_t _max_send_buffer_ms = SEND_TIME_OUT_SEC * 1000; + //控制是否接收监听socket可读事件,关闭后可用于流量控制 + std::atomic _enable_recv {true}; + //标记该socket是否可写,socket写缓存满了就不可写 + std::atomic _sendable {true}; + //是否已经触发err回调了 + bool _err_emit = false; + //是否启用网速统计 + bool _enable_speed = false; + // udp发送目标地址 + std::shared_ptr _udp_send_dst; + + //接收速率统计 + BytesSpeed _recv_speed; + //发送速率统计 + BytesSpeed _send_speed; + + //tcp连接超时定时器 + Timer::Ptr _con_timer; + //tcp连接结果回调对象 + std::shared_ptr _async_con_cb; + + //记录上次发送缓存(包括socket写缓存、应用层缓存)清空的计时器 + Ticker _send_flush_ticker; + //socket fd的抽象类 + SockFD::Ptr _sock_fd; + //本socket绑定的poller线程,事件触发于此线程 + EventPoller::Ptr _poller; + //跨线程访问_sock_fd时需要上锁 + mutable MutexWrapper _mtx_sock_fd; + + //socket异常事件(比如说断开) + onErrCB _on_err; + //收到数据事件 + onReadCB _on_read; + //socket缓存清空事件(可用于发送流速控制) + onFlush _on_flush; + //tcp监听收到accept请求事件 + onAcceptCB _on_accept; + //tcp监听收到accept请求,自定义创建peer Socket事件(可以控制子Socket绑定到其他poller线程) + onCreateSocket _on_before_accept; + //设置上述回调函数的锁 + MutexWrapper _mtx_event; + + //一级发送缓存, socket可写时,会把一级缓存批量送入到二级缓存 + List > _send_buf_waiting; + //一级发送缓存锁 + MutexWrapper _mtx_send_buf_waiting; + //二级发送缓存, socket可写时,会把二级缓存批量写入到socket + List _send_buf_sending; + //二级发送缓存锁 + MutexWrapper _mtx_send_buf_sending; + //发送buffer结果回调 + BufferList::SendResult _send_result; + //对象个数统计 + ObjectStatistic _statistic; + + //链接缓存地址,防止tcp reset 导致无法获取对端的地址 + struct sockaddr_storage _local_addr; + struct sockaddr_storage _peer_addr; +}; + +class SockSender { +public: + SockSender() = default; + virtual ~SockSender() = default; + virtual ssize_t send(Buffer::Ptr buf) = 0; + virtual void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) = 0; + + //发送char * + SockSender &operator << (const char *buf); + //发送字符串 + SockSender &operator << (std::string buf); + //发送Buffer对象 + SockSender &operator << (Buffer::Ptr buf); + + //发送其他类型是数据 + template + SockSender &operator << (T &&buf) { + std::ostringstream ss; + ss << std::forward(buf); + send(ss.str()); + return *this; + } + + ssize_t send(std::string buf); + ssize_t send(const char *buf, size_t size = 0); +}; + +//Socket对象的包装类 +class SocketHelper : public SockSender, public SockInfo, public TaskExecutorInterface, public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + SocketHelper(const Socket::Ptr &sock); + ~SocketHelper() override = default; + + ///////////////////// Socket util std::functions ///////////////////// + /** + * 获取poller线程 + */ + const EventPoller::Ptr& getPoller() const; + + /** + * 设置批量发送标记,用于提升性能 + * @param try_flush 批量发送标记 + */ + void setSendFlushFlag(bool try_flush); + + /** + * 设置socket发送flags + * @param flags socket发送flags + */ + void setSendFlags(int flags); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + */ + bool isSocketBusy() const; + + /** + * 设置Socket创建器,自定义Socket创建方式 + * @param cb 创建器 + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 创建socket对象 + */ + Socket::Ptr createSocket(); + + /** + * 获取socket对象 + */ + const Socket::Ptr &getSock() const; + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + */ + int flushAll(); + + /** + * 是否ssl加密 + */ + virtual bool overSsl() const { return false; } + + ///////////////////// SockInfo override ///////////////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + + ///////////////////// TaskExecutorInterface override ///////////////////// + /** + * 任务切换到所属poller线程执行 + * @param task 任务 + * @param may_sync 是否运行同步执行任务 + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + ///////////////////// SockSender override ///////////////////// + + /** + * 使能 SockSender 其他未被重写的send重载函数 + */ + using SockSender::send; + + /** + * 统一发送数据的出口 + */ + ssize_t send(Buffer::Ptr buf) override; + + /** + * 触发onErr事件 + */ + void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override; + + /** + * 线程安全的脱离 Server 并触发 onError 事件 + * @param ex 触发 onError 事件的原因 + */ + void safeShutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")); + + ///////////////////// event functions ///////////////////// + /** + * 接收数据入口 + * @param buf 数据,可以重复使用内存区,不可被缓存使用 + */ + virtual void onRecv(const Buffer::Ptr &buf) = 0; + + /** + * 收到 eof 或其他导致脱离 Server 事件的回调 + * 收到该事件时, 该对象一般将立即被销毁 + * @param err 原因 + */ + virtual void onError(const SockException &err) = 0; + + /** + * 数据全部发送完毕后回调 + */ + virtual void onFlush() {} + + /** + * 每隔一段时间触发, 用来做超时管理 + */ + virtual void onManager() = 0; + +protected: + void setPoller(const EventPoller::Ptr &poller); + void setSock(const Socket::Ptr &sock); + +private: + bool _try_flush = true; + Socket::Ptr _sock; + EventPoller::Ptr _poller; + Socket::onCreateSocket _on_create_socket; +}; + +} // namespace toolkit +#endif /* NETWORK_SOCKET_H */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpClient.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpClient.h new file mode 100644 index 00000000..f470f4ab --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpClient.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_TCPCLIENT_H +#define NETWORK_TCPCLIENT_H + +#include +#include "Socket.h" +#include "Util/SSLBox.h" + +namespace toolkit { + +//Tcp客户端,Socket对象默认开始互斥锁 +class TcpClient : public SocketHelper { +public: + using Ptr = std::shared_ptr; + TcpClient(const EventPoller::Ptr &poller = nullptr); + ~TcpClient() override; + + /** + * 开始连接tcp服务器 + * @param url 服务器ip或域名 + * @param port 服务器端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + */ + virtual void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0); + + /** + * 通过代理开始连接tcp服务器 + * @param url 服务器ip或域名 + * @proxy_host 代理ip + * @proxy_port 代理端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + */ + virtual void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0){}; + + /** + * 主动断开连接 + * @param ex 触发onErr事件时的参数 + */ + void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override; + + /** + * 连接中或已连接返回true,断开连接时返回false + */ + virtual bool alive() const; + + /** + * 设置网卡适配器,使用该网卡与服务器通信 + * @param local_ip 本地网卡ip + */ + virtual void setNetAdapter(const std::string &local_ip); + + /** + * 唯一标识 + */ + std::string getIdentifier() const override; + +protected: + /** + * 连接服务器结果回调 + * @param ex 成功与否 + */ + virtual void onConnect(const SockException &ex) = 0; + + /** + * tcp连接成功后每2秒触发一次该事件 + */ + void onManager() override {} + +private: + void onSockConnect(const SockException &ex); + +private: + mutable std::string _id; + std::string _net_adapter = "::"; + std::shared_ptr _timer; + //对象个数统计 + ObjectStatistic _statistic; +}; + +//用于实现TLS客户端的模板对象 +template +class TcpClientWithSSL : public TcpClientType { +public: + using Ptr = std::shared_ptr; + + template + TcpClientWithSSL(ArgsType &&...args):TcpClientType(std::forward(args)...) {} + + ~TcpClientWithSSL() override { + if (_ssl_box) { + _ssl_box->flush(); + } + } + + void onRecv(const Buffer::Ptr &buf) override { + if (_ssl_box) { + _ssl_box->onRecv(buf); + } else { + TcpClientType::onRecv(buf); + } + } + + // 使能其他未被重写的send函数 + using TcpClientType::send; + + ssize_t send(Buffer::Ptr buf) override { + if (_ssl_box) { + auto size = buf->size(); + _ssl_box->onSend(std::move(buf)); + return size; + } + return TcpClientType::send(std::move(buf)); + } + + //添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug + inline void public_onRecv(const Buffer::Ptr &buf) { + TcpClientType::onRecv(buf); + } + + inline void public_send(const Buffer::Ptr &buf) { + TcpClientType::send(buf); + } + + void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(url, port, timeout_sec, local_port); + } + void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(proxy_host, proxy_port, timeout_sec, local_port); + } + + bool overSsl() const override { return (bool)_ssl_box; } + +protected: + void onConnect(const SockException &ex) override { + if (!ex) { + _ssl_box = std::make_shared(false); + _ssl_box->setOnDecData([this](const Buffer::Ptr &buf) { + public_onRecv(buf); + }); + _ssl_box->setOnEncData([this](const Buffer::Ptr &buf) { + public_send(buf); + }); + + if (!isIP(_host.data())) { + //设置ssl域名 + _ssl_box->setHost(_host.data()); + } + } + TcpClientType::onConnect(ex); + } + /** + * 重置ssl, 主要为了解决一些302跳转时http与https的转换 + */ + void setDoNotUseSSL() { + _ssl_box.reset(); + } +private: + std::string _host; + std::shared_ptr _ssl_box; +}; + +} /* namespace toolkit */ +#endif /* NETWORK_TCPCLIENT_H */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpServer.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpServer.h new file mode 100644 index 00000000..1e0da5d1 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/TcpServer.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TCPSERVER_TCPSERVER_H +#define TCPSERVER_TCPSERVER_H + +#include +#include +#include +#include "Server.h" +#include "Session.h" +#include "Poller/Timer.h" +#include "Util/util.h" + +namespace toolkit { + +//TCP服务器,可配置的;配置通过Session::attachServer方法传递给会话对象 +class TcpServer : public Server { +public: + using Ptr = std::shared_ptr; + + /** + * 创建tcp服务器,listen fd的accept事件会加入到所有的poller线程中监听 + * 在调用TcpServer::start函数时,内部会创建多个子TcpServer对象, + * 这些子TcpServer对象通过Socket对象克隆的方式在多个poller线程中监听同一个listen fd + * 这样这个TCP服务器将会通过抢占式accept的方式把客户端均匀的分布到不同的poller线程 + * 通过该方式能实现客户端负载均衡以及提高连接接收速度 + */ + explicit TcpServer(const EventPoller::Ptr &poller = nullptr); + ~TcpServer() override; + + /** + * @brief 开始tcp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + * @param backlog tcp listen backlog + */ + template + void start(uint16_t port, const std::string &host = "::", uint32_t backlog = 1024) { + static std::string cls_name = toolkit::demangle(typeid(SessionType).name()); + //Session创建器,通过它创建不同类型的服务器 + _session_alloc = [](const TcpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType * ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + TraceP(static_cast(session.get())) << cls_name; + session->setOnCreateSocket(server->_on_create_socket); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host, backlog); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 根据socket对象创建Session对象 + * 需要确保在socket归属poller线程执行本函数 + */ + Session::Ptr createSession(const Socket::Ptr &socket); + +protected: + virtual void cloneFrom(const TcpServer &that); + virtual TcpServer::Ptr onCreatServer(const EventPoller::Ptr &poller); + + virtual Session::Ptr onAcceptConnection(const Socket::Ptr &sock); + virtual Socket::Ptr onBeforeAcceptConnection(const EventPoller::Ptr &poller); + +private: + void onManagerSession(); + Socket::Ptr createSocket(const EventPoller::Ptr &poller); + void start_l(uint16_t port, const std::string &host, uint32_t backlog); + Ptr getServer(const EventPoller *) const; + void setupEvent(); + +private: + bool _is_on_manager = false; + bool _main_server = true; + std::weak_ptr _parent; + Socket::Ptr _socket; + std::shared_ptr _timer; + Socket::onCreateSocket _on_create_socket; + std::unordered_map _session_map; + std::function _session_alloc; + std::unordered_map _cloned_server; + //对象个数统计 + ObjectStatistic _statistic; +}; + +} /* namespace toolkit */ +#endif /* TCPSERVER_TCPSERVER_H */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/UdpServer.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/UdpServer.h new file mode 100644 index 00000000..2fe33bdd --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/UdpServer.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TOOLKIT_NETWORK_UDPSERVER_H +#define TOOLKIT_NETWORK_UDPSERVER_H + +#include "Server.h" +#include "Session.h" + +namespace toolkit { + +class UdpServer : public Server { +public: + using Ptr = std::shared_ptr; + using PeerIdType = std::string; + using onCreateSocket = std::function; + + explicit UdpServer(const EventPoller::Ptr &poller = nullptr); + ~UdpServer() override; + + /** + * @brief 开始监听服务器 + */ + template + void start(uint16_t port, const std::string &host = "::") { + static std::string cls_name = toolkit::demangle(typeid(SessionType).name()); + // Session 创建器, 通过它创建不同类型的服务器 + _session_alloc = [](const UdpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType * ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + TraceP(static_cast(session.get())) << cls_name; + auto sock_creator = server->_on_create_socket; + session->setOnCreateSocket([sock_creator](const EventPoller::Ptr &poller) { + return sock_creator(poller, nullptr, nullptr, 0); + }); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + */ + void setOnCreateSocket(onCreateSocket cb); + +protected: + virtual Ptr onCreatServer(const EventPoller::Ptr &poller); + virtual void cloneFrom(const UdpServer &that); + +private: + /** + * @brief 开始udp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + */ + void start_l(uint16_t port, const std::string &host = "::"); + + /** + * @brief 定时管理 Session, UDP 会话需要根据需要处理超时 + */ + void onManagerSession(); + + void onRead(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 接收到数据,可能来自server fd,也可能来自peer fd + * @param is_server_fd 时候为server fd + * @param id 客户端id + * @param buf 数据 + * @param addr 客户端地址 + * @param addr_len 客户端地址长度 + */ + void onRead_l(bool is_server_fd, const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 根据对端信息获取或创建一个会话 + */ + SessionHelper::Ptr getOrCreateSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len, bool &is_new); + + /** + * @brief 创建一个会话, 同时进行必要的设置 + */ + SessionHelper::Ptr createSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 创建socket + */ + Socket::Ptr createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf = nullptr, struct sockaddr *addr = nullptr, int addr_len = 0); + + void setupEvent(); + +private: + bool _cloned = false; + Socket::Ptr _socket; + std::shared_ptr _timer; + onCreateSocket _on_create_socket; + //cloned server共享主server的session map,防止数据在不同server间漂移 + std::shared_ptr _session_mutex; + std::shared_ptr > _session_map; + //主server持有cloned server的引用 + std::unordered_map _cloned_server; + std::function _session_alloc; + // 对象个数统计 + ObjectStatistic _statistic; +}; + +} // namespace toolkit + +#endif // TOOLKIT_NETWORK_UDPSERVER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Network/sockutil.h b/webrtc_player/android/zlm/src/main/cpp/include/Network/sockutil.h new file mode 100644 index 00000000..9f7af5c1 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Network/sockutil.h @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef NETWORK_SOCKUTIL_H +#define NETWORK_SOCKUTIL_H + +#if defined(_WIN32) +#include +#include +#include +#pragma comment (lib, "Ws2_32.lib") +#pragma comment(lib,"Iphlpapi.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#endif // defined(_WIN32) + +#include +#include +#include +#include +#include + +namespace toolkit { + +#if defined(_WIN32) +#ifndef socklen_t +#define socklen_t int +#endif //!socklen_t +int ioctl(int fd, long cmd, u_long *ptr); +int close(int fd); +#endif // defined(_WIN32) + +#if !defined(SOCKET_DEFAULT_BUF_SIZE) +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#else +#if SOCKET_DEFAULT_BUF_SIZE == 0 && !defined(__linux__) +// just for linux, because in some high-throughput environments, +// kernel control is more efficient and reasonable than program +// settings. For example, refer to cloudflare's blog +#undef SOCKET_DEFAULT_BUF_SIZE +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#endif +#endif +#define TCP_KEEPALIVE_INTERVAL 30 +#define TCP_KEEPALIVE_PROBE_TIMES 9 +#define TCP_KEEPALIVE_TIME 120 + +//套接字工具类,封装了socket、网络的一些基本操作 +class SockUtil { +public: + /** + * 创建tcp客户端套接字并连接服务器 + * @param host 服务器ip或域名 + * @param port 服务器端口号 + * @param async 是否异步连接 + * @param local_ip 绑定的本地网卡ip + * @param local_port 绑定的本地端口号 + * @return -1代表失败,其他为socket fd号 + */ + static int connect(const char *host, uint16_t port, bool async = true, const char *local_ip = "::", uint16_t local_port = 0); + + /** + * 创建tcp监听套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param back_log accept列队长度 + * @return -1代表失败,其他为socket fd号 + */ + static int listen(const uint16_t port, const char *local_ip = "::", int back_log = 1024); + + /** + * 创建udp套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param enable_reuse 是否允许重复bind端口 + * @return -1代表失败,其他为socket fd号 + */ + static int bindUdpSock(const uint16_t port, const char *local_ip = "::", bool enable_reuse = true); + + /** + * @brief 解除与 sock 相关的绑定关系 + * @param sock, socket fd 号 + * @return 0 成功, -1 失败 + */ + static int dissolveUdpSock(int sock); + + /** + * 开启TCP_NODELAY,降低TCP交互延时 + * @param fd socket fd号 + * @param on 是否开启 + * @return 0代表成功,-1为失败 + */ + static int setNoDelay(int fd, bool on = true); + + /** + * 写socket不触发SIG_PIPE信号(貌似只有mac有效) + * @param fd socket fd号 + * @return 0代表成功,-1为失败 + */ + static int setNoSigpipe(int fd); + + /** + * 设置读写socket是否阻塞 + * @param fd socket fd号 + * @param noblock 是否阻塞 + * @return 0代表成功,-1为失败 + */ + static int setNoBlocked(int fd, bool noblock = true); + + /** + * 设置socket接收缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + */ + static int setRecvBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置socket接收缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + */ + static int setSendBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置后续可绑定复用端口(处于TIME_WAITE状态) + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setReuseable(int fd, bool on = true, bool reuse_port = true); + + /** + * 运行发送或接收udp广播信息 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setBroadcast(int fd, bool on = true); + + /** + * 是否开启TCP KeepAlive特性 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @param idle keepalive空闲时间 + * @param interval keepalive探测时间间隔 + * @param times keepalive探测次数 + * @return 0代表成功,-1为失败 + */ + static int setKeepAlive(int fd, bool on = true, int interval = TCP_KEEPALIVE_INTERVAL, int idle = TCP_KEEPALIVE_TIME, int times = TCP_KEEPALIVE_PROBE_TIMES); + + /** + * 是否开启FD_CLOEXEC特性(多进程相关) + * @param fd fd号,不一定是socket + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setCloExec(int fd, bool on = true); + + /** + * 开启SO_LINGER特性 + * @param sock socket fd号 + * @param second 内核等待关闭socket超时时间,单位秒 + * @return 0代表成功,-1为失败 + */ + static int setCloseWait(int sock, int second = 0); + + /** + * dns解析 + * @param host 域名或ip + * @param port 端口号 + * @param addr sockaddr结构体 + * @return 是否成功 + */ + static bool getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, int ai_family = AF_INET, + int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60); + + /** + * 设置组播ttl + * @param sock socket fd号 + * @param ttl ttl值 + * @return 0代表成功,-1为失败 + */ + static int setMultiTTL(int sock, uint8_t ttl = 64); + + /** + * 设置组播发送网卡 + * @param sock socket fd号 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int setMultiIF(int sock, const char *local_ip); + + /** + * 设置是否接收本机发出的组播包 + * @param fd socket fd号 + * @param acc 是否接收 + * @return 0代表成功,-1为失败 + */ + static int setMultiLOOP(int fd, bool acc = false); + + /** + * 加入组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int joinMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int leaveMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 加入组播并只接受该源端的组播数据 + * @param sock socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int joinMultiAddrFilter(int sock, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 获取该socket当前发生的错误 + * @param fd socket fd号 + * @return 错误代码 + */ + static int getSockError(int fd); + + /** + * 获取网卡列表 + * @return vector > + */ + static std::vector> getInterfaceList(); + + /** + * 获取本机默认网卡ip + */ + static std::string get_local_ip(); + + /** + * 获取该socket绑定的本地ip + * @param sock socket fd号 + */ + static std::string get_local_ip(int sock); + + /** + * 获取该socket绑定的本地端口 + * @param sock socket fd号 + */ + static uint16_t get_local_port(int sock); + + /** + * 获取该socket绑定的远端ip + * @param sock socket fd号 + */ + static std::string get_peer_ip(int sock); + + /** + * 获取该socket绑定的远端端口 + * @param sock socket fd号 + */ + static uint16_t get_peer_port(int sock); + + static bool support_ipv6(); + /** + * 线程安全的in_addr转ip字符串 + */ + static std::string inet_ntoa(const struct in_addr &addr); + static std::string inet_ntoa(const struct in6_addr &addr); + static std::string inet_ntoa(const struct sockaddr *addr); + static uint16_t inet_port(const struct sockaddr *addr); + static struct sockaddr_storage make_sockaddr(const char *ip, uint16_t port); + static socklen_t get_sock_len(const struct sockaddr *addr); + static bool get_sock_local_addr(int fd, struct sockaddr_storage &addr); + static bool get_sock_peer_addr(int fd, struct sockaddr_storage &addr); + + /** + * 获取网卡ip + * @param if_name 网卡名 + */ + static std::string get_ifr_ip(const char *if_name); + + /** + * 获取网卡名 + * @param local_op 网卡ip + */ + static std::string get_ifr_name(const char *local_op); + + /** + * 根据网卡名获取子网掩码 + * @param if_name 网卡名 + */ + static std::string get_ifr_mask(const char *if_name); + + /** + * 根据网卡名获取广播地址 + * @param if_name 网卡名 + */ + static std::string get_ifr_brdaddr(const char *if_name); + + /** + * 判断两个ip是否为同一网段 + * @param src_ip 我的ip + * @param dts_ip 对方ip + */ + static bool in_same_lan(const char *src_ip, const char *dts_ip); + + /** + * 判断是否为ipv4地址 + */ + static bool is_ipv4(const char *str); + + /** + * 判断是否为ipv6地址 + */ + static bool is_ipv6(const char *str); +}; + +} // namespace toolkit +#endif // !NETWORK_SOCKUTIL_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Player/MediaPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Player/MediaPlayer.h new file mode 100644 index 00000000..ca187019 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Player/MediaPlayer.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_PLAYER_MEDIAPLAYER_H_ +#define SRC_PLAYER_MEDIAPLAYER_H_ + +#include +#include +#include "PlayerBase.h" + +namespace mediakit { + +class MediaPlayer : public PlayerImp { +public: + using Ptr = std::shared_ptr; + + MediaPlayer(const toolkit::EventPoller::Ptr &poller = nullptr); + + void play(const std::string &url) override; + toolkit::EventPoller::Ptr getPoller(); + void setOnCreateSocket(toolkit::Socket::onCreateSocket cb); + +private: + toolkit::EventPoller::Ptr _poller; + toolkit::Socket::onCreateSocket _on_create_socket; +}; + +} /* namespace mediakit */ + +#endif /* SRC_PLAYER_MEDIAPLAYER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerBase.h b/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerBase.h new file mode 100644 index 00000000..b2f2f27d --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerBase.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_PLAYER_PLAYERBASE_H_ +#define SRC_PLAYER_PLAYERBASE_H_ + +#include +#include +#include +#include +#include "Network/Socket.h" +#include "Util/mini.h" +#include "Common/MediaSource.h" +#include "Common/MediaSink.h" +#include "Extension/Frame.h" +#include "Extension/Track.h" + +namespace mediakit { + +class PlayerBase : public TrackSource, public toolkit::mINI { +public: + using Ptr = std::shared_ptr; + using Event = std::function; + + static Ptr createPlayer(const toolkit::EventPoller::Ptr &poller, const std::string &strUrl); + + PlayerBase(); + + /** + * 开始播放 + * @param url 视频url,支持rtsp/rtmp + */ + virtual void play(const std::string &url) {}; + + /** + * 暂停或恢复 + * @param flag true:暂停,false:恢复 + */ + virtual void pause(bool flag) {}; + + /** + * 获取节目总时长,单位秒 + */ + virtual float getDuration() const { return 0; }; + + /** + * 倍数播放 + * @param speed 1.0 2.0 0.5 + */ + virtual void speed(float speed) {}; + + /** + * 中断播放 + */ + virtual void teardown() {}; + + /** + * 获取播放进度,取值 0.0 ~ 1.0 + */ + virtual float getProgress() const { return 0; }; + + /** + * 获取播放进度pos,取值 相对开始时间增量 单位秒 + */ + virtual uint32_t getProgressPos() const { return 0; }; + + /** + * 拖动进度条 + * @param progress 进度,取值 0.0 ~ 1.0 + */ + virtual void seekTo(float progress) {}; + + /** + * 拖动进度条 + * @param pos 进度,取值 相对于开始时间的增量 单位秒 + */ + virtual void seekTo(uint32_t pos) {}; + + /** + * 获取丢包率,只支持rtsp + * @param type 音频或视频,TrackInvalid时为总丢包率 + */ + virtual float getPacketLossRate(TrackType type) const { return -1; }; + + /** + * 获取所有track + */ + std::vector getTracks(bool ready = true) const override { return std::vector(); }; + + /** + * 设置一个MediaSource,直接生产rtsp/rtmp代理 + */ + virtual void setMediaSource(const MediaSource::Ptr &src) = 0; + + /** + * 设置异常中断回调 + */ + virtual void setOnShutdown(const Event &cb) = 0; + + /** + * 设置播放结果回调 + */ + virtual void setOnPlayResult(const Event &cb) = 0; + + /** + * 设置播放恢复回调 + */ + virtual void setOnResume(const std::function &cb) = 0; + +protected: + virtual void onResume() = 0; + virtual void onShutdown(const toolkit::SockException &ex) = 0; + virtual void onPlayResult(const toolkit::SockException &ex) = 0; +}; + +template +class PlayerImp : public Parent { +public: + using Ptr = std::shared_ptr; + + template + PlayerImp(ArgsType &&...args) : Parent(std::forward(args)...) {} + + void play(const std::string &url) override { + return _delegate ? _delegate->play(url) : Parent::play(url); + } + + void pause(bool flag) override { + return _delegate ? _delegate->pause(flag) : Parent::pause(flag); + } + + void speed(float speed) override { + return _delegate ? _delegate->speed(speed) : Parent::speed(speed); + } + + void teardown() override { + return _delegate ? _delegate->teardown() : Parent::teardown(); + } + + float getPacketLossRate(TrackType type) const override { + return _delegate ? _delegate->getPacketLossRate(type) : Parent::getPacketLossRate(type); + } + + float getDuration() const override { + return _delegate ? _delegate->getDuration() : Parent::getDuration(); + } + + float getProgress() const override { + return _delegate ? _delegate->getProgress() : Parent::getProgress(); + } + + uint32_t getProgressPos() const override { + return _delegate ? _delegate->getProgressPos() : Parent::getProgressPos(); + } + + void seekTo(float progress) override { + return _delegate ? _delegate->seekTo(progress) : Parent::seekTo(progress); + } + + void seekTo(uint32_t pos) override { + return _delegate ? _delegate->seekTo(pos) : Parent::seekTo(pos); + } + + std::vector getTracks(bool ready = true) const override { + return _delegate ? _delegate->getTracks(ready) : Parent::getTracks(ready); + } + + std::shared_ptr getSockInfo() const { + return std::dynamic_pointer_cast(_delegate); + } + + void setMediaSource(const MediaSource::Ptr &src) override { + if (_delegate) { + _delegate->setMediaSource(src); + } + _media_src = src; + } + + void setOnShutdown(const std::function &cb) override { + if (_delegate) { + _delegate->setOnShutdown(cb); + } + _on_shutdown = cb; + } + + void setOnPlayResult(const std::function &cb) override { + if (_delegate) { + _delegate->setOnPlayResult(cb); + } + _on_play_result = cb; + } + + void setOnResume(const std::function &cb) override { + if (_delegate) { + _delegate->setOnResume(cb); + } + _on_resume = cb; + } + +protected: + void onShutdown(const toolkit::SockException &ex) override { + if (_on_shutdown) { + _on_shutdown(ex); + _on_shutdown = nullptr; + } + } + + void onPlayResult(const toolkit::SockException &ex) override { + if (_on_play_result) { + _on_play_result(ex); + _on_play_result = nullptr; + } + } + + void onResume() override { + if (_on_resume) { + _on_resume(); + } + } + +protected: + std::function _on_resume; + PlayerBase::Event _on_shutdown; + PlayerBase::Event _on_play_result; + MediaSource::Ptr _media_src; + std::shared_ptr _delegate; +}; + +} /* namespace mediakit */ + +#endif /* SRC_PLAYER_PLAYERBASE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerProxy.h b/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerProxy.h new file mode 100644 index 00000000..c3c5b5bc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Player/PlayerProxy.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_DEVICE_PLAYERPROXY_H_ +#define SRC_DEVICE_PLAYERPROXY_H_ + +#include "Common/MultiMediaSourceMuxer.h" +#include "Player/MediaPlayer.h" +#include "Util/TimeTicker.h" +#include + +namespace mediakit { + +struct StreamInfo +{ + TrackType codec_type; + std::string codec_name; + int bitrate; + int audio_sample_rate; + int audio_sample_bit; + int audio_channel; + int video_width; + int video_height; + float video_fps; + + StreamInfo() + { + codec_type = TrackInvalid; + codec_name = "none"; + bitrate = -1; + audio_sample_rate = -1; + audio_channel = -1; + audio_sample_bit = -1; + video_height = -1; + video_width = -1; + video_fps = -1.0; + } +}; + +struct TranslationInfo +{ + std::vector stream_info; + int byte_speed; + uint64_t start_time_stamp; + + TranslationInfo() + { + byte_speed = -1; + start_time_stamp = 0; + } +}; + +class PlayerProxy + : public MediaPlayer + , public MediaSourceEvent + , public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 + // 默认一直重试 + PlayerProxy( + const std::string &vhost, const std::string &app, const std::string &stream_id, const ProtocolOption &option, int retry_count = -1, + const toolkit::EventPoller::Ptr &poller = nullptr, int reconnect_delay_min = 2, int reconnect_delay_max = 60, int reconnect_delay_step = 3); + + ~PlayerProxy() override; + + /** + * 设置play结果回调,只触发一次;在play执行之前有效 + * @param cb 回调对象 + */ + void setPlayCallbackOnce(std::function cb); + + /** + * 设置主动关闭回调 + * @param cb 回调对象 + */ + void setOnClose(std::function cb); + + /** + * Set a callback for failed server connection + * @param cb 回调对象 + */ + void setOnDisconnect(std::function cb); + + /** + * Set a callback for a successful connection to the server + * @param cb 回调对象 + */ + void setOnConnect(std::function cb); + + /** + * 开始拉流播放 + * @param strUrl + */ + void play(const std::string &strUrl) override; + + /** + * 获取观看总人数 + */ + int totalReaderCount(); + + int getStatus(); + uint64_t getLiveSecs(); + uint64_t getRePullCount(); + + // Using this only makes sense after a successful connection to the server + TranslationInfo getTranslationInfo(); + +private: + // MediaSourceEvent override + bool close(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; + MediaOriginType getOriginType(MediaSource &sender) const override; + std::string getOriginUrl(MediaSource &sender) const override; + std::shared_ptr getOriginSock(MediaSource &sender) const override; + float getLossRate(MediaSource &sender, TrackType type) override; + + void rePlay(const std::string &strUrl, int iFailedCnt); + void onPlaySuccess(); + void setDirectProxy(); + void setTranslationInfo(); + +private: + ProtocolOption _option; + int _retry_count; + int _reconnect_delay_min; + int _reconnect_delay_max; + int _reconnect_delay_step; + MediaTuple _tuple; + std::string _pull_url; + toolkit::Timer::Ptr _timer; + std::function _on_disconnect; + std::function _on_connect; + std::function _on_close; + std::function _on_play; + TranslationInfo _transtalion_info; + MultiMediaSourceMuxer::Ptr _muxer; + + toolkit::Ticker _live_ticker; + // 0 表示正常 1 表示正在尝试拉流 + std::atomic _live_status; + std::atomic _live_secs; + + std::atomic _repull_count; +}; + +} /* namespace mediakit */ +#endif /* SRC_DEVICE_PLAYERPROXY_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Poller/EventPoller.h b/webrtc_player/android/zlm/src/main/cpp/include/Poller/EventPoller.h new file mode 100644 index 00000000..b566276e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Poller/EventPoller.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef EventPoller_h +#define EventPoller_h + +#include +#include +#include +#include +#include +#include +#include "PipeWrap.h" +#include "Util/logger.h" +#include "Util/List.h" +#include "Thread/TaskExecutor.h" +#include "Thread/ThreadPool.h" +#include "Network/Buffer.h" + +#if defined(__linux__) || defined(__linux) +#define HAS_EPOLL +#endif //__linux__ + +namespace toolkit { + +class EventPoller : public TaskExecutor, public AnyStorage, public std::enable_shared_from_this { +public: + friend class TaskExecutorGetterImp; + + using Ptr = std::shared_ptr; + using PollEventCB = std::function; + using PollCompleteCB = std::function; + using DelayTask = TaskCancelableImp; + + typedef enum { + Event_Read = 1 << 0, //读事件 + Event_Write = 1 << 1, //写事件 + Event_Error = 1 << 2, //错误事件 + Event_LT = 1 << 3,//水平触发 + } Poll_Event; + + ~EventPoller(); + + /** + * 获取EventPollerPool单例中的第一个EventPoller实例, + * 保留该接口是为了兼容老代码 + * @return 单例 + */ + static EventPoller &Instance(); + + /** + * 添加事件监听 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @param cb 事件回调functional + * @return -1:失败,0:成功 + */ + int addEvent(int fd, int event, PollEventCB cb); + + /** + * 删除事件监听 + * @param fd 监听的文件描述符 + * @param cb 删除成功回调functional + * @return -1:失败,0:成功 + */ + int delEvent(int fd, PollCompleteCB cb = nullptr); + + /** + * 修改监听事件类型 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @return -1:失败,0:成功 + */ + int modifyEvent(int fd, int event, PollCompleteCB cb = nullptr); + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + + /** + * 同async方法,不过是把任务打入任务列队头,这样任务优先级最高 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + */ + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + /** + * 判断执行该接口的线程是否为本对象的轮询线程 + * @return 是否为本对象的轮询线程 + */ + bool isCurrentThread(); + + /** + * 延时执行某个任务 + * @param delay_ms 延时毫秒数 + * @param task 任务,返回值为0时代表不再重复任务,否则为下次执行延时,如果任务中抛异常,那么默认不重复任务 + * @return 可取消的任务标签 + */ + DelayTask::Ptr doDelayTask(uint64_t delay_ms, std::function task); + + /** + * 获取当前线程关联的Poller实例 + */ + static EventPoller::Ptr getCurrentPoller(); + + /** + * 获取当前线程下所有socket共享的读缓存 + */ + BufferRaw::Ptr getSharedBuffer(); + + /** + * 获取poller线程id + */ + std::thread::id getThreadId() const; + + /** + * 获取线程名 + */ + const std::string& getThreadName() const; + +private: + /** + * 本对象只允许在EventPollerPool中构造 + */ + EventPoller(std::string name); + + /** + * 执行事件轮询 + * @param blocked 是否用执行该接口的线程执行轮询 + * @param ref_self 是记录本对象到thread local变量 + */ + void runLoop(bool blocked, bool ref_self); + + /** + * 内部管道事件,用于唤醒轮询线程用 + */ + void onPipeEvent(); + + /** + * 切换线程并执行任务 + * @param task + * @param may_sync + * @param first + * @return 可取消的任务本体,如果已经同步执行,则返回nullptr + */ + Task::Ptr async_l(TaskIn task, bool may_sync = true, bool first = false); + + /** + * 结束事件轮询 + * 需要指出的是,一旦结束就不能再次恢复轮询线程 + */ + void shutdown(); + + /** + * 刷新延时任务 + */ + uint64_t flushDelayTask(uint64_t now); + + /** + * 获取select或epoll休眠时间 + */ + uint64_t getMinDelay(); + + /** + * 添加管道监听事件 + */ + void addEventPipe(); + +private: + class ExitException : public std::exception {}; + +private: + //标记loop线程是否退出 + bool _exit_flag; + //线程名 + std::string _name; + //当前线程下,所有socket共享的读缓存 + std::weak_ptr _shared_buffer; + //执行事件循环的线程 + std::thread *_loop_thread = nullptr; + //通知事件循环的线程已启动 + semaphore _sem_run_started; + + //内部事件管道 + PipeWrap _pipe; + //从其他线程切换过来的任务 + std::mutex _mtx_task; + List _list_task; + + //保持日志可用 + Logger::Ptr _logger; + +#if defined(HAS_EPOLL) + //epoll相关 + int _epoll_fd = -1; + unordered_map > _event_map; +#else + //select相关 + struct Poll_Record { + using Ptr = std::shared_ptr; + int fd; + int event; + int attach; + PollEventCB call_back; + }; + unordered_map _event_map; +#endif //HAS_EPOLL + unordered_map _event_cache_expired_map; + + //定时器相关 + std::multimap _delay_task_map; +}; + +class EventPollerPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + static const std::string kOnStarted; + #define EventPollerPoolOnStartedArgs EventPollerPool &pool, size_t &size + + ~EventPollerPool() = default; + + /** + * 获取单例 + * @return + */ + static EventPollerPool &Instance(); + + /** + * 设置EventPoller个数,在EventPollerPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @param prefer_current_thread 是否优先获取当前线程 + */ + EventPoller::Ptr getPoller(bool prefer_current_thread = true); + + /** + * 设置 getPoller() 是否优先返回当前线程 + * 在批量创建Socket对象时,如果优先返回当前线程, + * 那么将导致负载不够均衡,所以可以暂时关闭然后再开启 + * @param flag 是否优先返回当前线程 + */ + void preferCurrentThread(bool flag = true); + +private: + EventPollerPool(); + +private: + bool _prefer_current_thread = true; +}; + +} // namespace toolkit +#endif /* EventPoller_h */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Poller/Pipe.h b/webrtc_player/android/zlm/src/main/cpp/include/Poller/Pipe.h new file mode 100644 index 00000000..801ae318 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Poller/Pipe.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Pipe_h +#define Pipe_h + +#include +#include "PipeWrap.h" +#include "EventPoller.h" + +namespace toolkit { + +class Pipe { +public: + using onRead = std::function; + + Pipe(const onRead &cb = nullptr, const EventPoller::Ptr &poller = nullptr); + ~Pipe(); + + void send(const char *send, int size = 0); + +private: + std::shared_ptr _pipe; + EventPoller::Ptr _poller; +}; + +} // namespace toolkit +#endif /* Pipe_h */ \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Poller/PipeWrap.h b/webrtc_player/android/zlm/src/main/cpp/include/Poller/PipeWrap.h new file mode 100644 index 00000000..e457df60 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Poller/PipeWrap.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef PipeWarp_h +#define PipeWarp_h + +namespace toolkit { + +class PipeWrap { +public: + PipeWrap(); + ~PipeWrap(); + int write(const void *buf, int n); + int read(void *buf, int n); + int readFD() const { return _pipe_fd[0]; } + int writeFD() const { return _pipe_fd[1]; } + void reOpen(); + +private: + void clearFD(); + +private: + int _pipe_fd[2] = { -1, -1 }; +}; + +} /* namespace toolkit */ +#endif // !PipeWarp_h + diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Poller/SelectWrap.h b/webrtc_player/android/zlm/src/main/cpp/include/Poller/SelectWrap.h new file mode 100644 index 00000000..c68b35a9 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Poller/SelectWrap.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_POLLER_SELECTWRAP_H_ +#define SRC_POLLER_SELECTWRAP_H_ + +#include "Util/util.h" + +namespace toolkit { + +class FdSet { +public: + FdSet(); + ~FdSet(); + void fdZero(); + void fdSet(int fd); + void fdClr(int fd); + bool isSet(int fd); + void *_ptr; +}; + +int zl_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv); + +} /* namespace toolkit */ +#endif /* SRC_POLLER_SELECTWRAP_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Poller/Timer.h b/webrtc_player/android/zlm/src/main/cpp/include/Poller/Timer.h new file mode 100644 index 00000000..99ed4190 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Poller/Timer.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef Timer_h +#define Timer_h + +#include +#include "EventPoller.h" + +namespace toolkit { + +class Timer { +public: + using Ptr = std::shared_ptr; + + /** + * 构造定时器 + * @param second 定时器重复秒数 + * @param cb 定时器任务,返回true表示重复下次任务,否则不重复,如果任务中抛异常,则默认重复下次任务 + * @param poller EventPoller对象,可以为nullptr + */ + Timer(float second, const std::function &cb, const EventPoller::Ptr &poller); + ~Timer(); + +private: + std::weak_ptr _tag; + //定时器保持EventPoller的强引用 + EventPoller::Ptr _poller; +}; + +} // namespace toolkit +#endif /* Timer_h */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Pusher/MediaPusher.h b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/MediaPusher.h new file mode 100644 index 00000000..899f7cf2 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/MediaPusher.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_PUSHER_MEDIAPUSHER_H_ +#define SRC_PUSHER_MEDIAPUSHER_H_ + +#include +#include +#include "PusherBase.h" + +namespace mediakit { + +class MediaPusher : public PusherImp { +public: + using Ptr = std::shared_ptr; + + MediaPusher(const std::string &schema, + const std::string &vhost, + const std::string &app, + const std::string &stream, + const toolkit::EventPoller::Ptr &poller = nullptr); + + MediaPusher(const MediaSource::Ptr &src, + const toolkit::EventPoller::Ptr &poller = nullptr); + + void publish(const std::string &url) override; + toolkit::EventPoller::Ptr getPoller(); + void setOnCreateSocket(toolkit::Socket::onCreateSocket cb); + +private: + std::weak_ptr _src; + toolkit::EventPoller::Ptr _poller; + toolkit::Socket::onCreateSocket _on_create_socket; +}; + +} /* namespace mediakit */ + +#endif /* SRC_PUSHER_MEDIAPUSHER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherBase.h b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherBase.h new file mode 100644 index 00000000..fa5b879b --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherBase.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_PUSHER_PUSHERBASE_H_ +#define SRC_PUSHER_PUSHERBASE_H_ + +#include +#include +#include +#include +#include "Network/Socket.h" +#include "Util/mini.h" +#include "Common/MediaSource.h" + +namespace mediakit { + +class PusherBase : public toolkit::mINI { +public: + using Ptr = std::shared_ptr; + using Event = std::function; + + static Ptr createPusher(const toolkit::EventPoller::Ptr &poller, + const MediaSource::Ptr &src, + const std::string &strUrl); + + PusherBase(); + virtual ~PusherBase() = default; + + /** + * 开始推流 + * @param strUrl 视频url,支持rtsp/rtmp + */ + virtual void publish(const std::string &strUrl) {}; + + /** + * 中断推流 + */ + virtual void teardown() {}; + + /** + * 摄像推流结果回调 + */ + virtual void setOnPublished(const Event &cb) = 0; + + /** + * 设置断开回调 + */ + virtual void setOnShutdown(const Event &cb) = 0; + +protected: + virtual void onShutdown(const toolkit::SockException &ex) = 0; + virtual void onPublishResult(const toolkit::SockException &ex) = 0; +}; + +template +class PusherImp : public Parent { +public: + using Ptr = std::shared_ptr; + + template + PusherImp(ArgsType &&...args) : Parent(std::forward(args)...) {} + + /** + * 开始推流 + * @param url 推流url,支持rtsp/rtmp + */ + void publish(const std::string &url) override { + return _delegate ? _delegate->publish(url) : Parent::publish(url); + } + + /** + * 中断推流 + */ + void teardown() override { + return _delegate ? _delegate->teardown() : Parent::teardown(); + } + + std::shared_ptr getSockInfo() const { + return std::dynamic_pointer_cast(_delegate); + } + + /** + * 摄像推流结果回调 + */ + void setOnPublished(const PusherBase::Event &cb) override { + if (_delegate) { + _delegate->setOnPublished(cb); + } + _on_publish = cb; + } + + /** + * 设置断开回调 + */ + void setOnShutdown(const PusherBase::Event &cb) override { + if (_delegate) { + _delegate->setOnShutdown(cb); + } + _on_shutdown = cb; + } + +protected: + void onShutdown(const toolkit::SockException &ex) override { + if (_on_shutdown) { + _on_shutdown(ex); + _on_shutdown = nullptr; + } + } + + void onPublishResult(const toolkit::SockException &ex) override { + if (_on_publish) { + _on_publish(ex); + _on_publish = nullptr; + } + } + +protected: + PusherBase::Event _on_shutdown; + PusherBase::Event _on_publish; + std::shared_ptr _delegate; +}; + +} /* namespace mediakit */ +#endif /* SRC_PUSHER_PUSHERBASE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherProxy.h b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherProxy.h new file mode 100644 index 00000000..8a61488c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Pusher/PusherProxy.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_DEVICE_PUSHERPROXY_H +#define SRC_DEVICE_PUSHERPROXY_H + +#include "Pusher/MediaPusher.h" +#include "Util/TimeTicker.h" + +namespace mediakit { + +class PusherProxy + : public MediaPusher + , public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + // 如果retry_count<0,则一直重试播放;否则重试retry_count次数 + // 默认一直重试,创建此对象时候,需要外部保证MediaSource存在 + PusherProxy(const MediaSource::Ptr &src, int retry_count = -1, const toolkit::EventPoller::Ptr &poller = nullptr); + ~PusherProxy() override; + + /** + * 设置push结果回调,只触发一次;在publish执行之前有效 + * @param cb 回调对象 + */ + void setPushCallbackOnce(const std::function &cb); + + /** + * 设置主动关闭回调 + * @param cb 回调对象 + */ + void setOnClose(const std::function &cb); + + /** + * 开始拉流播放 + * @param dstUrl 目标推流地址 + */ + void publish(const std::string &dstUrl) override; + + int getStatus(); + uint64_t getLiveSecs(); + uint64_t getRePublishCount(); + +private: + // 重推逻辑函数 + void rePublish(const std::string &dstUrl, int iFailedCnt); + +private: + int _retry_count; + toolkit::Timer::Ptr _timer; + toolkit::Ticker _live_ticker; + // 0 表示正常 1 表示正在尝试推流 + std::atomic _live_status; + std::atomic _live_secs; + std::atomic _republish_count; + std::weak_ptr _weak_src; + std::function _on_close; + std::function _on_publish; +}; + +} /* namespace mediakit */ + +#endif // SRC_DEVICE_PUSHERPROXY_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMaker.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMaker.h new file mode 100644 index 00000000..a95e2296 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMaker.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HLSMAKER_H +#define HLSMAKER_H + +#include +#include +#include +#include + +namespace mediakit { + +class HlsMaker { +public: + /** + * @param is_fmp4 使用fmp4还是mpegts + * @param seg_duration 切片文件长度 + * @param seg_number 切片个数 + * @param seg_keep 是否保留切片文件 + */ + HlsMaker(bool is_fmp4 = false, float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false); + virtual ~HlsMaker() = default; + + /** + * 写入ts数据 + * @param data 数据 + * @param len 数据长度 + * @param timestamp 毫秒时间戳 + * @param is_idr_fast_packet 是否为关键帧第一个包 + */ + void inputData(const char *data, size_t len, uint64_t timestamp, bool is_idr_fast_packet); + + /** + * 输入fmp4 init segment + * @param data 数据 + * @param len 数据长度 + */ + void inputInitSegment(const char *data, size_t len); + + /** + * 是否为直播 + */ + bool isLive() const; + + /** + * 是否保留切片文件 + */ + bool isKeep() const; + + /** + * 是否采用fmp4切片还是mpegts + */ + bool isFmp4() const; + + /** + * 清空记录 + */ + void clear(); + +protected: + /** + * 创建ts切片文件回调 + * @param index + * @return + */ + virtual std::string onOpenSegment(uint64_t index) = 0; + + /** + * 删除ts切片文件回调 + * @param index + */ + virtual void onDelSegment(uint64_t index) = 0; + + /** + * 写init.mp4切片文件回调 + * @param data + * @param len + */ + virtual void onWriteInitSegment(const char *data, size_t len) = 0; + + /** + * 写ts切片文件回调 + * @param data + * @param len + */ + virtual void onWriteSegment(const char *data, size_t len) = 0; + + /** + * 写m3u8文件回调 + */ + virtual void onWriteHls(const std::string &data, bool include_delay) = 0; + + /** + * 上一个 ts 切片写入完成, 可在这里进行通知处理 + * @param duration_ms 上一个 ts 切片的时长, 单位为毫秒 + */ + virtual void onFlushLastSegment(uint64_t duration_ms) {}; + + /** + * 关闭上个ts切片并且写入m3u8索引 + * @param eof HLS直播是否已结束 + */ + void flushLastSegment(bool eof); + +private: + /** + * 生成m3u8文件 + * @param eof true代表点播 + */ + void makeIndexFile(bool include_delay, bool eof = false); + + /** + * 删除旧的ts切片 + */ + void delOldSegment(); + + /** + * 添加新的ts切片 + * @param timestamp + */ + void addNewSegment(uint64_t timestamp); + +private: + bool _is_fmp4 = false; + float _seg_duration = 0; + uint32_t _seg_number = 0; + bool _seg_keep = false; + uint64_t _last_timestamp = 0; + uint64_t _last_seg_timestamp = 0; + uint64_t _file_index = 0; + std::string _last_file_name; + std::deque > _seg_dur_list; +}; + +}//namespace mediakit +#endif //HLSMAKER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMakerImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMakerImp.h new file mode 100644 index 00000000..1485e8d3 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMakerImp.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HLSMAKERIMP_H +#define HLSMAKERIMP_H + +#include +#include +#include +#include "HlsMaker.h" +#include "HlsMediaSource.h" + +namespace mediakit { + +class HlsMakerImp : public HlsMaker { +public: + HlsMakerImp(bool is_fmp4, const std::string &m3u8_file, const std::string ¶ms, uint32_t bufSize = 64 * 1024, + float seg_duration = 5, uint32_t seg_number = 3, bool seg_keep = false); + ~HlsMakerImp() override; + + /** + * 设置媒体信息 + */ + void setMediaSource(const MediaTuple& tuple); + + /** + * 获取MediaSource + * @return + */ + HlsMediaSource::Ptr getMediaSource() const; + + /** + * 清空缓存 + */ + void clearCache(); + +protected: + std::string onOpenSegment(uint64_t index) override ; + void onDelSegment(uint64_t index) override; + void onWriteInitSegment(const char *data, size_t len) override; + void onWriteSegment(const char *data, size_t len) override; + void onWriteHls(const std::string &data, bool include_delay) override; + void onFlushLastSegment(uint64_t duration_ms) override; + +private: + std::shared_ptr makeFile(const std::string &file,bool setbuf = false); + void clearCache(bool immediately, bool eof); + +private: + int _buf_size; + std::string _params; + std::string _path_hls; + std::string _path_hls_delay; + std::string _path_init; + std::string _path_prefix; + RecordInfo _info; + std::shared_ptr _file; + std::shared_ptr _file_buf; + HlsMediaSource::Ptr _media_src; + toolkit::EventPoller::Ptr _poller; + std::map _segment_file_paths; +}; + +}//namespace mediakit +#endif //HLSMAKERIMP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMediaSource.h new file mode 100644 index 00000000..f9c4c56d --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsMediaSource.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_HLSMEDIASOURCE_H +#define ZLMEDIAKIT_HLSMEDIASOURCE_H + +#include "Common/MediaSource.h" +#include "Util/TimeTicker.h" +#include "Util/RingBuffer.h" +#include + +namespace mediakit { + +class HlsMediaSource : public MediaSource { +public: + friend class HlsCookieData; + + using RingType = toolkit::RingBuffer; + using Ptr = std::shared_ptr; + + HlsMediaSource(const std::string &schema, const MediaTuple &tuple) : MediaSource(schema, tuple) {} + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { return _ring; } + + /** + * 获取播放器个数 + */ + int readerCount() override { return _ring ? _ring->readerCount() : 0; } + + /** + * 设置或清空m3u8索引文件内容 + */ + void setIndexFile(std::string index_file); + + /** + * 异步获取m3u8文件 + */ + void getIndexFile(std::function cb); + + /** + * 同步获取m3u8文件 + */ + std::string getIndexFile() const { + std::lock_guard lck(_mtx_index); + return _index_file; + } + + void onSegmentSize(size_t bytes) { _speed[TrackVideo] += bytes; } + + void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) override { + _ring->getInfoList(cb, on_change); + } + +private: + RingType::Ptr _ring; + std::string _index_file; + mutable std::mutex _mtx_index; + toolkit::List> _list_cb; +}; + +class HlsCookieData { +public: + using Ptr = std::shared_ptr; + + HlsCookieData(const MediaInfo &info, const std::shared_ptr &sock_info); + ~HlsCookieData(); + + void addByteUsage(size_t bytes); + void setMediaSource(const HlsMediaSource::Ptr &src); + HlsMediaSource::Ptr getMediaSource() const; + +private: + void addReaderCount(); + +private: + std::atomic _bytes { 0 }; + MediaInfo _info; + std::shared_ptr _added; + toolkit::Ticker _ticker; + std::weak_ptr _src; + std::shared_ptr _sock_info; + HlsMediaSource::RingType::RingReader::Ptr _ring_reader; +}; + +} // namespace mediakit +#endif // ZLMEDIAKIT_HLSMEDIASOURCE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsRecorder.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsRecorder.h new file mode 100644 index 00000000..26894ccf --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/HlsRecorder.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HLSRECORDER_H +#define HLSRECORDER_H + +#include "HlsMakerImp.h" +#include "MPEG.h" +#include "MP4Muxer.h" +#include "Common/config.h" + +namespace mediakit { + +template +class HlsRecorderBase : public MediaSourceEventInterceptor, public Muxer, public std::enable_shared_from_this > { +public: + HlsRecorderBase(bool is_fmp4, const std::string &m3u8_file, const std::string ¶ms, const ProtocolOption &option) { + GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum); + GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep); + GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize); + GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration); + + _option = option; + _hls = std::make_shared(is_fmp4, m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep); + // 清空上次的残余文件 + _hls->clearCache(); + } + + void setMediaSource(const MediaTuple& tuple) { + _hls->setMediaSource(tuple); + } + + void setListener(const std::weak_ptr &listener) { + setDelegate(listener); + _hls->getMediaSource()->setListener(this->shared_from_this()); + } + + int readerCount() { return _hls->getMediaSource()->readerCount(); } + + void onReaderChanged(MediaSource &sender, int size) override { + // hls保留切片个数为0时代表为hls录制(不删除切片),那么不管有无观看者都一直生成hls + _enabled = _option.hls_demand ? (_hls->isLive() ? size : true) : true; + if (!size && _hls->isLive() && _option.hls_demand) { + // hls直播时,如果无人观看就删除视频缓存,目的是为了防止视频跳跃 + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache && _option.hls_demand) { + _clear_cache = false; + //清空旧的m3u8索引文件于ts切片 + _hls->clearCache(); + _hls->getMediaSource()->setIndexFile(""); + } + if (_enabled || !_option.hls_demand) { + return Muxer::inputFrame(frame); + } + return false; + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _option.hls_demand ? (_clear_cache ? true : _enabled) : true; + } + +protected: + bool _enabled = true; + bool _clear_cache = false; + ProtocolOption _option; + std::shared_ptr _hls; +}; + +class HlsRecorder final : public HlsRecorderBase { +public: + using Ptr = std::shared_ptr; + template + HlsRecorder(ARGS && ...args) : HlsRecorderBase(false, std::forward(args)...) {} + ~HlsRecorder() override { this->flush(); } + +private: + void onWrite(std::shared_ptr buffer, uint64_t timestamp, bool key_pos) override { + if (!buffer) { + // reset tracks + _hls->inputData(nullptr, 0, timestamp, key_pos); + } else { + _hls->inputData(buffer->data(), buffer->size(), timestamp, key_pos); + } + } +}; + +class HlsFMP4Recorder final : public HlsRecorderBase { +public: + using Ptr = std::shared_ptr; + template + HlsFMP4Recorder(ARGS && ...args) : HlsRecorderBase(true, std::forward(args)...) {} + ~HlsFMP4Recorder() override { this->flush(); } + + void addTrackCompleted() override { + HlsRecorderBase::addTrackCompleted(); + auto data = getInitSegment(); + _hls->inputInitSegment(data.data(), data.size()); + } + +private: + void onSegmentData(std::string buffer, uint64_t timestamp, bool key_pos) override { + if (buffer.empty()) { + // reset tracks + _hls->inputData(nullptr, 0, timestamp, key_pos); + } else { + _hls->inputData((char *)buffer.data(), buffer.size(), timestamp, key_pos); + } + } +}; + +}//namespace mediakit +#endif //HLSRECORDER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4.h new file mode 100644 index 00000000..4b150170 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MP4_H +#define ZLMEDIAKIT_MP4_H + +#if defined(ENABLE_MP4) + +#include +#include +#include "mp4-writer.h" +#include "mov-writer.h" +#include "mov-reader.h" +#include "mpeg4-hevc.h" +#include "mpeg4-avc.h" +#include "mpeg4-aac.h" +#include "mov-buffer.h" +#include "mov-format.h" + +namespace mediakit { + +//mp4文件IO的抽象接口类 +class MP4FileIO : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + using Writer = std::shared_ptr; + using Reader = std::shared_ptr; + + virtual ~MP4FileIO() = default; + + /** + * 创建mp4复用器 + * @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT + * @param is_fmp4 是否为fmp4还是普通mp4 + * @return mp4复用器 + */ + virtual Writer createWriter(int flags, bool is_fmp4 = false); + + /** + * 创建mp4解复用器 + * @return mp4解复用器 + */ + virtual Reader createReader(); + + /** + * 获取文件读写位置 + */ + virtual uint64_t onTell() = 0; + + /** + * seek至文件某处 + * @param offset 文件偏移量 + * @return 是否成功(0成功) + */ + virtual int onSeek(uint64_t offset) = 0; + + /** + * 从文件读取一定数据 + * @param data 数据存放指针 + * @param bytes 指针长度 + * @return 是否成功(0成功) + */ + virtual int onRead(void *data, size_t bytes) = 0; + + /** + * 写入文件一定数据 + * @param data 数据指针 + * @param bytes 数据长度 + * @return 是否成功(0成功) + */ + virtual int onWrite(const void *data, size_t bytes) = 0; +}; + +//磁盘MP4文件类 +class MP4FileDisk : public MP4FileIO { +public: + using Ptr = std::shared_ptr; + + /** + * 打开磁盘文件 + * @param file 文件路径 + * @param mode fopen的方式 + */ + void openFile(const char *file, const char *mode); + + /** + * 关闭磁盘文件 + */ + void closeFile(); + +protected: + uint64_t onTell() override; + int onSeek(uint64_t offset) override; + int onRead(void *data, size_t bytes) override; + int onWrite(const void *data, size_t bytes) override; + +private: + std::shared_ptr _file; +}; + +class MP4FileMemory : public MP4FileIO{ +public: + using Ptr = std::shared_ptr; + + /** + * 获取文件大小 + */ + size_t fileSize() const; + + /** + * 获取并清空文件缓存 + */ + std::string getAndClearMemory(); + +protected: + uint64_t onTell() override; + int onSeek(uint64_t offset) override; + int onRead(void *data, size_t bytes) override; + int onWrite(const void *data, size_t bytes) override; + +private: + uint64_t _offset = 0; + std::string _memory; +}; + +}//namespace mediakit +#endif //defined(ENABLE_MP4) +#endif //ZLMEDIAKIT_MP4_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Demuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Demuxer.h new file mode 100644 index 00000000..889f91fc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Demuxer.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MP4DEMUXER_H +#define ZLMEDIAKIT_MP4DEMUXER_H +#ifdef ENABLE_MP4 +#include "MP4.h" +#include "Extension/Track.h" +#include "Util/ResourcePool.h" +namespace mediakit { + +class MP4Demuxer : public TrackSource { +public: + using Ptr = std::shared_ptr; + + ~MP4Demuxer() override; + + /** + * 打开文件 + * @param file mp4文件路径 + */ + void openMP4(const std::string &file); + + /** + * @brief 关闭 mp4 文件 + */ + void closeMP4(); + + /** + * 移动时间轴至某处 + * @param stamp_ms 预期的时间轴位置,单位毫秒 + * @return 时间轴位置 + */ + int64_t seekTo(int64_t stamp_ms); + + /** + * 读取一帧数据 + * @param keyFrame 是否为关键帧 + * @param eof 是否文件读取完毕 + * @return 帧数据,可能为空 + */ + Frame::Ptr readFrame(bool &keyFrame, bool &eof); + + /** + * 获取所有Track信息 + * @param trackReady 是否要求track为就绪状态 + * @return 所有Track + */ + std::vector getTracks(bool trackReady) const override; + + /** + * 获取文件长度 + * @return 文件长度,单位毫秒 + */ + uint64_t getDurationMS() const; + +private: + int getAllTracks(); + void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void *extra, size_t bytes); + void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void *extra, size_t bytes); + Frame::Ptr makeFrame(uint32_t track_id, const toolkit::Buffer::Ptr &buf, int64_t pts, int64_t dts); + +private: + MP4FileDisk::Ptr _mp4_file; + MP4FileDisk::Reader _mov_reader; + uint64_t _duration_ms = 0; + std::unordered_map _tracks; + toolkit::ResourcePool _buffer_pool; +}; + + +}//namespace mediakit +#endif//ENABLE_MP4 +#endif //ZLMEDIAKIT_MP4DEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Muxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Muxer.h new file mode 100644 index 00000000..4f764c3f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Muxer.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MP4MUXER_H +#define ZLMEDIAKIT_MP4MUXER_H + +#if defined(ENABLE_MP4) + +#include "Common/MediaSink.h" +#include "Common/Stamp.h" +#include "MP4.h" + +namespace mediakit { + +class MP4MuxerInterface : public MediaSinkInterface { +public: + + /** + * 添加已经ready状态的track + */ + bool addTrack(const Track::Ptr &track) override; + + /** + * 输入帧 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 重置所有track + */ + void resetTracks() override; + + /** + * 刷新输出所有frame缓存 + */ + void flush() override; + + /** + * 是否包含视频 + */ + bool haveVideo() const; + + /** + * 保存fmp4分片 + */ + void saveSegment(); + + /** + * 创建新切片 + */ + void initSegment(); + + /** + * 获取mp4时长,单位毫秒 + */ + uint64_t getDuration() const; + +protected: + virtual MP4FileIO::Writer createWriter() = 0; + +private: + void stampSync(); + +private: + bool _started = false; + bool _have_video = false; + MP4FileIO::Writer _mov_writter; + + class FrameMergerImp : public FrameMerger { + public: + FrameMergerImp() : FrameMerger(FrameMerger::mp4_nal_size) {} + }; + + struct MP4Track { + int track_id = -1; + Stamp stamp; + FrameMergerImp merger; + }; + std::unordered_map _tracks; +}; + +class MP4Muxer : public MP4MuxerInterface{ +public: + using Ptr = std::shared_ptr; + ~MP4Muxer() override; + /** + * 重置所有track + */ + void resetTracks() override; + + /** + * 打开mp4 + * @param file 文件完整路径 + */ + void openMP4(const std::string &file); + + /** + * 手动关闭文件(对象析构时会自动关闭) + */ + void closeMP4(); + +protected: + MP4FileIO::Writer createWriter() override; + +private: + std::string _file_name; + MP4FileDisk::Ptr _mp4_file; +}; + +class MP4MuxerMemory : public MP4MuxerInterface{ +public: + MP4MuxerMemory(); + + /** + * 重置所有track + */ + void resetTracks() override; + + /** + * 输入帧 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 获取fmp4 init segment + */ + const std::string &getInitSegment(); + +protected: + /** + * 输出fmp4切片回调函数 + * @param std::string 切片内容 + * @param stamp 切片末尾时间戳 + * @param key_frame 是否有关键帧 + */ + virtual void onSegmentData(std::string string, uint64_t stamp, bool key_frame) = 0; + +protected: + MP4FileIO::Writer createWriter() override; + +private: + bool _key_frame = false; + uint64_t _last_dst = 0; + std::string _init_segment; + MP4FileMemory::Ptr _memory_file; +}; + +} // namespace mediakit + +#else + +#include "Common/MediaSink.h" + +namespace mediakit { + +class MP4MuxerMemory : public MediaSinkInterface { +public: + bool addTrack(const Track::Ptr & track) override { return false; } + bool inputFrame(const Frame::Ptr &frame) override { return false; } + const std::string &getInitSegment() { static std::string kNull; return kNull; }; + +protected: + /** + * 输出fmp4切片回调函数 + * @param std::string 切片内容 + * @param stamp 切片末尾时间戳 + * @param key_frame 是否有关键帧 + */ + virtual void onSegmentData(std::string string, uint64_t stamp, bool key_frame) = 0; +}; + +} // namespace mediakit + +#endif //defined(ENABLE_MP4) +#endif //ZLMEDIAKIT_MP4MUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Reader.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Reader.h new file mode 100644 index 00000000..c97c4862 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Reader.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_MEDIAFILE_MEDIAREADER_H_ +#define SRC_MEDIAFILE_MEDIAREADER_H_ +#ifdef ENABLE_MP4 + +#include "MP4Demuxer.h" +#include "Common/MultiMediaSourceMuxer.h" + +namespace mediakit { + +class MP4Reader : public std::enable_shared_from_this, public MediaSourceEvent { +public: + using Ptr = std::shared_ptr; + + /** + * 点播一个mp4文件,使之转换成MediaSource流媒体 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id,置空时,只解复用mp4,但是不生成MediaSource + * @param file_path 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 + */ + MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, + const std::string &file_path = "", toolkit::EventPoller::Ptr poller = nullptr); + + MP4Reader(const std::string &vhost, const std::string &app, const std::string &stream_id, + const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller = nullptr); + + /** + * 开始解复用MP4文件 + * @param sample_ms 每次读取文件数据量,单位毫秒,置0时采用配置文件配置 + * @param ref_self 是否让定时器引用此对象本身,如果无其他对象引用本身,在不循环读文件时,读取文件结束后本对象将自动销毁 + * @param file_repeat 是否循环读取文件,如果配置文件设置为循环读文件,此参数无效 + */ + void startReadMP4(uint64_t sample_ms = 0, bool ref_self = true, bool file_repeat = false); + + /** + * 停止解复用MP4定时器 + */ + void stopReadMP4(); + + /** + * 获取mp4解复用器 + */ + const MP4Demuxer::Ptr& getDemuxer() const; + +private: + //MediaSourceEvent override + bool seekTo(MediaSource &sender,uint32_t stamp) override; + bool pause(MediaSource &sender, bool pause) override; + bool speed(MediaSource &sender, float speed) override; + + bool close(MediaSource &sender) override; + MediaOriginType getOriginType(MediaSource &sender) const override; + std::string getOriginUrl(MediaSource &sender) const override; + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + + bool readSample(); + bool readNextSample(); + uint32_t getCurrentStamp(); + void setCurrentStamp(uint32_t stamp); + bool seekTo(uint32_t stamp_seek); + + void setup(const std::string &vhost, const std::string &app, const std::string &stream_id, const std::string &file_path, const ProtocolOption &option, toolkit::EventPoller::Ptr poller); + +private: + bool _file_repeat = false; + bool _have_video = false; + bool _paused = false; + float _speed = 1.0; + uint32_t _last_dts = 0; + uint32_t _seek_to = 0; + std::string _file_path; + std::recursive_mutex _mtx; + toolkit::Ticker _seek_ticker; + toolkit::Timer::Ptr _timer; + MP4Demuxer::Ptr _demuxer; + MultiMediaSourceMuxer::Ptr _muxer; + toolkit::EventPoller::Ptr _poller; +}; + +} /* namespace mediakit */ +#endif //ENABLE_MP4 +#endif /* SRC_MEDIAFILE_MEDIAREADER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Recorder.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Recorder.h new file mode 100644 index 00000000..e1258029 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MP4Recorder.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MP4MAKER_H_ +#define MP4MAKER_H_ + +#include +#include +#include "Common/MediaSink.h" +#include "Record/Recorder.h" +#include "MP4Muxer.h" + +namespace mediakit { + +#ifdef ENABLE_MP4 +class MP4Muxer; + +class MP4Recorder final : public MediaSinkInterface { +public: + using Ptr = std::shared_ptr; + + MP4Recorder(const MediaTuple &tuple, const std::string &path, size_t max_second); + ~MP4Recorder() override; + + /** + * 重置所有Track + */ + void resetTracks() override; + + /** + * 输入frame + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 刷新输出所有frame缓存 + */ + void flush() override; + + /** + * 添加ready状态的track + */ + bool addTrack(const Track::Ptr & track) override; + +private: + void createFile(); + void closeFile(); + void asyncClose(); + +private: + bool _have_video = false; + size_t _max_second; + uint64_t _last_dts = 0; + uint64_t _file_index = 0; + std::string _folder_path; + std::string _full_path; + std::string _full_path_tmp; + RecordInfo _info; + MP4Muxer::Ptr _muxer; + std::list _tracks; +}; + +#endif ///ENABLE_MP4 + +} /* namespace mediakit */ + +#endif /* MP4MAKER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/MPEG.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/MPEG.h new file mode 100644 index 00000000..dc07af4a --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/MPEG.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_MPEG_H +#define ZLMEDIAKIT_MPEG_H + +#if defined(ENABLE_HLS) || defined(ENABLE_RTPPROXY) + +#include +#include +#include +#include "Extension/Frame.h" +#include "Extension/Track.h" +#include "Common/MediaSink.h" +#include "Util/ResourcePool.h" +namespace mediakit { + +//该类用于产生MPEG-TS/MPEG-PS +class MpegMuxer : public MediaSinkInterface { +public: + MpegMuxer(bool is_ps = false); + ~MpegMuxer() override; + + /** + * 添加音视频轨道 + */ + bool addTrack(const Track::Ptr &track) override; + + /** + * 重置音视频轨道 + */ + void resetTracks() override; + + /** + * 输入帧数据 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 刷新输出所有frame缓存 + */ + void flush() override; + +protected: + /** + * 输出ts/ps数据回调 + * @param buffer ts/ps数据包 + * @param timestamp 时间戳,单位毫秒 + * @param key_pos 是否为关键帧的第一个ts/ps包,用于确保ts切片第一帧为关键帧 + */ + virtual void onWrite(std::shared_ptr buffer, uint64_t timestamp, bool key_pos) = 0; + +private: + void createContext(); + void releaseContext(); + void onWrite_l(const void *packet, size_t bytes); + void flushCache(); + +private: + bool _is_ps = false; + bool _have_video = false; + bool _key_pos = false; + uint32_t _max_cache_size = 0; + uint64_t _timestamp = 0; + struct mpeg_muxer_t *_context = nullptr; + + class FrameMergerImp : public FrameMerger { + public: + FrameMergerImp() : FrameMerger(FrameMerger::h264_prefix) {} + }; + + struct MP4Track { + int track_id = -1; + FrameMergerImp merger; + }; + std::unordered_map _tracks; + toolkit::BufferRaw::Ptr _current_buffer; + toolkit::ResourcePool _buffer_pool; +}; + +}//mediakit + +#else + +#include "Common/MediaSink.h" + +namespace mediakit { + +class MpegMuxer : public MediaSinkInterface { +public: + MpegMuxer(bool is_ps = false) {} + bool addTrack(const Track::Ptr &track) override { return false; } + void resetTracks() override {} + bool inputFrame(const Frame::Ptr &frame) override { return false; } + +protected: + virtual void onWrite(std::shared_ptr buffer, uint64_t timestamp, bool key_pos) = 0; +}; + +}//namespace mediakit + +#endif + +#endif //ZLMEDIAKIT_MPEG_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Record/Recorder.h b/webrtc_player/android/zlm/src/main/cpp/include/Record/Recorder.h new file mode 100644 index 00000000..ea924618 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Record/Recorder.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_MEDIAFILE_RECORDER_H_ +#define SRC_MEDIAFILE_RECORDER_H_ + +#include +#include + +namespace mediakit { +class MediaSinkInterface; +class ProtocolOption; + +struct MediaTuple { + std::string vhost; + std::string app; + std::string stream; + std::string params; + std::string shortUrl() const { + return vhost + '/' + app + '/' + stream; + } +}; + +class RecordInfo: public MediaTuple { +public: + time_t start_time; // GMT 标准时间,单位秒 + float time_len; // 录像长度,单位秒 + off_t file_size; // 文件大小,单位 BYTE + std::string file_path; // 文件路径 + std::string file_name; // 文件名称 + std::string folder; // 文件夹路径 + std::string url; // 播放路径 +}; + +class Recorder{ +public: + typedef enum { + // 录制hls + type_hls = 0, + // 录制MP4 + type_mp4 = 1, + // 录制hls.fmp4 + type_hls_fmp4 = 2, + // fmp4直播 + type_fmp4 = 3, + // ts直播 + type_ts = 4, + } type; + + /** + * 获取录制文件绝对路径 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 + * @return 录制文件绝对路径 + */ + static std::string getRecordPath(type type, const MediaTuple& tuple, const std::string &customized_path = ""); + + /** + * 创建录制器对象 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @param customized_path 录像文件保存自定义根目录,为空则采用配置文件设置 + * @param max_second mp4录制最大切片时间,单位秒,置0则采用配置文件配置 + * @return 对象指针,可能为nullptr + */ + static std::shared_ptr createRecorder(type type, const MediaTuple& tuple, const ProtocolOption &option); + +private: + Recorder() = delete; + ~Recorder() = delete; +}; + +} /* namespace mediakit */ +#endif /* SRC_MEDIAFILE_RECORDER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/Rtcp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/Rtcp.h new file mode 100644 index 00000000..53e6340c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/Rtcp.h @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTCP_H +#define ZLMEDIAKIT_RTCP_H + +#include "Common/macros.h" +#include "Network/Buffer.h" +#include "Util/util.h" +#include +#include + +namespace mediakit { + +#pragma pack(push, 1) + +// http://www.networksorcery.com/enp/protocol/rtcp.htm +#define RTCP_PT_MAP(XX) \ + XX(RTCP_FIR, 192) \ + XX(RTCP_NACK, 193) \ + XX(RTCP_SMPTETC, 194) \ + XX(RTCP_IJ, 195) \ + XX(RTCP_SR, 200) \ + XX(RTCP_RR, 201) \ + XX(RTCP_SDES, 202) \ + XX(RTCP_BYE, 203) \ + XX(RTCP_APP, 204) \ + XX(RTCP_RTPFB, 205) \ + XX(RTCP_PSFB, 206) \ + XX(RTCP_XR, 207) \ + XX(RTCP_AVB, 208) \ + XX(RTCP_RSI, 209) \ + XX(RTCP_TOKEN, 210) + +// https://tools.ietf.org/html/rfc3550#section-6.5 +#define SDES_TYPE_MAP(XX) \ + XX(RTCP_SDES_END, 0) \ + XX(RTCP_SDES_CNAME, 1) \ + XX(RTCP_SDES_NAME, 2) \ + XX(RTCP_SDES_EMAIL, 3) \ + XX(RTCP_SDES_PHONE, 4) \ + XX(RTCP_SDES_LOC, 5) \ + XX(RTCP_SDES_TOOL, 6) \ + XX(RTCP_SDES_NOTE, 7) \ + XX(RTCP_SDES_PRIVATE, 8) + +// https://datatracker.ietf.org/doc/rfc4585/?include_text=1 +// 6.3. Payload-Specific Feedback Messages +// +// Payload-Specific FB messages are identified by the value PT=PSFB as +// RTCP message type. +// +// Three payload-specific FB messages are defined so far plus an +// application layer FB message. They are identified by means of the +// FMT parameter as follows: +// +// 0: unassigned +// 1: Picture Loss Indication (PLI) +// 2: Slice Loss Indication (SLI) +// 3: Reference Picture Selection Indication (RPSI) +// 4: FIR https://tools.ietf.org/html/rfc5104#section-4.3.1.1 +// 5: TSTR https://tools.ietf.org/html/rfc5104#section-4.3.2.1 +// 6: TSTN https://tools.ietf.org/html/rfc5104#section-4.3.2.1 +// 7: VBCM https://tools.ietf.org/html/rfc5104#section-4.3.4.1 +// 8-14: unassigned +// 15: REMB / Application layer FB (AFB) message, https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 +// 16-30: unassigned +// 31: reserved for future expansion of the sequence number space +#define PSFB_TYPE_MAP(XX) \ + XX(RTCP_PSFB_PLI, 1) \ + XX(RTCP_PSFB_SLI, 2) \ + XX(RTCP_PSFB_RPSI, 3) \ + XX(RTCP_PSFB_FIR, 4) \ + XX(RTCP_PSFB_TSTR, 5) \ + XX(RTCP_PSFB_TSTN, 6) \ + XX(RTCP_PSFB_VBCM, 7) \ + XX(RTCP_PSFB_REMB, 15) + +// https://tools.ietf.org/html/rfc4585#section-6.2 +// 6.2. Transport Layer Feedback Messages +// +// Transport layer FB messages are identified by the value RTPFB as RTCP +// message type. +// +// A single general purpose transport layer FB message is defined in +// this document: Generic NACK. It is identified by means of the FMT +// parameter as follows: +// +// 0: unassigned +// 1: Generic NACK +// 2: reserved https://tools.ietf.org/html/rfc5104#section-4.2 +// 3: TMMBR https://tools.ietf.org/html/rfc5104#section-4.2.1.1 +// 4: TMMBN https://tools.ietf.org/html/rfc5104#section-4.2.2.1 +// 5-14: unassigned +// 15 transport-cc https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 +// 16-30: unassigned +// 31: reserved for future expansion of the identifier number space +#define RTPFB_TYPE_MAP(XX) \ + XX(RTCP_RTPFB_NACK, 1) \ + XX(RTCP_RTPFB_TMMBR, 3) \ + XX(RTCP_RTPFB_TMMBN, 4) \ + XX(RTCP_RTPFB_TWCC, 15) + +// rtcp类型枚举 +enum class RtcpType : uint8_t { +#define XX(key, value) key = value, + RTCP_PT_MAP(XX) +#undef XX +}; + +// sdes类型枚举 +enum class SdesType : uint8_t { +#define XX(key, value) key = value, + SDES_TYPE_MAP(XX) +#undef XX +}; + +// psfb类型枚举 +enum class PSFBType : uint8_t { +#define XX(key, value) key = value, + PSFB_TYPE_MAP(XX) +#undef XX +}; + +// rtpfb类型枚举 +enum class RTPFBType : uint8_t { +#define XX(key, value) key = value, + RTPFB_TYPE_MAP(XX) +#undef XX +}; + +/** + * RtcpType转描述字符串 + */ +const char *rtcpTypeToStr(RtcpType type); + +/** + * SdesType枚举转描述字符串 + */ +const char *sdesTypeToStr(SdesType type); + +/** + * psfb枚举转描述字符串 + */ +const char *psfbTypeToStr(PSFBType type); + +/** + * rtpfb枚举转描述字符串 + */ +const char *rtpfbTypeToStr(RTPFBType type); + +class RtcpHeader { +public: +#if __BYTE_ORDER == __BIG_ENDIAN + // 版本号,固定为2 + uint32_t version : 2; + // padding,固定为0 + uint32_t padding : 1; + // reception report count + uint32_t report_count : 5; +#else + // reception report count + uint32_t report_count : 5; + // padding,末尾是否有追加填充 + uint32_t padding : 1; + // 版本号,固定为2 + uint32_t version : 2; +#endif + // rtcp类型,RtcpType + uint32_t pt : 8; + +private: + // 长度 + uint32_t length : 16; + +public: + /** + * 解析rtcp并转换网络字节序为主机字节序,返回RtcpHeader派生类列表 + * @param data 数据指针 + * @param size 数据总长度 + * @return rtcp对象列表,无需free + */ + static std::vector loadFromBytes(char *data, size_t size); + + /** + * rtcp包转Buffer对象 + * @param rtcp rtcp包对象智能指针 + * @return Buffer对象 + */ + static toolkit::Buffer::Ptr toBuffer(std::shared_ptr rtcp); + + /** + * 打印rtcp相关字段详情(调用派生类的dumpString函数) + * 内部会判断是什么类型的派生类 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 根据length字段获取rtcp总长度 + */ + size_t getSize() const; + + /** + * 后面追加padding数据长度 + */ + size_t getPaddingSize() const; + + /** + * 设置rtcp length字段 + * @param size rtcp总长度,单位字节 + */ + void setSize(size_t size); + +protected: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpHeader() const; + +private: + /** + * 调用派生类的net2Host函数 + * @param size rtcp字符长度 + */ + void net2Host(size_t size); + +}; + +///////////////////////////////////////////////////////////////////////////// + +// ReportBlock +class ReportItem { +public: + friend class RtcpSR; + friend class RtcpRR; + + uint32_t ssrc; + // Fraction lost + uint32_t fraction : 8; + // Cumulative number of packets lost + uint32_t cumulative : 24; + // Sequence number cycles count + uint16_t seq_cycles; + // Highest sequence number received + uint16_t seq_max; + // Interarrival jitter + uint32_t jitter; + // Last SR timestamp, NTP timestamp,(ntpmsw & 0xFFFF) << 16 | (ntplsw >> 16) & 0xFFFF) + uint32_t last_sr_stamp; + // Delay since last SR timestamp,expressed in units of 1/65536 seconds + uint32_t delay_since_last_sr; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + */ + void net2Host(); +}; + +/* + * 6.4.1 SR: Sender Report RTCP Packet + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=SR=200 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +sender | NTP timestamp, most significant word | +info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, least significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTP timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's packet count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's octet count | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +// sender report +class RtcpSR : public RtcpHeader { +public: + friend class RtcpHeader; + uint32_t ssrc; + // ntp timestamp MSW(in second) + uint32_t ntpmsw; + // ntp timestamp LSW(in picosecond) + uint32_t ntplsw; + // rtp timestamp + uint32_t rtpts; + // sender packet count + uint32_t packet_count; + // sender octet count + uint32_t octet_count; + // 可能有很多个 + ReportItem items; + +public: + /** + * 创建SR包,只赋值了RtcpHeader部分(网络字节序) + * @param item_count ReportItem对象个数 + * @return SR包 + */ + static std::shared_ptr create(size_t item_count); + + /** + * 设置ntpmsw与ntplsw字段为网络字节序 + * @param tv 时间 + */ + void setNtpStamp(struct timeval tv); + void setNtpStamp(uint64_t unix_stamp_ms); + + /** + * 返回ntp时间的字符串 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string getNtpStamp() const; + uint64_t getNtpUnixStampMS() const; + + /** + * 获取ReportItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::vector getItemList(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +}; + +///////////////////////////////////////////////////////////////////////////// + +/* + * 6.4.2 RR: Receiver Report RTCP Packet + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=RR=201 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +// Receiver Report +class RtcpRR : public RtcpHeader { +public: + friend class RtcpHeader; + + uint32_t ssrc; + // 可能有很多个 + ReportItem items; + +public: + /** + * 创建RR包,只赋值了RtcpHeader部分 + * @param item_count ReportItem对象个数 + * @return RR包 + */ + static std::shared_ptr create(size_t item_count); + + /** + * 获取ReportItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::vector getItemList(); + +private: + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); + + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + +}; + +///////////////////////////////////////////////////////////////////////////// + +/* + * 6.5 SDES: Source Description RTCP Packet + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| SC | PT=SDES=202 | length | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_1 | + 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_2 | + 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + +/* + +SDES items 定义 +0 1 2 3 +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| SdesType | length | user and domain name ... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +// Source description Chunk +class SdesChunk { +public: + friend class RtcpSdes; + + uint32_t ssrc; + // SdesType + uint8_t type; + // text长度股,可以为0 + uint8_t txt_len; + // 不定长 + char text[1]; + // 最后以RTCP_SDES_END结尾 + // 只字段为占位字段,不代表真实位置 + uint8_t end; + +public: + /** + * 返回改对象字节长度 + */ + size_t totalBytes() const; + + /** + * 本对象最少长度 + */ + static size_t minSize(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + */ + void net2Host(); +}; + +// Source description +class RtcpSdes : public RtcpHeader { +public: + friend class RtcpHeader; + + // 可能有很多个 + SdesChunk chunks; + +public: + /** + * 创建SDES包,只赋值了RtcpHeader以及SdesChunk对象的length和text部分 + * @param item_text SdesChunk列表,只赋值length和text部分 + * @return SDES包 + */ + static std::shared_ptr create(const std::vector &item_text); + + /** + * 获取SdesChunk对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::vector getChunkList(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +}; + +// https://tools.ietf.org/html/rfc4585#section-6.1 +// 6.1. Common Packet Format for Feedback Messages +// +// All FB messages MUST use a common packet format that is depicted in +// Figure 3: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : +// rtcpfb和psfb的数据结构一致 +class RtcpFB : public RtcpHeader { +public: + friend class RtcpHeader; + uint32_t ssrc; + uint32_t ssrc_media; + +public: + /** + * 创建psfb类型的反馈包 + */ + static std::shared_ptr create(PSFBType fmt, const void *fci = nullptr, size_t fci_len = 0); + + /** + * 创建rtpfb类型的反馈包 + */ + static std::shared_ptr create(RTPFBType fmt, const void *fci = nullptr, size_t fci_len = 0); + + /** + * fci转换成某对象指针 + * @tparam Type 对象类型 + * @return 对象指针 + */ + template + const Type &getFci() const { + auto fci_data = getFciPtr(); + auto fci_len = getFciSize(); + Type *fci = (Type *)fci_data; + fci->check(fci_len); + return *fci; + } + + /** + * 获取fci指针 + */ + const void *getFciPtr() const; + + /** + * 获取fci数据长度 + */ + size_t getFciSize() const; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); + +private: + static std::shared_ptr create_l(RtcpType type, int fmt, const void *fci, size_t fci_len); +}; + +// BYE +/* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| SC | PT=BYE=203 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC/CSRC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +(opt) | length | reason for leaving ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +class RtcpBye : public RtcpHeader { +public: + friend class RtcpHeader; + /* 变长,根据count决定有多少个ssrc */ + uint32_t ssrc[1]; + + /** 中间可能有若干个 ssrc **/ + + /* 可选 */ + uint8_t reason_len; + char reason[1]; + +public: + /** + * 创建bye包 + * @param ssrc ssrc列表 + * @param reason 原因 + * @return rtcp bye包 + */ + static std::shared_ptr create(const std::vector &ssrc, const std::string &reason); + + /** + * 获取ssrc列表 + */ + std::vector getSSRC(); + + /** + * 获取原因 + */ + std::string getReason() const; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +}; + +/* +0 1 2 3 +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|V=2|P|reserved | PT=XR=207 | length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| SSRC | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +: report blocks : ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +*/ +/* + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BT=4 | reserved | block length = 2 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, most significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, least significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +*/ +class RtcpXRRRTR : public RtcpHeader { +public: + friend class RtcpHeader; + uint32_t ssrc; + // 4 + uint8_t bt; + uint8_t reserved; + // 2 + uint16_t block_length; + // ntp timestamp MSW(in second) + uint32_t ntpmsw; + // ntp timestamp LSW(in picosecond) + uint32_t ntplsw; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); + +}; + +/* + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BT=5 | reserved | block length | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | SSRC_1 (SSRC of first receiver) | sub- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block + | last RR (LRR) | 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last RR (DLRR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | SSRC_2 (SSRC of second receiver) | sub- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block + : ... : 2 + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +*/ +class RtcpXRDLRRReportItem { +public: + friend class RtcpXRDLRR; + uint32_t ssrc; + uint32_t lrr; + uint32_t dlrr; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(); +}; + +class RtcpXRDLRR : public RtcpHeader { +public: + friend class RtcpHeader; + uint32_t ssrc; + uint8_t bt; + uint8_t reserved; + uint16_t block_length; + RtcpXRDLRRReportItem items; + + /** + * 创建RtcpXRDLRR包,只赋值了RtcpHeader部分(网络字节序) + * @param item_count RtcpXRDLRRReportItem对象个数 + * @return RtcpXRDLRR包 + */ + static std::shared_ptr create(size_t item_count); + + /** + * 获取RtcpXRDLRRReportItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::vector getItemList(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + std::string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); + +}; + +#pragma pack(pop) + +} // namespace mediakit +#endif // ZLMEDIAKIT_RTCP_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpContext.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpContext.h new file mode 100644 index 00000000..f80b2efe --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpContext.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTCPCONTEXT_H +#define ZLMEDIAKIT_RTCPCONTEXT_H + +#include "Rtcp.h" +#include +#include + +namespace mediakit { + +class RtcpContext { +public: + using Ptr = std::shared_ptr; + virtual ~RtcpContext() = default; + + /** + * 输出或输入rtp时调用 + * @param seq rtp的seq + * @param stamp rtp的时间戳,单位采样数(非毫秒) + * @param ntp_stamp_ms ntp时间戳 + * @param rtp rtp时间戳采样率,视频一般为90000,音频一般为采样率 + * @param bytes rtp数据长度 + */ + virtual void onRtp(uint16_t seq, uint32_t stamp, uint64_t ntp_stamp_ms, uint32_t sample_rate, size_t bytes); + + /** + * 输入sr rtcp包 + * @param rtcp 输入一个rtcp + */ + virtual void onRtcp(RtcpHeader *rtcp) = 0; + + /** + * 计算总丢包数 + */ + virtual size_t getLost(); + + /** + * 返回理应收到的rtp数 + */ + virtual size_t getExpectedPackets() const; + + /** + * 创建SR rtcp包 + * @param rtcp_ssrc rtcp的ssrc + * @return rtcp包 + */ + virtual toolkit::Buffer::Ptr createRtcpSR(uint32_t rtcp_ssrc); + + /** + * @brief 创建xr的dlrr包,用于接收者估算rtt + * + * @return toolkit::Buffer::Ptr + */ + virtual toolkit::Buffer::Ptr createRtcpXRDLRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc); + + /** + * 创建RR rtcp包 + * @param rtcp_ssrc rtcp的ssrc + * @param rtp_ssrc rtp的ssrc + * @return rtcp包 + */ + virtual toolkit::Buffer::Ptr createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc); + + /** + * 上次结果与本次结果间应收包数 + */ + virtual size_t getExpectedPacketsInterval(); + + /** + * 上次结果与本次结果间丢包个数 + */ + virtual size_t getLostInterval(); + +protected: + // 收到或发送的rtp的字节数 + size_t _bytes = 0; + // 收到或发送的rtp的个数 + size_t _packets = 0; + // 上次的rtp时间戳,毫秒 + uint32_t _last_rtp_stamp = 0; + uint64_t _last_ntp_stamp_ms = 0; +}; + +class RtcpContextForSend : public RtcpContext { +public: + toolkit::Buffer::Ptr createRtcpSR(uint32_t rtcp_ssrc) override; + + void onRtcp(RtcpHeader *rtcp) override; + + toolkit::Buffer::Ptr createRtcpXRDLRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc) override; + + /** + * 获取rtt + * @param ssrc rtp ssrc + * @return rtt,单位毫秒 + */ + uint32_t getRtt(uint32_t ssrc) const; + +private: + std::map _rtt; + std::map _sender_report_ntp; + + std::map _xr_rrtr_recv_sys_stamp; + std::map _xr_xrrtr_recv_last_rr; +}; + +class RtcpContextForRecv : public RtcpContext { +public: + void onRtp(uint16_t seq, uint32_t stamp, uint64_t ntp_stamp_ms, uint32_t sample_rate, size_t bytes) override; + toolkit::Buffer::Ptr createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc) override; + size_t getExpectedPackets() const override; + size_t getExpectedPacketsInterval() override; + size_t getLost() override; + size_t getLostInterval() override; + void onRtcp(RtcpHeader *rtcp) override; + +private: + // 时间戳抖动值 + double _jitter = 0; + // 第一个seq的值 + uint16_t _seq_base = 0; + // rtp最大seq + uint16_t _seq_max = 0; + // rtp回环次数 + uint16_t _seq_cycles = 0; + // 上次回环发生时,记录的rtp包数 + size_t _last_cycle_packets = 0; + // 上次的seq + uint16_t _last_rtp_seq = 0; + // 上次的rtp的系统时间戳(毫秒)用于统计抖动 + uint64_t _last_rtp_sys_stamp = 0; + // 上次统计的丢包总数 + size_t _last_lost = 0; + // 上次统计应收rtp包总数 + size_t _last_expected = 0; + // 上次收到sr包时计算出的Last SR timestamp + uint32_t _last_sr_lsr = 0; + // 上次收到sr时的系统时间戳,单位毫秒 + uint64_t _last_sr_ntp_sys = 0; +}; + +} // namespace mediakit +#endif // ZLMEDIAKIT_RTCPCONTEXT_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpFCI.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpFCI.h new file mode 100644 index 00000000..43c45487 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtcp/RtcpFCI.h @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTCPFCI_H +#define ZLMEDIAKIT_RTCPFCI_H + +#include "Rtcp.h" + +namespace mediakit { +#pragma pack(push, 1) + +/////////////////////////////////////////// PSFB //////////////////////////////////////////////////// + +// PSFB fmt = 2 +// https://tools.ietf.org/html/rfc4585#section-6.3.2.2 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | First | Number | PictureID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// First: 13 bits +// The macroblock (MB) address of the first lost macroblock. The MB +// numbering is done such that the macroblock in the upper left +// corner of the picture is considered macroblock number 1 and the +// number for each macroblock increases from left to right and then +// from top to bottom in raster-scan order (such that if there is a +// total of N macroblocks in a picture, the bottom right macroblock +// is considered macroblock number N). +// +// Number: 13 bits +// The number of lost macroblocks, in scan order as discussed above. +// +// PictureID: 6 bits +// The six least significant bits of the codec-specific identifier +// that is used to reference the picture in which the loss of the +// macroblock(s) has occurred. For many video codecs, the PictureID +// is identical to the Temporal Reference. +class FCI_SLI { +public: + static size_t constexpr kSize = 4; + + FCI_SLI(uint16_t first, uint16_t number, uint8_t pic_id); + + void check(size_t size); + uint16_t getFirst() const; + uint16_t getNumber() const; + uint8_t getPicID() const; + std::string dumpString() const; + +private: + uint32_t data; +}; + +#if 0 +//PSFB fmt = 3 +//https://tools.ietf.org/html/rfc4585#section-6.3.3.2 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PB |0| Payload Type| Native RPSI bit string | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | defined per codec ... | Padding (0) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_RPSI { +public: + //The number of unused bits required to pad the length of the RPSI + // message to a multiple of 32 bits. + uint8_t pb; + +#if __BYTE_ORDER == __BIG_ENDIAN + //0: 1 bit + // MUST be set to zero upon transmission and ignored upon reception. + uint8_t zero : 1; + //Payload Type: 7 bits + // Indicates the RTP payload type in the context of which the native + // RPSI bit string MUST be interpreted. + uint8_t pt : 7; +#else + uint8_t pt: 7; + uint8_t zero: 1; +#endif + + // Native RPSI bit string: variable length + // The RPSI information as natively defined by the video codec. + char bit_string[5]; + + //Padding: #PB bits + // A number of bits set to zero to fill up the contents of the RPSI + // message to the next 32-bit boundary. The number of padding bits + // MUST be indicated by the PB field. + uint8_t padding; + + static size_t constexpr kSize = 8; +}; +#endif + +// PSFB fmt = 4 +// https://tools.ietf.org/html/rfc5104#section-4.3.1.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_FIR { +public: + static size_t constexpr kSize = 8; + + FCI_FIR(uint32_t ssrc, uint8_t seq_number, uint32_t reserved = 0); + + void check(size_t size); + uint32_t getSSRC() const; + uint8_t getSeq() const; + uint32_t getReserved() const; + std::string dumpString() const; + +private: + uint32_t ssrc; + uint8_t seq_number; + uint8_t reserved[3]; +}; + +#if 0 +//PSFB fmt = 5 +//https://tools.ietf.org/html/rfc5104#section-4.3.2.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | Index | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_TSTR { +public: + static size_t constexpr kSize = 8; + + void check(size_t size) { + CHECK(size == kSize); + } + +private: + uint8_t data[kSize]; +}; + +//PSFB fmt = 6 +//https://tools.ietf.org/html/rfc5104#section-4.3.2.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | Index | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_TSTN : public FCI_TSTR{ + +}; + +//PSFB fmt = 7 +//https://tools.ietf.org/html/rfc5104#section-4.3.4.1 +//0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. |0| Payload Type| Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | VBCM Octet String.... | Padding | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_VBCM { +public: + static size_t constexpr kSize = 12; + + void check(size_t size) { + CHECK(size == kSize); + } + +private: + uint8_t data[kSize]; +}; + +#endif + +// PSFB fmt = 15 +// https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | +// Num SSRC (8 bits): Number of SSRCs in this message. +// +// BR Exp (6 bits): The exponential scaling of the mantissa for the +// maximum total media bit rate value, ignoring all packet +// overhead. The value is an unsigned integer [0..63], as +// in RFC 5104 section 4.2.2.1. +// +// BR Mantissa (18 bits): The mantissa of the maximum total media bit +// rate (ignoring all packet overhead) that the sender of +// the REMB estimates. The BR is the estimate of the +// traveled path for the SSRCs reported in this message. +// The value is an unsigned integer in number of bits per +// second. +// +// SSRC feedback (32 bits) Consists of one or more SSRC entries which +// this feedback message applies to. +class FCI_REMB { +public: + static size_t constexpr kSize = 8; + + static std::string create(const std::vector &ssrcs, uint32_t bitrate); + void check(size_t size); + std::string dumpString() const; + uint32_t getBitRate() const; + std::vector getSSRC(); + +private: + // Unique identifier 'R' 'E' 'M' 'B' + char magic[4]; + // Num SSRC (8 bits)/BR Exp (6 bits)/ BR Mantissa (18 bits) + uint8_t bitrate[4]; + // SSRC feedback (32 bits) Consists of one or more SSRC entries which + // this feedback message applies to. + uint32_t ssrc_feedback[1]; +}; + +/////////////////////////////////////////// RTPFB //////////////////////////////////////////////////// + +// RTPFB fmt = 1 +// https://tools.ietf.org/html/rfc4585#section-6.2.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_NACK { +public: + static constexpr size_t kSize = 4; + static constexpr size_t kBitSize = 16; + + FCI_NACK(uint16_t pid_h, const std::vector &type); + + void check(size_t size); + uint16_t getPid() const; + uint16_t getBlp() const; + // 返回丢包列表,总长度17,第一个包必丢 + // TODO: replace std::bitset + std::vector getBitArray() const; + std::string dumpString() const; + +private: + // The PID field is used to specify a lost packet. The PID field + // refers to the RTP sequence number of the lost packet. + uint16_t pid; + // bitmask of following lost packets (BLP): 16 bits + uint16_t blp; +}; + +#if 0 +//RTPFB fmt = 3 +//https://tools.ietf.org/html/rfc5104#section-4.2.1.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_TMMBR { +public: + static size_t constexpr kSize = 8; + + void check(size_t size) { + CHECK(size == kSize); + } + +private: + //SSRC (32 bits): The SSRC value of the media sender that is + // requested to obey the new maximum bit rate. + uint32_t ssrc; + + // MxTBR Exp (6 bits): The exponential scaling of the mantissa for the + // maximum total media bit rate value. The value is an + // unsigned integer [0..63]. + // MxTBR Mantissa (17 bits): The mantissa of the maximum total media + // bit rate value as an unsigned integer. + // Measured Overhead (9 bits): The measured average packet overhead + // value in bytes. The measurement SHALL be done according + // to the description in section 4.2.1.2. The value is an + // unsigned integer [0..511]. + uint32_t max_tbr; +}; + +//RTPFB fmt = 4 +// https://tools.ietf.org/html/rfc5104#section-4.2.2.1 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_TMMBN : public FCI_TMMBR{ +public: + +}; +#endif + +enum class SymbolStatus : uint8_t { + // Packet not received + not_received = 0, + // Packet received, small delta (所谓small detal是指能用一个字节表示的数值) + small_delta = 1, + // Packet received, large ornegative delta (large即是能用两个字节表示的数值) + large_delta = 2, + // Reserved + reserved = 3 +}; + +// RTPFB fmt = 15 +// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1 +// https://zhuanlan.zhihu.com/p/206656654 +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | base sequence number | packet status count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | reference time | fb pkt. count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | packet chunk | packet chunk | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | packet chunk | recv delta | recv delta | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | recv delta | recv delta | zero padding | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +class FCI_TWCC { +public: + static size_t constexpr kSize = 8; + using TwccPacketStatus + = std::map>; + void check(size_t size); + std::string dumpString(size_t total_size) const; + uint16_t getBaseSeq() const; + // 单位64ms + uint32_t getReferenceTime() const; + uint16_t getPacketCount() const; + TwccPacketStatus getPacketChunkList(size_t total_size) const; + + static std::string create(uint32_t ref_time, uint8_t fb_pkt_count, TwccPacketStatus &status); + +private: + // base sequence number,基础序号,本次反馈的第一个包的序号;也就是RTP扩展头的序列号 + uint16_t base_seq; + // packet status count, 包个数,本次反馈包含多少个包的状态;从基础序号开始算 + uint16_t pkt_status_count; + // reference time,基准时间,绝对时间;计算该包中每个媒体包的到达时间都要基于这个基准时间计算 + uint8_t ref_time[3]; + // feedback packet count,反馈包号,本包是第几个transport-cc包,每次加1 | + uint8_t fb_pkt_count; +}; +#pragma pack(pop) +} // namespace mediakit +#endif // ZLMEDIAKIT_RTCPFCI_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvMuxer.h new file mode 100644 index 00000000..01e34a82 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvMuxer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FLVMUXER_H +#define ZLMEDIAKIT_FLVMUXER_H + +#include "Rtmp/Rtmp.h" +#include "Rtmp/RtmpMediaSource.h" +#include "Poller/EventPoller.h" + +namespace mediakit { + +class FlvMuxer { +public: + using Ptr = std::shared_ptr; + FlvMuxer(); + virtual ~FlvMuxer() = default; + + void stop(); + +protected: + void start(const toolkit::EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media, uint32_t start_pts = 0); + virtual void onWrite(const toolkit::Buffer::Ptr &data, bool flush) = 0; + virtual void onDetach() = 0; + virtual std::shared_ptr getSharedPtr() = 0; + +private: + void onWriteFlvHeader(const RtmpMediaSource::Ptr &src); + void onWriteRtmp(const RtmpPacket::Ptr &pkt, bool flush); + void onWriteFlvTag(const RtmpPacket::Ptr &pkt, uint32_t time_stamp, bool flush); + void onWriteFlvTag(uint8_t type, const toolkit::Buffer::Ptr &buffer, uint32_t time_stamp, bool flush); + toolkit::BufferRaw::Ptr obtainBuffer(const void *data, size_t len); + toolkit::BufferRaw::Ptr obtainBuffer(); + +private: + toolkit::ResourcePool _packet_pool; + RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; +}; + +class FlvRecorder : public FlvMuxer , public std::enable_shared_from_this{ +public: + using Ptr = std::shared_ptr; + + void startRecord(const toolkit::EventPoller::Ptr &poller, const RtmpMediaSource::Ptr &media, const std::string &file_path); + void startRecord(const toolkit::EventPoller::Ptr &poller, const std::string &vhost, const std::string &app, const std::string &stream, const std::string &file_path); + +private: + virtual void onWrite(const toolkit::Buffer::Ptr &data, bool flush) override ; + virtual void onDetach() override; + virtual std::shared_ptr getSharedPtr() override; + +private: + std::shared_ptr _file; + std::recursive_mutex _file_mtx; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_FLVMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvPlayer.h new file mode 100644 index 00000000..12ba54e3 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvPlayer.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FLVPLAYER_H +#define ZLMEDIAKIT_FLVPLAYER_H + +#include "FlvSplitter.h" +#include "Http/HttpClientImp.h" +#include "Player/PlayerBase.h" + +namespace mediakit { + +class FlvPlayer : public PlayerBase, public HttpClientImp, private FlvSplitter { +public: + FlvPlayer(const toolkit::EventPoller::Ptr &poller); + + void play(const std::string &url) override; + void teardown() override; + +protected: + void onResponseHeader(const std::string &status, const HttpHeader &header) override; + void onResponseCompleted(const toolkit::SockException &ex) override; + void onResponseBody(const char *buf, size_t size) override; + +protected: + virtual void onRtmpPacket(RtmpPacket::Ptr packet) = 0; + virtual bool onMetadata(const AMFValue &metadata) = 0; + +private: + bool onRecvMetadata(const AMFValue &metadata) override; + void onRecvRtmpPacket(RtmpPacket::Ptr packet) override; + +private: + bool _play_result = false; + bool _benchmark_mode = false; +}; + +using FlvPlayerImp = FlvPlayerBase; + +}//namespace mediakit +#endif //ZLMEDIAKIT_FLVPLAYER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvSplitter.h new file mode 100644 index 00000000..cde80d82 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/FlvSplitter.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_FLVSPLITTER_H +#define ZLMEDIAKIT_FLVSPLITTER_H + +#include "Rtmp.h" +#include "Http/HttpRequestSplitter.h" +#include "RtmpPlayerImp.h" + +namespace mediakit { + +class FlvSplitter : public HttpRequestSplitter { +protected: + void onRecvContent(const char *data,size_t len) override; + ssize_t onRecvHeader(const char *data,size_t len) override; + const char *onSearchPacketTail(const char *data, size_t len) override; + +protected: + virtual void onRecvFlvHeader(const FLVHeader &header) {}; + virtual bool onRecvMetadata(const AMFValue &metadata) = 0; + virtual void onRecvRtmpPacket(RtmpPacket::Ptr packet) = 0; + +private: + bool _flv_started = false; + uint8_t _type; + uint32_t _time_stamp; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_FLVSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/Rtmp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/Rtmp.h new file mode 100644 index 00000000..ec87892e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/Rtmp.h @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef __rtmp_h +#define __rtmp_h + +#include +#include +#include +#include "amf.h" +#include "Network/Buffer.h" +#include "Extension/Track.h" + +#define DEFAULT_CHUNK_LEN 128 +#define HANDSHAKE_PLAINTEXT 0x03 +#define RANDOM_LEN (1536 - 8) + +#define MSG_SET_CHUNK 1 /*Set Chunk Size (1)*/ +#define MSG_ABORT 2 /*Abort Message (2)*/ +#define MSG_ACK 3 /*Acknowledgement (3)*/ +#define MSG_USER_CONTROL 4 /*User Control Messages (4)*/ +#define MSG_WIN_SIZE 5 /*Window Acknowledgement Size (5)*/ +#define MSG_SET_PEER_BW 6 /*Set Peer Bandwidth (6)*/ +#define MSG_AUDIO 8 /*Audio Message (8)*/ +#define MSG_VIDEO 9 /*Video Message (9)*/ +#define MSG_DATA 18 /*Data Message (18, 15) AMF0*/ +#define MSG_DATA3 15 /*Data Message (18, 15) AMF3*/ +#define MSG_CMD 20 /*Command Message AMF0 */ +#define MSG_CMD3 17 /*Command Message AMF3 */ +#define MSG_OBJECT3 16 /*Shared Object Message (19, 16) AMF3*/ +#define MSG_OBJECT 19 /*Shared Object Message (19, 16) AMF0*/ +#define MSG_AGGREGATE 22 /*Aggregate Message (22)*/ + +#define CONTROL_STREAM_BEGIN 0 +#define CONTROL_STREAM_EOF 1 +#define CONTROL_STREAM_DRY 2 +#define CONTROL_SETBUFFER 3 +#define CONTROL_STREAM_ISRECORDED 4 +#define CONTROL_PING_REQUEST 6 +#define CONTROL_PING_RESPONSE 7 + +#define STREAM_CONTROL 0 +#define STREAM_MEDIA 1 + +#define CHUNK_NETWORK 2 /*网络相关的消息(参见 Protocol Control Messages)*/ +#define CHUNK_SYSTEM 3 /*向服务器发送控制消息(反之亦可)*/ +#define CHUNK_CLIENT_REQUEST_BEFORE 3 /*客户端在createStream前,向服务器发出请求的chunkID*/ +#define CHUNK_CLIENT_REQUEST_AFTER 4 /*客户端在createStream后,向服务器发出请求的chunkID*/ +#define CHUNK_AUDIO 6 /*音频chunkID*/ +#define CHUNK_VIDEO 7 /*视频chunkID*/ + +namespace mediakit { + +#pragma pack(push, 1) + +class RtmpHandshake { +public: + RtmpHandshake(uint32_t _time, uint8_t *_random = nullptr); + + uint8_t time_stamp[4]; + uint8_t zero[4] = {0}; + uint8_t random[RANDOM_LEN]; + + void random_generate(char *bytes, int size); + + void create_complex_c0c1(); + +}; + +class RtmpHeader { +public: +#if __BYTE_ORDER == __BIG_ENDIAN + uint8_t fmt : 2; + uint8_t chunk_id : 6; +#else + uint8_t chunk_id : 6; + //0、1、2、3分别对应 12、8、4、1长度 + uint8_t fmt : 2; +#endif + uint8_t time_stamp[3]; + uint8_t body_size[3]; + uint8_t type_id; + uint8_t stream_index[4]; /* Note, this is little-endian while others are BE */ +}; + +class FLVHeader { +public: + static constexpr uint8_t kFlvVersion = 1; + static constexpr uint8_t kFlvHeaderLength = 9; + //FLV + char flv[3]; + //File version (for example, 0x01 for FLV version 1) + uint8_t version; +#if __BYTE_ORDER == __BIG_ENDIAN + //保留,置0 + uint8_t : 5; + //是否有音频 + uint8_t have_audio: 1; + //保留,置0 + uint8_t : 1; + //是否有视频 + uint8_t have_video: 1; +#else + //是否有视频 + uint8_t have_video: 1; + //保留,置0 + uint8_t : 1; + //是否有音频 + uint8_t have_audio: 1; + //保留,置0 + uint8_t : 5; +#endif + //The length of this header in bytes,固定为9 + uint32_t length; + //固定为0 + uint32_t previous_tag_size0; +}; + +class RtmpTagHeader { +public: + uint8_t type = 0; + uint8_t data_size[3] = {0}; + uint8_t timestamp[3] = {0}; + uint8_t timestamp_ex = 0; + uint8_t streamid[3] = {0}; /* Always 0. */ +}; + +struct RtmpVideoHeaderEnhanced { +#if __BYTE_ORDER == __BIG_ENDIAN + uint8_t enhanced : 1; + uint8_t frame_type : 3; + uint8_t pkt_type : 4; + uint32_t fourcc; +#else + uint8_t pkt_type : 4; + uint8_t frame_type : 3; + uint8_t enhanced : 1; + uint32_t fourcc; +#endif +}; + +struct RtmpVideoHeaderClassic { +#if __BYTE_ORDER == __BIG_ENDIAN + uint8_t frame_type : 4; + uint8_t codec_id : 4; + uint8_t h264_pkt_type; +#else + uint8_t codec_id : 4; + uint8_t frame_type : 4; + uint8_t h264_pkt_type; +#endif +}; + +#pragma pack(pop) + +class RtmpPacket : public toolkit::Buffer{ +public: + friend class RtmpProtocol; + using Ptr = std::shared_ptr; + bool is_abs_stamp; + uint8_t type_id; + uint32_t time_stamp; + uint32_t ts_field; + uint32_t stream_index; + uint32_t chunk_id; + size_t body_size; + toolkit::BufferLikeString buffer; + +public: + static Ptr create(); + + char *data() const override{ + return (char*)buffer.data(); + } + size_t size() const override { + return buffer.size(); + } + + void clear(); + + // video config frame和key frame都返回true + // 用于gop缓存定位 + bool isVideoKeyFrame() const; + + // aac config或h264/h265 config返回true,支持增强型rtmp + // 用于缓存解码配置信息 + bool isConfigFrame() const; + + int getRtmpCodecId() const; + int getAudioSampleRate() const; + int getAudioSampleBit() const; + int getAudioChannel() const; + +private: + friend class toolkit::ResourcePool_l; + RtmpPacket(){ + clear(); + } + + RtmpPacket &operator=(const RtmpPacket &that); + +private: + //对象个数统计 + toolkit::ObjectStatistic _statistic; +}; + +/** + * rtmp metadata基类,用于描述rtmp格式信息 + */ +class Metadata { +public: + using Ptr = std::shared_ptr; + + Metadata(): _metadata(AMF_OBJECT) {} + const AMFValue &getMetadata() const{ + return _metadata; + } + + static void addTrack(AMFValue &metadata, const Track::Ptr &track); + +protected: + AMFValue _metadata; +}; + +/** +* metadata中除音视频外的其他描述部分 +*/ +class TitleMeta : public Metadata { +public: + using Ptr = std::shared_ptr; + + TitleMeta(float dur_sec = 0, + size_t fileSize = 0, + const std::map &header = std::map()); + +}; + +class VideoMeta : public Metadata { +public: + using Ptr = std::shared_ptr; + + VideoMeta(const VideoTrack::Ptr &video); +}; + +class AudioMeta : public Metadata { +public: + using Ptr = std::shared_ptr; + + AudioMeta(const AudioTrack::Ptr &audio); +}; + +//根据音频track获取flags +uint8_t getAudioRtmpFlags(const Track::Ptr &track); + +////////////////// rtmp video ////////////////////////// +//https://rtmp.veriskope.com/pdf/video_file_format_spec_v10_1.pdf + +// UB [4]; Type of video frame. +enum class RtmpFrameType : uint8_t { + reserved = 0, + key_frame = 1, // key frame (for AVC, a seekable frame) + inter_frame = 2, // inter frame (for AVC, a non-seekable frame) + disposable_inter_frame = 3, // disposable inter frame (H.263 only) + generated_key_frame = 4, // generated key frame (reserved for server use only) + video_info_frame = 5, // video info/command frame +}; + +#define MKBETAG(a, b, c, d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) + +// UB [4]; Codec Identifier. +enum class RtmpVideoCodec : uint32_t { + h263 = 2, // Sorenson H.263 + screen_video = 3, // Screen video + vp6 = 4, // On2 VP6 + vp6_alpha = 5, // On2 VP6 with alpha channel + screen_video2 = 6, // Screen video version 2 + h264 = 7, // avc + h265 = 12, // 国内扩展 + + // 增强型rtmp FourCC + fourcc_vp9 = MKBETAG('v', 'p', '0', '9'), + fourcc_av1 = MKBETAG('a', 'v', '0', '1'), + fourcc_hevc = MKBETAG('h', 'v', 'c', '1') +}; + +// UI8; +enum class RtmpH264PacketType : uint8_t { + h264_config_header = 0, // AVC or HEVC sequence header(sps/pps) + h264_nalu = 1, // AVC or HEVC NALU + h264_end_seq = 2, // AVC or HEVC end of sequence (lower level NALU sequence ender is not REQUIRED or supported) +}; + +// https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp.pdf +// UB[4] +enum class RtmpPacketType : uint8_t { + PacketTypeSequenceStart = 0, + PacketTypeCodedFrames = 1, + PacketTypeSequenceEnd = 2, + + // CompositionTime Offset is implied to equal zero. This is + // an optimization to save putting SI24 composition time value of zero on + // the wire. See pseudo code below in the VideoTagBody section + PacketTypeCodedFramesX = 3, + + // VideoTagBody does not contain video data. VideoTagBody + // instead contains an AMF encoded metadata. See Metadata Frame + // section for an illustration of its usage. As an example, the metadata + // can be HDR information. This is a good way to signal HDR + // information. This also opens up future ways to express additional + // metadata that is meant for the next video sequence. + // + // note: presence of PacketTypeMetadata means that FrameType + // flags at the top of this table should be ignored + PacketTypeMetadata = 4, + + // Carriage of bitstream in MPEG-2 TS format + // note: PacketTypeSequenceStart and PacketTypeMPEG2TSSequenceStart + // are mutually exclusive + PacketTypeMPEG2TSSequenceStart = 5, +}; + +////////////////// rtmp audio ////////////////////////// +//https://rtmp.veriskope.com/pdf/video_file_format_spec_v10_1.pdf + +// UB [4]; Format of SoundData +enum class RtmpAudioCodec : uint8_t { + /** + 0 = Linear PCM, platform endian + 1 = ADPCM + 2 = MP3 + 3 = Linear PCM, little endian + 4 = Nellymoser 16 kHz mono + 5 = Nellymoser 8 kHz mono + 6 = Nellymoser + 7 = G.711 A-law logarithmic PCM + 8 = G.711 mu-law logarithmic PCM + 9 = reserved + 10 = AAC + 11 = Speex + 14 = MP3 8 kHz + 15 = Device-specific sound + */ + g711a = 7, + g711u = 8, + aac = 10, + opus = 13 // 国内扩展 +}; + +// UI8; +enum class RtmpAACPacketType : uint8_t { + aac_config_header = 0, // AAC sequence header + aac_raw = 1, // AAC raw +}; + +//////////////////////////////////////////// + +struct RtmpPacketInfo { + enum { kEnhancedRtmpHeaderSize = sizeof(RtmpVideoHeaderEnhanced) }; + + CodecId codec = CodecInvalid; + bool is_enhanced; + union { + struct { + RtmpFrameType frame_type; + RtmpPacketType pkt_type; // enhanced = true + RtmpH264PacketType h264_pkt_type; // enhanced = false + } video; + }; +}; +// https://github.com/veovera/enhanced-rtmp +CodecId parseVideoRtmpPacket(const uint8_t *data, size_t size, RtmpPacketInfo *info = nullptr); + +}//namespace mediakit +#endif//__rtmp_h diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpCodec.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpCodec.h new file mode 100644 index 00000000..a01f1f63 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpCodec.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTMPCODEC_H +#define ZLMEDIAKIT_RTMPCODEC_H + +#include "Rtmp/Rtmp.h" +#include "Extension/Frame.h" +#include "Util/RingBuffer.h" + +namespace mediakit{ + +class RtmpRing { +public: + using Ptr = std::shared_ptr; + using RingType = toolkit::RingBuffer; + + virtual ~RtmpRing() = default; + + /** + * 设置rtmp环形缓存 + */ + void setRtmpRing(const RingType::Ptr &ring) { + _ring = ring; + } + + /** + * 输入rtmp包 + * @param rtmp rtmp包 + */ + virtual void inputRtmp(const RtmpPacket::Ptr &rtmp) { + if (_ring) { + _ring->write(rtmp, rtmp->isVideoKeyFrame()); + } + } + +protected: + RingType::Ptr _ring; +}; + +class RtmpCodec : public RtmpRing, public FrameWriterInterface { +public: + using Ptr = std::shared_ptr; + RtmpCodec(Track::Ptr track) { _track = std::move(track); } + + virtual void makeConfigPacket() {} + + bool inputFrame(const Frame::Ptr &frame) override { return _track->inputFrame(frame); } + + const Track::Ptr &getTrack() const { return _track; } + +private: + Track::Ptr _track; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_RTMPCODEC_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpDemuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpDemuxer.h new file mode 100644 index 00000000..85f358ed --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpDemuxer.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPDEMUXER_H_ +#define SRC_RTMP_RTMPDEMUXER_H_ + +#include +#include +#include "Rtmp/amf.h" +#include "Rtmp/Rtmp.h" +#include "Common/MediaSink.h" +#include "RtmpCodec.h" + +namespace mediakit { + +class RtmpDemuxer : public Demuxer { +public: + using Ptr = std::shared_ptr; + + static size_t trackCount(const AMFValue &metadata); + + bool loadMetaData(const AMFValue &metadata); + + /** + * 开始解复用 + * @param pkt rtmp包 + */ + void inputRtmp(const RtmpPacket::Ptr &pkt); + + /** + * 获取节目总时长 + * @return 节目总时长,单位秒 + */ + float getDuration() const; + +private: + void makeVideoTrack(const AMFValue &val, int bit_rate); + void makeVideoTrack(const Track::Ptr &val, int bit_rate); + void makeAudioTrack(const AMFValue &val, int sample_rate, int channels, int sample_bit, int bit_rate); + +private: + bool _try_get_video_track = false; + bool _try_get_audio_track = false; + float _duration = 0; + AudioTrack::Ptr _audio_track; + VideoTrack::Ptr _video_track; + RtmpCodec::Ptr _audio_rtmp_decoder; + RtmpCodec::Ptr _video_rtmp_decoder; +}; + +} /* namespace mediakit */ + +#endif /* SRC_RTMP_RTMPDEMUXER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSource.h new file mode 100644 index 00000000..eb1e3ded --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSource.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPMEDIASOURCE_H_ +#define SRC_RTMP_RTMPMEDIASOURCE_H_ + +#include +#include +#include +#include +#include +#include "amf.h" +#include "Rtmp.h" +#include "Common/MediaSource.h" +#include "Common/PacketCache.h" +#include "Util/RingBuffer.h" + +#define RTMP_GOP_SIZE 512 + +namespace mediakit { + +/** + * rtmp媒体源的数据抽象 + * rtmp有关键的三要素,分别是metadata、config帧,普通帧 + * 其中metadata是非必须的,有些编码格式也没有config帧(比如MP3) + * 只要生成了这三要素,那么要实现rtmp推流、rtmp服务器就很简单了 + * rtmp推拉流协议中,先传递metadata,然后传递config帧,然后一直传递普通帧 + */ +class RtmpMediaSource : public MediaSource, public toolkit::RingDelegate, private PacketCache{ +public: + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = toolkit::RingBuffer; + + /** + * 构造函数 + * @param vhost 虚拟主机名 + * @param app 应用名 + * @param stream_id 流id + * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + */ + RtmpMediaSource(const MediaTuple& tuple, int ring_size = RTMP_GOP_SIZE): MediaSource(RTMP_SCHEMA, tuple), _ring_size(ring_size) {} + + ~RtmpMediaSource() override { flush(); } + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) override { + _ring->getInfoList(cb, on_change); + } + + /** + * 获取播放器个数 + * @return + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 获取metadata + */ + template + void getMetaData(const FUNC &func) const { + std::lock_guard lock(_mtx); + if (_metadata) { + func(_metadata); + } + } + + /** + * 获取所有的config帧 + */ + template + void getConfigFrame(const FUNC &func) { + std::lock_guard lock(_mtx); + for (auto &pr : _config_frame_map) { + func(pr.second); + } + } + + /** + * 设置metadata + */ + virtual void setMetaData(const AMFValue &metadata); + + /** + * 输入rtmp包 + * @param pkt rtmp包 + */ + void onWrite(RtmpPacket::Ptr pkt, bool = true) override; + + /** + * 获取当前时间戳 + */ + uint32_t getTimeStamp(TrackType trackType) override; + + void clearCache() override{ + PacketCache::clearCache(); + _ring->clearCache(); + } + + bool haveVideo() const { + return _have_video; + } + + bool haveAudio() const { + return _have_audio; + } + +private: + /** + * 批量flush rtmp包时触发该函数 + * @param rtmp_list rtmp包列表 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > rtmp_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + _ring->write(std::move(rtmp_list), _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + bool _have_audio = false; + int _ring_size; + uint32_t _track_stamps[TrackMax] = {0}; + AMFValue _metadata; + RingType::Ptr _ring; + + mutable std::recursive_mutex _mtx; + std::unordered_map _config_frame_map; +}; + +} /* namespace mediakit */ + +#endif /* SRC_RTMP_RTMPMEDIASOURCE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceImp.h new file mode 100644 index 00000000..31857c65 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceImp.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPTORTSPMEDIASOURCE_H_ +#define SRC_RTMP_RTMPTORTSPMEDIASOURCE_H_ + +#include +#include +#include +#include +#include +#include "amf.h" +#include "Rtmp.h" +#include "RtmpDemuxer.h" +#include "RtmpMediaSource.h" +#include "Common/MultiMediaSourceMuxer.h" + +namespace mediakit { + +class RtmpMediaSourceImp final : public RtmpMediaSource, private TrackListener, public MultiMediaSourceMuxer::Listener { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param id 流id + * @param ringSize 环形缓存大小 + */ + RtmpMediaSourceImp(const MediaTuple& tuple, int ringSize = RTMP_GOP_SIZE); + + /** + * 设置metadata + */ + void setMetaData(const AMFValue &metadata) override; + + /** + * 输入rtmp并解析 + */ + void onWrite(RtmpPacket::Ptr pkt, bool = true) override; + + /** + * 获取观看总人数,包括(hls/rtsp/rtmp) + */ + int totalReaderCount() override; + + /** + * 设置协议转换 + */ + void setProtocolOption(const ProtocolOption &option); + + const ProtocolOption &getProtocolOption() const { + return _option; + } + + /** + * _demuxer触发的添加Track事件 + */ + bool addTrack(const Track::Ptr &track) override; + + /** + * _demuxer触发的Track添加完毕事件 + */ + void addTrackCompleted() override; + + void resetTracks() override; + + /** + * _muxer触发的所有Track就绪的事件 + */ + void onAllTrackReady() override; + + /** + * 设置事件监听器 + * @param listener 监听器 + */ + void setListener(const std::weak_ptr &listener) override; + +private: + bool _all_track_ready = false; + bool _recreate_metadata = false; + ProtocolOption _option; + AMFValue _metadata; + RtmpDemuxer::Ptr _demuxer; + MultiMediaSourceMuxer::Ptr _muxer; + +}; +} /* namespace mediakit */ + +#endif /* SRC_RTMP_RTMPTORTSPMEDIASOURCE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceMuxer.h new file mode 100644 index 00000000..1d16d5de --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMediaSourceMuxer.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTMPMEDIASOURCEMUXER_H +#define ZLMEDIAKIT_RTMPMEDIASOURCEMUXER_H + +#include "RtmpMuxer.h" +#include "Rtmp/RtmpMediaSource.h" + +namespace mediakit { + +class RtmpMediaSourceMuxer final : public RtmpMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + RtmpMediaSourceMuxer(const MediaTuple& tuple, + const ProtocolOption &option, + const TitleMeta::Ptr &title = nullptr) : RtmpMuxer(title) { + _option = option; + _media_src = std::make_shared(tuple); + getRtmpRing()->setDelegate(_media_src); + } + + ~RtmpMediaSourceMuxer() override { + try { + RtmpMuxer::flush(); + } catch (std::exception &ex) { + WarnL << ex.what(); + } + } + + void setListener(const std::weak_ptr &listener){ + setDelegate(listener); + _media_src->setListener(shared_from_this()); + } + + void setTimeStamp(uint32_t stamp){ + _media_src->setTimeStamp(stamp); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void addTrackCompleted() override { + RtmpMuxer::addTrackCompleted(); + makeConfigPacket(); + _media_src->setMetaData(getMetadata()); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = _option.rtmp_demand ? size : true; + if (!size && _option.rtmp_demand) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache && _option.rtmp_demand) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled || !_option.rtmp_demand) { + return RtmpMuxer::inputFrame(frame); + } + return false; + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _option.rtmp_demand ? (_clear_cache ? true : _enabled) : true; + } + +private: + bool _enabled = true; + bool _clear_cache = false; + ProtocolOption _option; + RtmpMediaSource::Ptr _media_src; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_RTMPMEDIASOURCEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMuxer.h new file mode 100644 index 00000000..d029a632 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpMuxer.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTMPMUXER_H +#define ZLMEDIAKIT_RTMPMUXER_H + +#include "Rtmp/Rtmp.h" +#include "Extension/Frame.h" +#include "Common/MediaSink.h" +#include "RtmpCodec.h" + +namespace mediakit{ + +class RtmpMuxer : public MediaSinkInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + */ + RtmpMuxer(const TitleMeta::Ptr &title); + + /** + * 获取完整的SDP字符串 + * @return SDP字符串 + */ + const AMFValue &getMetadata() const ; + + /** + * 获取rtmp环形缓存 + * @return + */ + RtmpRing::RingType::Ptr getRtmpRing() const; + + /** + * 添加ready状态的track + */ + bool addTrack(const Track::Ptr & track) override; + + /** + * 写入帧数据 + * @param frame 帧 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 刷新输出所有frame缓存 + */ + void flush() override; + + /** + * 重置所有track + */ + void resetTracks() override ; + + /** + * 生成config包 + */ + void makeConfigPacket(); + +private: + bool _track_existed[2] = { false, false }; + + AMFValue _metadata; + RtmpRing::RingType::Ptr _rtmp_ring; + std::unordered_map _encoders; +}; + + +} /* namespace mediakit */ + +#endif //ZLMEDIAKIT_RTMPMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayer.h new file mode 100644 index 00000000..ccac9360 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayer.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RtmpPlayer_H_ +#define SRC_RTMP_RtmpPlayer_H_ + +#include +#include +#include +#include "amf.h" +#include "Rtmp.h" +#include "RtmpProtocol.h" +#include "Player/PlayerBase.h" +#include "Util/TimeTicker.h" +#include "Network/Socket.h" +#include "Network/TcpClient.h" + +namespace mediakit { + +//实现了rtmp播放器协议部分的功能,及数据接收功能 +class RtmpPlayer : public PlayerBase, public toolkit::TcpClient, public RtmpProtocol { +public: + using Ptr = std::shared_ptr; + RtmpPlayer(const toolkit::EventPoller::Ptr &poller); + ~RtmpPlayer() override; + + void play(const std::string &strUrl) override; + void pause(bool bPause) override; + void speed(float speed) override; + void teardown() override; + +protected: + virtual bool onMetadata(const AMFValue &val) = 0; + virtual void onRtmpPacket(RtmpPacket::Ptr chunk_data) = 0; + uint32_t getProgressMilliSecond() const; + void seekToMilliSecond(uint32_t ms); + +protected: + void onMediaData_l(RtmpPacket::Ptr chunk_data); + //在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了 + void onPlayResult_l(const toolkit::SockException &ex, bool handshake_done); + + //form Tcpclient + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onConnect(const toolkit::SockException &err) override; + void onError(const toolkit::SockException &ex) override; + //from RtmpProtocol + void onRtmpChunk(RtmpPacket::Ptr chunk_data) override; + void onStreamDry(uint32_t stream_index) override; + void onSendRawData(toolkit::Buffer::Ptr buffer) override { + send(std::move(buffer)); + } + + template + void addOnResultCB(const FUNC &func) { + _map_on_result.emplace(_send_req_id, func); + } + template + void addOnStatusCB(const FUNC &func) { + _deque_on_status.emplace_back(func); + } + + void onCmd_result(AMFDecoder &dec); + void onCmd_onStatus(AMFDecoder &dec); + void onCmd_onMetaData(AMFDecoder &dec); + + void send_connect(); + void send_createStream(); + void send_play(); + void send_pause(bool pause); + +private: + std::string _app; + std::string _stream_id; + std::string _tc_url; + + bool _paused = false; + bool _metadata_got = false; + //是否为性能测试模式 + bool _benchmark_mode = false; + + //播放进度控制 + uint32_t _seek_ms = 0; + uint32_t _fist_stamp[2] = {0, 0}; + uint32_t _now_stamp[2] = {0, 0}; + toolkit::Ticker _now_stamp_ticker[2]; + std::deque > _deque_on_status; + std::unordered_map > _map_on_result; + + //rtmp接收超时计时器 + toolkit::Ticker _rtmp_recv_ticker; + //心跳发送定时器 + std::shared_ptr _beat_timer; + //播放超时定时器 + std::shared_ptr _play_timer; + //rtmp接收超时定时器 + std::shared_ptr _rtmp_recv_timer; +}; + +} /* namespace mediakit */ +#endif /* SRC_RTMP_RtmpPlayer_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayerImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayerImp.h new file mode 100644 index 00000000..ddcf469f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPlayerImp.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPPLAYERIMP_H_ +#define SRC_RTMP_RTMPPLAYERIMP_H_ + +#include +#include +#include "Common/config.h" +#include "RtmpPlayer.h" +#include "RtmpMediaSource.h" +#include "RtmpDemuxer.h" +#include "Poller/Timer.h" +#include "Util/TimeTicker.h" + +namespace mediakit { + +template +class FlvPlayerBase: public PlayerImp, private TrackListener { +public: + using Ptr = std::shared_ptr; + using Super = PlayerImp; + + FlvPlayerBase(const toolkit::EventPoller::Ptr &poller) : Super(poller) {}; + + ~FlvPlayerBase() override { + DebugL << std::endl; + } + + float getDuration() const override { + return _demuxer ? _demuxer->getDuration() : 0; + } + + std::vector getTracks(bool ready = true) const override { + return _demuxer ? _demuxer->getTracks(ready) : Super::getTracks(ready); + } + +private: + //派生类回调函数 + bool onMetadata(const AMFValue &val) override { + //无metadata或metadata中无track信息时,需要从数据包中获取track + _wait_track_ready = this->Super::operator[](Client::kWaitTrackReady).template as() || RtmpDemuxer::trackCount(val) == 0; + onCheckMeta_l(val); + return true; + } + + void onRtmpPacket(RtmpPacket::Ptr chunkData) override { + if (!_demuxer) { + //有些rtmp流没metadata + onCheckMeta_l(TitleMeta().getMetadata()); + } + _demuxer->inputRtmp(chunkData); + if (_rtmp_src) { + _rtmp_src->onWrite(std::move(chunkData)); + } + } + + void onPlayResult(const toolkit::SockException &ex) override { + if (!_wait_track_ready || ex) { + Super::onPlayResult(ex); + return; + } + } + + bool addTrack(const Track::Ptr &track) override { return true; } + + void addTrackCompleted() override { + if (_wait_track_ready) { + Super::onPlayResult(toolkit::SockException(toolkit::Err_success, "play success")); + } + } + +private: + void onCheckMeta_l(const AMFValue &val) { + _rtmp_src = std::dynamic_pointer_cast(this->Super::_media_src); + if (_rtmp_src) { + _rtmp_src->setMetaData(val); + } + if(_demuxer){ + return; + } + _demuxer = std::make_shared(); + //TraceL<<" _wait_track_ready "<<_wait_track_ready; + _demuxer->setTrackListener(this, _wait_track_ready); + _demuxer->loadMetaData(val); + } + +private: + bool _wait_track_ready = true; + RtmpDemuxer::Ptr _demuxer; + RtmpMediaSource::Ptr _rtmp_src; +}; + +class RtmpPlayerImp: public FlvPlayerBase { +public: + using Ptr = std::shared_ptr; + using Super = FlvPlayerBase; + + RtmpPlayerImp(const toolkit::EventPoller::Ptr &poller) : Super(poller) {}; + + ~RtmpPlayerImp() override { + DebugL; + } + + float getProgress() const override { + if (getDuration() > 0) { + return getProgressMilliSecond() / (getDuration() * 1000); + } + return PlayerBase::getProgress(); + } + + void seekTo(float fProgress) override { + fProgress = MAX(float(0), MIN(fProgress, float(1.0))); + seekToMilliSecond((uint32_t)(fProgress * getDuration() * 1000)); + } + + void seekTo(uint32_t seekPos) override { + uint32_t pos = MAX(float(0), MIN(seekPos, getDuration())) * 1000; + seekToMilliSecond(pos); + } +}; + + +} /* namespace mediakit */ + +#endif /* SRC_RTMP_RTMPPLAYERIMP_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpProtocol.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpProtocol.h new file mode 100644 index 00000000..7ace93eb --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpProtocol.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPPROTOCOL_H_ +#define SRC_RTMP_RTMPPROTOCOL_H_ + +#include +#include +#include +#include +#include "amf.h" +#include "Rtmp.h" +#include "Util/ResourcePool.h" +#include "Http/HttpRequestSplitter.h" + +namespace mediakit { + +class RtmpProtocol : public HttpRequestSplitter{ +public: + RtmpProtocol(); + virtual ~RtmpProtocol(); + + void onParseRtmp(const char *data, size_t size); + //作为客户端发送c0c1,等待s0s1s2并且回调 + void startClientSession(const std::function &cb, bool complex = true); + +protected: + virtual void onSendRawData(toolkit::Buffer::Ptr buffer) = 0; + virtual void onRtmpChunk(RtmpPacket::Ptr chunk_data) = 0; + virtual void onStreamBegin(uint32_t stream_index){ + _stream_index = stream_index; + } + virtual void onStreamEof(uint32_t stream_index){}; + virtual void onStreamDry(uint32_t stream_index){}; + +protected: + //// HttpRequestSplitter override //// + ssize_t onRecvHeader(const char *data, size_t len) override { return 0; } + const char *onSearchPacketTail(const char *data, size_t len) override; + +protected: + void reset(); + void sendAcknowledgement(uint32_t size); + void sendAcknowledgementSize(uint32_t size); + void sendPeerBandwidth(uint32_t size); + void sendChunkSize(uint32_t size); + void sendPingRequest(uint32_t ti = ::time(NULL)); + void sendPingResponse(uint32_t time_stamp = ::time(NULL)); + void sendSetBufferLength(uint32_t stream_index, uint32_t len); + void sendUserControl(uint16_t event_type, uint32_t event_data); + void sendUserControl(uint16_t event_type, const std::string &event_data); + void sendInvoke(const std::string &cmd, const AMFValue &val); + void sendRequest(int cmd, const std::string &str); + void sendResponse(int type, const std::string &str); + void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id); + void sendRtmp(uint8_t type, uint32_t stream_index, const toolkit::Buffer::Ptr &buffer, uint32_t stamp, int chunk_id); + toolkit::BufferRaw::Ptr obtainBuffer(const void *data = nullptr, size_t len = 0); + +private: + void handle_C1_simple(const char *data); +#ifdef ENABLE_OPENSSL + void handle_C1_complex(const char *data); + std::string get_C1_digest(const uint8_t *ptr,char **digestPos); + std::string get_C1_key(const uint8_t *ptr); + void check_C1_Digest(const std::string &digest,const std::string &data); + void send_complex_S0S1S2(int schemeType,const std::string &digest); +#endif //ENABLE_OPENSSL + + const char* handle_S0S1S2(const char *data, size_t len, const std::function &func); + const char* handle_C0C1(const char *data, size_t len); + const char* handle_C2(const char *data, size_t len); + const char* handle_rtmp(const char *data, size_t len); + void handle_chunk(RtmpPacket::Ptr chunk_data); + +protected: + int _send_req_id = 0; + int _now_stream_index = 0; + uint32_t _stream_index = STREAM_CONTROL; + +private: + bool _data_started = false; + int _now_chunk_id = 0; + ////////////ChunkSize//////////// + size_t _chunk_size_in = DEFAULT_CHUNK_LEN; + size_t _chunk_size_out = DEFAULT_CHUNK_LEN; + ////////////Acknowledgement//////////// + uint64_t _bytes_sent = 0; + uint64_t _bytes_sent_last = 0; + uint64_t _bytes_recv = 0; + uint64_t _bytes_recv_last = 0; + uint32_t _windows_size = 0; + ///////////PeerBandwidth/////////// + uint32_t _bandwidth = 2500000; + uint8_t _band_limit_type = 2; + //////////Rtmp parser////////// + std::function _next_step_func; + ////////////Chunk//////////// + std::unordered_map > _map_chunk_data; + //循环池 + toolkit::ResourcePool _packet_pool; +}; + +} /* namespace mediakit */ +#endif /* SRC_RTMP_RTMPPROTOCOL_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPusher.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPusher.h new file mode 100644 index 00000000..e74012ee --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpPusher.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPPUSHER_H_ +#define SRC_RTMP_RTMPPUSHER_H_ + +#include "RtmpProtocol.h" +#include "RtmpMediaSource.h" +#include "Network/TcpClient.h" +#include "Pusher/PusherBase.h" + +namespace mediakit { + +class RtmpPusher : public RtmpProtocol, public toolkit::TcpClient, public PusherBase { +public: + using Ptr = std::shared_ptr; + RtmpPusher(const toolkit::EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src); + ~RtmpPusher() override; + + void publish(const std::string &url) override ; + void teardown() override; + +protected: + //for Tcpclient override + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onConnect(const toolkit::SockException &err) override; + void onError(const toolkit::SockException &ex) override; + + //for RtmpProtocol override + void onRtmpChunk(RtmpPacket::Ptr chunk_data) override; + void onSendRawData(toolkit::Buffer::Ptr buffer) override{ + send(std::move(buffer)); + } + +private: + void onPublishResult_l(const toolkit::SockException &ex, bool handshake_done); + + template + inline void addOnResultCB(const FUN &fun) { + _map_on_result.emplace(_send_req_id, fun); + } + template + inline void addOnStatusCB(const FUN &fun) { + _deque_on_status.emplace_back(fun); + } + + void onCmd_result(AMFDecoder &dec); + void onCmd_onStatus(AMFDecoder &dec); + void onCmd_onMetaData(AMFDecoder &dec); + + inline void send_connect(); + inline void send_createStream(); + inline void send_publish(); + inline void send_metaData(); + void setSocketFlags(); + +private: + std::string _app; + std::string _stream_id; + std::string _tc_url; + std::deque > _deque_on_status; + std::unordered_map > _map_on_result; + + //推流超时定时器 + std::shared_ptr _publish_timer; + std::weak_ptr _publish_src; + RtmpMediaSource::RingType::RingReader::Ptr _rtmp_reader; +}; + +using RtmpPusherImp = PusherImp; + +} /* namespace mediakit */ + +#endif /* SRC_RTMP_RTMPPUSHER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpSession.h new file mode 100644 index 00000000..754b2609 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/RtmpSession.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTMP_RTMPSESSION_H_ +#define SRC_RTMP_RTMPSESSION_H_ + +#include +#include "amf.h" +#include "Rtmp.h" +#include "utils.h" +#include "RtmpProtocol.h" +#include "RtmpMediaSourceImp.h" +#include "Util/TimeTicker.h" +#include "Network/Session.h" + +namespace mediakit { + +class RtmpSession : public toolkit::Session, public RtmpProtocol, public MediaSourceEvent { +public: + using Ptr = std::shared_ptr; + + RtmpSession(const toolkit::Socket::Ptr &sock); + + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onError(const toolkit::SockException &err) override; + void onManager() override; + +private: + void onProcessCmd(AMFDecoder &dec); + void onCmd_connect(AMFDecoder &dec); + void onCmd_createStream(AMFDecoder &dec); + + void onCmd_publish(AMFDecoder &dec); + void onCmd_deleteStream(AMFDecoder &dec); + + void onCmd_play(AMFDecoder &dec); + void onCmd_play2(AMFDecoder &dec); + void doPlay(AMFDecoder &dec); + void doPlayResponse(const std::string &err,const std::function &cb); + void sendPlayResponse(const std::string &err,const RtmpMediaSource::Ptr &src); + + void onCmd_seek(AMFDecoder &dec); + void onCmd_pause(AMFDecoder &dec); + void onCmd_playCtrl(AMFDecoder &dec); + void setMetaData(AMFDecoder &dec); + + void onSendMedia(const RtmpPacket::Ptr &pkt); + void onSendRawData(toolkit::Buffer::Ptr buffer) override{ + _total_bytes += buffer->size(); + send(std::move(buffer)); + } + void onRtmpChunk(RtmpPacket::Ptr chunk_data) override; + + template + inline void sendReply(const char *str, const first &reply, const second &status) { + AMFEncoder invoke; + invoke << str << _recv_req_id << reply << status; + sendResponse(MSG_CMD, invoke.data()); + } + + ///////MediaSourceEvent override/////// + // 关闭 + bool close(MediaSource &sender) override; + // 播放总人数 + int totalReaderCount(MediaSource &sender) override; + // 获取媒体源类型 + MediaOriginType getOriginType(MediaSource &sender) const override; + // 获取媒体源url或者文件路径 + std::string getOriginUrl(MediaSource &sender) const override; + // 获取媒体源客户端相关信息 + std::shared_ptr getOriginSock(MediaSource &sender) const override; + // 由于支持断连续推,存在OwnerPoller变更的可能 + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + + void setSocketFlags(); + std::string getStreamId(const std::string &str); + void dumpMetadata(const AMFValue &metadata); + void sendStatus(const std::initializer_list &key_value); + +private: + bool _set_meta_data = false; + double _recv_req_id = 0; + //断连续推延时 + uint32_t _continue_push_ms = 0; + //消耗的总流量 + uint64_t _total_bytes = 0; + //数据接收超时计时器 + toolkit::Ticker _ticker; + MediaInfo _media_info; + std::weak_ptr _play_src; + AMFValue _push_metadata; + std::map _push_config_packets; + RtmpMediaSourceImp::Ptr _push_src; + std::shared_ptr _push_src_ownership; + RtmpMediaSource::RingType::RingReader::Ptr _ring_reader; +}; + +/** + * 支持ssl加密的rtmp服务器 + */ +using RtmpSessionWithSSL = toolkit::SessionWithSSL; + +} /* namespace mediakit */ +#endif /* SRC_RTMP_RTMPSESSION_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/amf.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/amf.h new file mode 100644 index 00000000..c3f7ed29 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/amf.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef __amf_h +#define __amf_h + +#include +#include +#include +#include +#include +#include +namespace toolkit { + class BufferLikeString; +} +enum AMFType { + AMF_NUMBER, + AMF_INTEGER, + AMF_BOOLEAN, + AMF_STRING, + AMF_OBJECT, + AMF_NULL, + AMF_UNDEFINED, + AMF_ECMA_ARRAY, + AMF_STRICT_ARRAY, +}; + +class AMFValue; + +class AMFValue { +public: + friend class AMFEncoder; + + using mapType = std::map; + using arrayType = std::vector; + + ~AMFValue(); + AMFValue(AMFType type = AMF_NULL); + AMFValue(const char *s); + AMFValue(const std::string &s); + AMFValue(double n); + AMFValue(int i); + AMFValue(bool b); + AMFValue(const AMFValue &from); + AMFValue &operator = (const AMFValue &from); + + void clear(); + AMFType type() const ; + const std::string &as_string() const; + double as_number() const; + int as_integer() const; + bool as_boolean() const; + std::string to_string() const; + const AMFValue &operator[](const char *str) const; + void object_for_each(const std::function &fun) const ; + operator bool() const; + void set(const std::string &s, const AMFValue &val); + void add(const AMFValue &val); + +private: + const mapType &getMap() const; + const arrayType &getArr() const; + void destroy(); + void init(); + +private: + AMFType _type; + union { + std::string *string; + double number; + int integer; + bool boolean; + mapType *object; + arrayType *array; + } _value; +}; + +class AMFDecoder { +public: + AMFDecoder(const toolkit::BufferLikeString &buf, size_t pos, int version = 0); + template + TP load(); + +private: + std::string load_key(); + AMFValue load_object(); + AMFValue load_ecma(); + AMFValue load_arr(); + uint8_t front(); + uint8_t pop_front(); + +private: + const toolkit::BufferLikeString &buf; + size_t pos; + int version; +}; + +class AMFEncoder { +public: + AMFEncoder & operator <<(const char *s); + AMFEncoder & operator <<(const std::string &s); + AMFEncoder & operator <<(std::nullptr_t); + AMFEncoder & operator <<(const int n); + AMFEncoder & operator <<(const double n); + AMFEncoder & operator <<(const bool b); + AMFEncoder & operator <<(const AMFValue &value); + const std::string& data() const ; + void clear() ; + +private: + void write_key(const std::string &s); + AMFEncoder &write_undefined(); + +private: + std::string buf; +}; + + +#endif diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/utils.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/utils.h new file mode 100644 index 00000000..3e13a7f8 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtmp/utils.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef __utils_h +#define __utils_h + +#include +#include + + +uint32_t load_be32(const void *p); +uint16_t load_be16(const void *p); +uint32_t load_be24(const void *p); +uint32_t load_le32(const void *p); +void set_be24(void *p, uint32_t val); +void set_le32(void *p, uint32_t val); +void set_be32(void *p, uint32_t val); + + +#endif diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/Decoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/Decoder.h new file mode 100644 index 00000000..9da460c4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/Decoder.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_DECODER_H +#define ZLMEDIAKIT_DECODER_H + +#include +#include +#include +#include "Common/MediaSink.h" + +namespace mediakit { + +class Decoder { +public: + using Ptr = std::shared_ptr; + using onDecode = std::function; + using onStream = std::function; + + virtual ssize_t input(const uint8_t *data, size_t bytes) = 0; + void setOnDecode(onDecode cb); + void setOnStream(onStream cb); + +protected: + Decoder() = default; + virtual ~Decoder() = default; + +protected: + onDecode _on_decode; + onStream _on_stream; +}; + +class DecoderImp { +public: + typedef enum { decoder_ts = 0, decoder_ps } Type; + + using Ptr = std::shared_ptr; + + static Ptr createDecoder(Type type, MediaSinkInterface *sink); + ssize_t input(const uint8_t *data, size_t bytes); + void flush(); + +protected: + void onTrack(int index, const Track::Ptr &track); + void onFrame(int index, const Frame::Ptr &frame); + +private: + DecoderImp(const Decoder::Ptr &decoder, MediaSinkInterface *sink); + void onDecode(int stream, int codecid, int flags, int64_t pts, int64_t dts, const void *data, size_t bytes); + void onStream(int stream, int codecid, const void *extra, size_t bytes, int finish); + +private: + bool _have_video = false; + Decoder::Ptr _decoder; + MediaSinkInterface *_sink; + + class FrameMergerImp : public FrameMerger { + public: + FrameMergerImp() : FrameMerger(FrameMerger::none) {} + }; + std::unordered_map > _tracks; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_DECODER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/GB28181Process.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/GB28181Process.h new file mode 100644 index 00000000..4cef1e95 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/GB28181Process.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_GB28181ROCESS_H +#define ZLMEDIAKIT_GB28181ROCESS_H + +#if defined(ENABLE_RTPPROXY) + +#include "Decoder.h" +#include "ProcessInterface.h" +#include "Http/HttpRequestSplitter.h" +#include "Rtsp/RtpCodec.h" +#include "Common/MediaSource.h" + +namespace mediakit{ + +class RtpReceiverImp; +class GB28181Process : public ProcessInterface { +public: + using Ptr = std::shared_ptr; + + GB28181Process(const MediaInfo &media_info, MediaSinkInterface *sink); + + /** + * 输入rtp + * @param data rtp数据指针 + * @param data_len rtp数据长度 + * @return 是否解析成功 + */ + bool inputRtp(bool, const char *data, size_t data_len) override; + + /** + * 刷新输出所有缓存 + */ + void flush() override; + +protected: + void onRtpSorted(RtpPacket::Ptr rtp); + +private: + void onRtpDecode(const Frame::Ptr &frame); + +private: + MediaInfo _media_info; + DecoderImp::Ptr _decoder; + MediaSinkInterface *_interface; + std::shared_ptr _save_file_ps; + std::unordered_map _rtp_decoder; + std::unordered_map > _rtp_receiver; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_GB28181ROCESS_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSDecoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSDecoder.h new file mode 100644 index 00000000..db05e282 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSDecoder.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_PSDECODER_H +#define ZLMEDIAKIT_PSDECODER_H + +#if defined(ENABLE_RTPPROXY) +#include +#include "Decoder.h" +#include "Http/HttpRequestSplitter.h" + +namespace mediakit{ + +//ps解析器 +class PSDecoder : public Decoder, private HttpRequestSplitter { +public: + PSDecoder(); + ~PSDecoder(); + + ssize_t input(const uint8_t* data, size_t bytes) override; + + // HttpRequestSplitter interface +private: + using HttpRequestSplitter::input; + const char *onSearchPacketTail(const char *data, size_t len) override; + ssize_t onRecvHeader(const char *, size_t) override { return 0; }; + +private: + void *_ps_demuxer = nullptr; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_PSDECODER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSEncoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSEncoder.h new file mode 100644 index 00000000..e195913a --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/PSEncoder.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_PSENCODER_H +#define ZLMEDIAKIT_PSENCODER_H + +#if defined(ENABLE_RTPPROXY) + +#include "Record/MPEG.h" +#include "Common/MediaSink.h" + +namespace mediakit { + +class CommonRtpEncoder; + +class PSEncoderImp : public MpegMuxer { +public: + /** + * 创建psh或ts rtp编码器 + * @param ssrc rtp的ssrc + * @param payload_type rtp的pt + * @param ps_or_ts true: ps, false: ts + */ + PSEncoderImp(uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true); + ~PSEncoderImp() override; + +protected: + //rtp打包后回调 + virtual void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) = 0; + +protected: + void onWrite(std::shared_ptr buffer, uint64_t stamp, bool key_pos) override; + +private: + std::shared_ptr _rtp_encoder; +}; + +}//namespace mediakit + +#endif //ENABLE_RTPPROXY +#endif //ZLMEDIAKIT_PSENCODER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/ProcessInterface.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/ProcessInterface.h new file mode 100644 index 00000000..b4fd97f4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/ProcessInterface.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + + +#ifndef ZLMEDIAKIT_PROCESSINTERFACE_H +#define ZLMEDIAKIT_PROCESSINTERFACE_H + +#include +#include + +namespace mediakit { + +class ProcessInterface { +public: + using Ptr = std::shared_ptr; + virtual ~ProcessInterface() = default; + + /** + * 输入rtp + * @param is_udp 是否为udp模式 + * @param data rtp数据指针 + * @param data_len rtp数据长度 + * @return 是否解析成功 + */ + virtual bool inputRtp(bool is_udp, const char *data, size_t data_len) = 0; + + /** + * 刷新输出所有缓存 + */ + virtual void flush() {} +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_PROCESSINTERFACE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RawEncoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RawEncoder.h new file mode 100644 index 00000000..71ae12f5 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RawEncoder.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RAWENCODER_H +#define ZLMEDIAKIT_RAWENCODER_H + +#if defined(ENABLE_RTPPROXY) + +#include "Common/MediaSink.h" +#include "Rtsp/RtpCodec.h" + +namespace mediakit { + +class RawEncoderImp : public MediaSinkInterface { +public: + RawEncoderImp(uint32_t ssrc, uint8_t payload_type = 96, bool send_audio = true); + ~RawEncoderImp() override; + + /** + * 添加音视频轨道 + */ + bool addTrack(const Track::Ptr &track) override; + + /** + * 重置音视频轨道 + */ + void resetTracks() override; + + /** + * 输入帧数据 + */ + bool inputFrame(const Frame::Ptr &frame) override; + +protected: + // rtp打包后回调 + virtual void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) = 0; + +private: + std::shared_ptr createRtpEncoder(const Track::Ptr &track); + +private: + bool _send_audio; + uint8_t _payload_type; + uint32_t _ssrc; + RtpCodec::Ptr _rtp_encoder; +}; + +} // namespace mediakit + +#endif // ENABLE_RTPPROXY +#endif // ZLMEDIAKIT_RAWENCODER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpCache.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpCache.h new file mode 100644 index 00000000..bd570c47 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpCache.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPCACHE_H +#define ZLMEDIAKIT_RTPCACHE_H + +#if defined(ENABLE_RTPPROXY) + +#include "PSEncoder.h" +#include "RawEncoder.h" +#include "Common/PacketCache.h" + +namespace mediakit{ + +class RtpCache : protected PacketCache { +public: + using onFlushed = std::function >)>; + RtpCache(onFlushed cb); + +protected: + /** + * 输入rtp(目的是为了合并写) + * @param buffer rtp数据 + */ + void input(uint64_t stamp, toolkit::Buffer::Ptr buffer,bool is_key = false); + +protected: + void onFlush(std::shared_ptr > rtp_list, bool) override; + +private: + onFlushed _cb; +}; + +class RtpCachePS : public RtpCache, public PSEncoderImp { +public: + RtpCachePS(onFlushed cb, uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true) : + RtpCache(std::move(cb)), PSEncoderImp(ssrc, ps_or_ts ? payload_type : Rtsp::PT_MP2T, ps_or_ts) {}; + + void flush() override; + +protected: + void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) override; +}; + +class RtpCacheRaw : public RtpCache, public RawEncoderImp { +public: + RtpCacheRaw(onFlushed cb, uint32_t ssrc, uint8_t payload_type = 96, bool send_audio = true) : RtpCache(std::move(cb)), RawEncoderImp(ssrc, payload_type, send_audio) {}; + void flush() override; + +protected: + void onRTP(toolkit::Buffer::Ptr rtp, bool is_key = false) override; +}; + +} //namespace mediakit + +#endif//ENABLE_RTPPROXY +#endif //ZLMEDIAKIT_RTPCACHE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpProcess.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpProcess.h new file mode 100644 index 00000000..b680936c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpProcess.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPPROCESS_H +#define ZLMEDIAKIT_RTPPROCESS_H + +#if defined(ENABLE_RTPPROXY) +#include "ProcessInterface.h" +#include "Rtcp/RtcpContext.h" +#include "Common/MultiMediaSourceMuxer.h" + +namespace mediakit { + +class RtpProcess final : public RtcpContextForRecv, public toolkit::SockInfo, public MediaSinkInterface, public MediaSourceEventInterceptor, public std::enable_shared_from_this{ +public: + using Ptr = std::shared_ptr; + friend class RtpProcessHelper; + RtpProcess(const std::string &stream_id); + ~RtpProcess(); + enum OnlyTrack { kAll = 0, kOnlyAudio = 1, kOnlyVideo = 2 }; + + /** + * 输入rtp + * @param is_udp 是否为udp模式 + * @param sock 本地监听的socket + * @param data rtp数据指针 + * @param len rtp数据长度 + * @param addr 数据源地址 + * @param dts_out 解析出最新的dts + * @return 是否解析成功 + */ + bool inputRtp(bool is_udp, const toolkit::Socket::Ptr &sock, const char *data, size_t len, const struct sockaddr *addr , uint64_t *dts_out = nullptr); + + /** + * 是否超时,用于超时移除对象 + */ + bool alive(); + + /** + * 超时时被RtpSelector移除时触发 + */ + void onDetach(); + + /** + * 设置onDetach事件回调 + */ + void setOnDetach(std::function cb); + + /** + * 设置onDetach事件回调,false检查RTP超时,true停止 + */ + void setStopCheckRtp(bool is_check=false); + + /** + * 设置为单track,单音频/单视频时可以加快媒体注册速度 + * 请在inputRtp前调用此方法,否则可能会是空操作 + */ + void setOnlyTrack(OnlyTrack only_track); + + /** + * flush输出缓存 + */ + void flush() override; + + /// SockInfo override + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + std::string getIdentifier() const override; + +protected: + bool inputFrame(const Frame::Ptr &frame) override; + bool addTrack(const Track::Ptr & track) override; + void addTrackCompleted() override; + void resetTracks() override {}; + + //// MediaSourceEvent override //// + MediaOriginType getOriginType(MediaSource &sender) const override; + std::string getOriginUrl(MediaSource &sender) const override; + std::shared_ptr getOriginSock(MediaSource &sender) const override; + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + float getLossRate(MediaSource &sender, TrackType type) override; + +private: + void emitOnPublish(); + void doCachedFunc(); + +private: + OnlyTrack _only_track = kAll; + std::string _auth_err; + uint64_t _dts = 0; + uint64_t _total_bytes = 0; + std::unique_ptr _addr; + toolkit::Socket::Ptr _sock; + MediaInfo _media_info; + toolkit::Ticker _last_frame_time; + std::function _on_detach; + std::shared_ptr _save_file_rtp; + std::shared_ptr _save_file_video; + ProcessInterface::Ptr _process; + MultiMediaSourceMuxer::Ptr _muxer; + std::atomic_bool _stop_rtp_check{false}; + toolkit::Ticker _last_check_alive; + std::recursive_mutex _func_mtx; + std::deque > _cached_func; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPPROCESS_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSelector.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSelector.h new file mode 100644 index 00000000..4f46e8dc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSelector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPSELECTOR_H +#define ZLMEDIAKIT_RTPSELECTOR_H + +#if defined(ENABLE_RTPPROXY) +#include +#include +#include +#include "RtpProcess.h" +#include "Common/MediaSource.h" + +namespace mediakit{ + +class RtpSelector; +class RtpProcessHelper : public MediaSourceEvent , public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + RtpProcessHelper(const std::string &stream_id, const std::weak_ptr &parent); + ~RtpProcessHelper(); + void attachEvent(); + RtpProcess::Ptr & getProcess(); + +protected: + // 通知其停止推流 + bool close(MediaSource &sender) override; + +private: + std::string _stream_id; + RtpProcess::Ptr _process; + std::weak_ptr _parent; +}; + +class RtpSelector : public std::enable_shared_from_this{ +public: + class ProcessExisted : public std::runtime_error { + public: + template + ProcessExisted(T && ...args) : std::runtime_error(std::forward(args)...) {} + }; + + static bool getSSRC(const char *data,size_t data_len, uint32_t &ssrc); + static RtpSelector &Instance(); + + /** + * 清空所有对象 + */ + void clear(); + + /** + * 获取一个rtp处理器 + * @param stream_id 流id + * @param makeNew 不存在时是否新建, 该参数为true时,必须确保之前未创建同名对象 + * @return rtp处理器 + */ + RtpProcess::Ptr getProcess(const std::string &stream_id, bool makeNew); + + /** + * 删除rtp处理器 + * @param stream_id 流id + * @param ptr rtp处理器指针 + */ + void delProcess(const std::string &stream_id, const RtpProcess *ptr); + + void addStreamReplace(const std::string &stream_id, const std::string &stream_replace); + +private: + void onManager(); + void createTimer(); + void delStreamReplace(const std::string &stream_id); + +private: + toolkit::Timer::Ptr _timer; + std::recursive_mutex _mtx_map; + std::unordered_map _map_rtp_process; + std::unordered_map _map_stream_replace; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSELECTOR_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSender.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSender.h new file mode 100644 index 00000000..0902d12d --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSender.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPSENDER_H +#define ZLMEDIAKIT_RTPSENDER_H +#if defined(ENABLE_RTPPROXY) +#include "PSEncoder.h" +#include "Extension/CommonRtp.h" +#include "Rtcp/RtcpContext.h" +#include "Common/MediaSource.h" +#include "Common/MediaSink.h" + +namespace mediakit{ + +class RtpSession; + +//rtp发送客户端,支持发送GB28181协议 +class RtpSender final : public MediaSinkInterface, public std::enable_shared_from_this{ +public: + using Ptr = std::shared_ptr; + + RtpSender(toolkit::EventPoller::Ptr poller = nullptr); + ~RtpSender() override; + + /** + * 开始发送ps-rtp包 + * @param args 发送参数 + * @param cb 连接目标端口是否成功的回调 + */ + void startSend(const MediaSourceEvent::SendRtpArgs &args, const std::function &cb); + + /** + * 输入帧数据 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 刷新输出frame缓存 + */ + void flush() override; + + /** + * 添加track,内部会调用Track的clone方法 + * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 + * @param track + */ + virtual bool addTrack(const Track::Ptr & track) override; + + /** + * 添加所有Track完毕 + */ + virtual void addTrackCompleted() override; + + /** + * 重置track + */ + virtual void resetTracks() override; + + void setOnClose(std::function on_close); + +private: + //合并写输出 + void onFlushRtpList(std::shared_ptr > rtp_list); + //udp/tcp连接成功回调 + void onConnect(); + //异常断开socket事件 + void onErr(const toolkit::SockException &ex); + void createRtcpSocket(); + void onRecvRtcp(RtcpHeader *rtcp); + void onSendRtpUdp(const toolkit::Buffer::Ptr &buf, bool check); + void onClose(const toolkit::SockException &ex); + +private: + bool _is_connect = false; + MediaSourceEvent::SendRtpArgs _args; + toolkit::Socket::Ptr _socket_rtp; + toolkit::Socket::Ptr _socket_rtcp; + toolkit::EventPoller::Ptr _poller; + MediaSinkInterface::Ptr _interface; + std::shared_ptr _rtcp_context; + toolkit::Ticker _rtcp_send_ticker; + toolkit::Ticker _rtcp_recv_ticker; + std::shared_ptr _rtp_session; + std::function _on_close; +}; + +}//namespace mediakit +#endif// defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSENDER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpServer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpServer.h new file mode 100644 index 00000000..3654828e --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpServer.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPSERVER_H +#define ZLMEDIAKIT_RTPSERVER_H + +#if defined(ENABLE_RTPPROXY) +#include +#include "Network/Socket.h" +#include "Network/TcpServer.h" +#include "Network/UdpServer.h" +#include "RtpSession.h" + +namespace mediakit { + +class RtcpHelper; + +/** + * RTP服务器,支持UDP/TCP + */ +class RtpServer : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + using onRecv = std::function; + enum TcpMode { NONE = 0, PASSIVE, ACTIVE }; + + ~RtpServer(); + + /** + * 开启服务器,可能抛异常 + * @param local_port 本地端口,0时为随机端口 + * @param stream_id 流id,置空则使用ssrc + * @param tcp_mode tcp服务模式 + * @param local_ip 绑定的本地网卡ip + * @param re_use_port 是否设置socket为re_use属性 + * @param ssrc 指定的ssrc + * @param multiplex 多路复用 + */ + void start(uint16_t local_port, const std::string &stream_id = "", TcpMode tcp_mode = PASSIVE, + const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false); + + /** + * 连接到tcp服务(tcp主动模式) + * @param url 服务器地址 + * @param port 服务器端口 + * @param cb 连接服务器是否成功的回调 + */ + void connectToServer(const std::string &url, uint16_t port, const std::function &cb); + + /** + * 获取绑定的本地端口 + */ + uint16_t getPort(); + + /** + * 设置RtpProcess onDetach事件回调 + */ + void setOnDetach(std::function cb); + + /** + * 更新ssrc + */ + void updateSSRC(uint32_t ssrc); + +private: + // tcp主动模式连接服务器成功回调 + void onConnect(); + +protected: + toolkit::Socket::Ptr _rtp_socket; + toolkit::UdpServer::Ptr _udp_server; + toolkit::TcpServer::Ptr _tcp_server; + std::shared_ptr _ssrc; + std::shared_ptr _rtcp_helper; + std::function _on_cleanup; + + int _only_track = 0; + //用于tcp主动模式 + TcpMode _tcp_mode = NONE; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSERVER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSession.h new file mode 100644 index 00000000..2bff4f5f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSession.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPSESSION_H +#define ZLMEDIAKIT_RTPSESSION_H + +#if defined(ENABLE_RTPPROXY) + +#include "Network/Session.h" +#include "RtpSplitter.h" +#include "RtpProcess.h" +#include "Util/TimeTicker.h" + +namespace mediakit{ + +class RtpSession : public toolkit::Session, public RtpSplitter, public MediaSourceEvent { +public: + static const std::string kStreamID; + static const std::string kSSRC; + static const std::string kOnlyTrack; + static const std::string kUdpRecvBuffer; + + RtpSession(const toolkit::Socket::Ptr &sock); + ~RtpSession() override; + void onRecv(const toolkit::Buffer::Ptr &) override; + void onError(const toolkit::SockException &err) override; + void onManager() override; + void setParams(toolkit::mINI &ini); + void attachServer(const toolkit::Server &server) override; + +protected: + // 通知其停止推流 + bool close(MediaSource &sender) override; + // 收到rtp回调 + void onRtpPacket(const char *data, size_t len) override; + // RtpSplitter override + const char *onSearchPacketTail(const char *data, size_t len) override; + // 搜寻SSRC + const char *searchBySSRC(const char *data, size_t len); + // 搜寻PS包里的关键帧标头 + const char *searchByPsHeaderFlag(const char *data, size_t len); + +private: + bool _delay_close = false; + bool _is_udp = false; + bool _search_rtp = false; + bool _search_rtp_finished = false; + int _only_track = 0; + uint32_t _ssrc = 0; + toolkit::Ticker _ticker; + std::string _stream_id; + struct sockaddr_storage _addr; + RtpProcess::Ptr _process; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSESSION_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSplitter.h new file mode 100644 index 00000000..216f3021 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/RtpSplitter.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPSPLITTER_H +#define ZLMEDIAKIT_RTPSPLITTER_H + +#if defined(ENABLE_RTPPROXY) +#include "Http/HttpRequestSplitter.h" + +namespace mediakit{ + +class RtpSplitter : public HttpRequestSplitter{ +protected: + /** + * 收到rtp包回调 + * @param data RTP包数据指针 + * @param len RTP包数据长度 + */ + virtual void onRtpPacket(const char *data, size_t len) = 0; + +protected: + ssize_t onRecvHeader(const char *data, size_t len) override; + const char *onSearchPacketTail(const char *data, size_t len) override; + const char *onSearchPacketTail_l(const char *data, size_t len); + +private: + bool _is_ehome = true; + bool _is_rtsp_interleaved = true; + size_t _offset = 0; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtp/TSDecoder.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/TSDecoder.h new file mode 100644 index 00000000..849b358b --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtp/TSDecoder.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TSDECODER_H +#define ZLMEDIAKIT_TSDECODER_H + +#include "Util/logger.h" +#include "Http/HttpRequestSplitter.h" +#include "Decoder.h" + +#define TS_PACKET_SIZE 188 +#define TS_SYNC_BYTE 0x47 + +namespace mediakit { + +//TS包分割器,用于split一个一个的ts包 +class TSSegment : public HttpRequestSplitter { +public: + typedef std::function onSegment; + TSSegment(size_t size = TS_PACKET_SIZE) : _size(size){} + void setOnSegment(onSegment cb); + static bool isTSPacket(const char *data, size_t len); + +protected: + ssize_t onRecvHeader(const char *data, size_t len) override ; + const char *onSearchPacketTail(const char *data, size_t len) override ; + +private: + size_t _size; + onSegment _onSegment; +}; + +#if defined(ENABLE_HLS) +//ts解析器 +class TSDecoder : public Decoder { +public: + TSDecoder(); + ~TSDecoder(); + ssize_t input(const uint8_t* data, size_t bytes) override ; + +private: + TSSegment _ts_segment; + struct ts_demuxer_t* _demuxer_ctx = nullptr; +}; +#endif//defined(ENABLE_HLS) + +}//namespace mediakit +#endif //ZLMEDIAKIT_TSDECODER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpCodec.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpCodec.h new file mode 100644 index 00000000..9a8e3396 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpCodec.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPCODEC_H +#define ZLMEDIAKIT_RTPCODEC_H + +#include +#include "Extension/Frame.h" +#include "Util/RingBuffer.h" +#include "Rtsp/Rtsp.h" + +namespace mediakit { + +class RtpRing { +public: + using Ptr = std::shared_ptr; + using RingType = toolkit::RingBuffer; + + virtual ~RtpRing() = default; + + /** + * 设置rtp环形缓存 + * @param ring + */ + void setRtpRing(RingType::Ptr ring) { + _ring = std::move(ring); + } + + /** + * 输入rtp包 + * @param rtp rtp包 + * @param key_pos 是否为关键帧第一个rtp包 + * @return 是否为关键帧第一个rtp包 + */ + virtual bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { + if (_ring) { + _ring->write(rtp, key_pos); + } + return key_pos; + } + +protected: + RingType::Ptr _ring; +}; + +class RtpInfo { +public: + using Ptr = std::shared_ptr; + + RtpInfo(uint32_t ssrc, size_t mtu_size, uint32_t sample_rate, uint8_t pt, uint8_t interleaved, int track_index) { + if (ssrc == 0) { + ssrc = ((uint64_t) this) & 0xFFFFFFFF; + } + _pt = pt; + _ssrc = ssrc; + _mtu_size = mtu_size; + _sample_rate = sample_rate; + _interleaved = interleaved; + _track_index = track_index; + } + + //返回rtp负载最大长度 + size_t getMaxSize() const { + return _mtu_size - RtpPacket::kRtpHeaderSize; + } + + RtpPacket::Ptr makeRtp(TrackType type,const void *data, size_t len, bool mark, uint64_t stamp); + +private: + uint8_t _pt; + uint8_t _interleaved; + uint16_t _seq = 0; + uint32_t _ssrc; + uint32_t _sample_rate; + int _track_index; + size_t _mtu_size; +}; + +class RtpCodec : public RtpRing, public FrameDispatcher { +public: + using Ptr = std::shared_ptr; + + void setRtpInfo(uint32_t ssrc, size_t mtu_size, uint32_t sample_rate, uint8_t pt, uint8_t interleaved = 0, int track_index = 0) { + _rtp_info.reset(new RtpInfo(ssrc, mtu_size, sample_rate, pt, interleaved, track_index)); + } + + RtpInfo &getRtpInfo() { return *_rtp_info; } + + enum { + RTP_ENCODER_PKT_DUR_MS = 1 // 主要应用于g711 rtp 打包器每个包的时间长度,option_value 为int*, option_len 为4 + }; + /** + * @brief 设置rtp打包器与解包器的相关参数,主要应用与g711 rtp 打包器,使用方法类似setsockopt + * + * @param opt 设置的选项 + * @param param 设置的参数 + */ + virtual void setOpt(int opt, const toolkit::Any ¶m) {}; + +private: + std::unique_ptr _rtp_info; +}; + +}//namespace mediakit + + +#endif //ZLMEDIAKIT_RTPCODEC_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpMultiCaster.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpMultiCaster.h new file mode 100644 index 00000000..1b9aa1bf --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpMultiCaster.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTSP_RTPBROADCASTER_H_ +#define SRC_RTSP_RTPBROADCASTER_H_ + +#include +#include +#include +#include +#include "RtspMediaSource.h" +#include "Network/Socket.h" + +namespace mediakit{ + +class MultiCastAddressMaker { +public: + static MultiCastAddressMaker& Instance(); + static bool isMultiCastAddress(uint32_t addr); + static std::string toString(uint32_t addr); + + std::shared_ptr obtain(uint32_t max_try = 10); + +private: + MultiCastAddressMaker() = default; + void release(uint32_t addr); + +private: + uint32_t _addr = 0; + std::recursive_mutex _mtx; + std::unordered_set _used_addr; +}; + +class RtpMultiCaster { +public: + using Ptr = std::shared_ptr; + using onDetach = std::function; + + ~RtpMultiCaster(); + + static Ptr get(toolkit::SocketHelper &helper, const std::string &local_ip, const std::string &vhost, const std::string &app, const std::string &stream, uint32_t multicast_ip = 0, uint16_t video_port = 0, uint16_t audio_port = 0); + void setDetachCB(void *listener,const onDetach &cb); + + std::string getMultiCasterIP(); + uint16_t getMultiCasterPort(TrackType trackType); + +private: + RtpMultiCaster(toolkit::SocketHelper &helper, const std::string &local_ip, const std::string &vhost, const std::string &app, const std::string &stream, uint32_t multicast_ip, uint16_t video_port, uint16_t audio_port); + +private: + std::recursive_mutex _mtx; + toolkit::Socket::Ptr _udp_sock[2]; + std::shared_ptr _multicast_ip; + std::unordered_map _detach_map; + RtspMediaSource::RingType::RingReader::Ptr _rtp_reader; +}; + +}//namespace mediakit +#endif /* SRC_RTSP_RTPBROADCASTER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpReceiver.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpReceiver.h new file mode 100644 index 00000000..c2049855 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtpReceiver.h @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTPRECEIVER_H +#define ZLMEDIAKIT_RTPRECEIVER_H + +#include +#include +#include +#include "Rtsp/Rtsp.h" +#include "Extension/Frame.h" +// for NtpStamp +#include "Common/Stamp.h" +#include "Util/TimeTicker.h" + +namespace mediakit { + +template +class PacketSortor { +public: + static constexpr SEQ SEQ_MAX = (std::numeric_limits::max)(); + using iterator = typename std::map::iterator; + + virtual ~PacketSortor() = default; + + void setOnSort(std::function cb) { _cb = std::move(cb); } + + /** + * 清空状态 + */ + void clear() { + _started = false; + _ticker.resetTime(); + _pkt_sort_cache_map.clear(); + } + + /** + * 获取排序缓存长度 + */ + size_t getJitterSize() const { return _pkt_sort_cache_map.size(); } + + /** + * 输入并排序 + * @param seq 序列号 + * @param packet 包负载 + */ + void sortPacket(SEQ seq, T packet) { + _latest_seq = seq; + if (!_started) { + // 记录第一个seq + _started = true; + _last_seq_out = seq - 1; + } + auto next_seq = static_cast(_last_seq_out + 1); + if (seq == next_seq) { + // 收到下一个seq + output(seq, std::move(packet)); + // 清空连续包列表 + flushPacket(); + return; + } + + if (seq < next_seq && !mayLooped(next_seq, seq)) { + // 无回环风险, 过滤seq回退包 + return; + } + _pkt_sort_cache_map.emplace(seq, std::move(packet)); + + if (needForceFlush(seq)) { + forceFlush(next_seq); + } + } + + void flush() { + if (!_pkt_sort_cache_map.empty()) { + forceFlush(static_cast(_last_seq_out + 1)); + _pkt_sort_cache_map.clear(); + } + } + + void setParams(size_t max_buffer_size, size_t max_buffer_ms, size_t max_distance) { + _max_buffer_size = max_buffer_size; + _max_buffer_ms = max_buffer_ms; + _max_distance = max_distance; + } + +private: + SEQ distance(SEQ seq) { + SEQ ret; + auto next_seq = static_cast(_last_seq_out + 1); + if (seq > next_seq) { + ret = seq - next_seq; + } else { + ret = next_seq - seq; + } + if (ret > SEQ_MAX >> 1) { + return SEQ_MAX - ret; + } + return ret; + } + + bool needForceFlush(SEQ seq) { + return !_pkt_sort_cache_map.empty() && (_pkt_sort_cache_map.size() > _max_buffer_size || + distance(seq) > _max_distance || _ticker.elapsedTime() > _max_buffer_ms); + } + + //外部调用代码确保_pkt_sort_cache_map不为空 + void forceFlush(SEQ next_seq) { + // 寻找距离比next_seq大的最近的seq + auto it = _pkt_sort_cache_map.lower_bound(next_seq); + if (it == _pkt_sort_cache_map.end()) { + // 没有比next_seq更大的seq,应该是回环时丢包导致 + it = _pkt_sort_cache_map.begin(); + } + // 丢包无法恢复,把这个包当做next_seq + popIterator(it); + // 清空连续包列表 + flushPacket(); + // 删除距离next_seq太大的包 + for (auto it = _pkt_sort_cache_map.begin(); it != _pkt_sort_cache_map.end();) { + if (distance(it->first) > _max_distance) { + it = _pkt_sort_cache_map.erase(it); + } else { + ++it; + } + } + } + + bool mayLooped(SEQ last_seq, SEQ now_seq) { return last_seq > SEQ_MAX - _max_distance || now_seq < _max_distance; } + + void flushPacket() { + if (_pkt_sort_cache_map.empty()) { + return; + } + auto next_seq = static_cast(_last_seq_out + 1); + auto it = _pkt_sort_cache_map.lower_bound(next_seq); + if (!mayLooped(next_seq, next_seq)) { + // 无回环风险, 清空 < next_seq的值 + it = _pkt_sort_cache_map.erase(_pkt_sort_cache_map.begin(), it); + } + + while (it != _pkt_sort_cache_map.end()) { + // 找到下一个包 + if (it->first == static_cast(_last_seq_out + 1)) { + it = popIterator(it); + continue; + } + break; + } + } + + iterator popIterator(iterator it) { + output(it->first, std::move(it->second)); + return _pkt_sort_cache_map.erase(it); + } + + void output(SEQ seq, T packet) { + auto next_seq = static_cast(_last_seq_out + 1); + if (seq != next_seq) { + WarnL << "packet dropped: " << next_seq << " -> " << static_cast(seq - 1) + << ", latest seq: " << _latest_seq + << ", jitter buffer size: " << _pkt_sort_cache_map.size() + << ", jitter buffer ms: " << _ticker.elapsedTime(); + } + _last_seq_out = seq; + _cb(seq, std::move(packet)); + _ticker.resetTime(); + } + +private: + bool _started = false; + // 排序缓存最大保存数据长度,单位毫秒 + size_t _max_buffer_ms = 1000; + // 排序缓存最大保存数据个数 + size_t _max_buffer_size = 1024; + // seq最大跳跃距离 + size_t _max_distance = 256; + // 记录上次output至今的时间 + toolkit::Ticker _ticker; + // 最近输入的seq + SEQ _latest_seq = 0; + // 下次应该输出的SEQ + SEQ _last_seq_out = 0; + // pkt排序缓存,根据seq排序 + std::map _pkt_sort_cache_map; + // 回调 + std::function _cb; +}; + +class RtpTrack : public PacketSortor { +public: + class BadRtpException : public std::invalid_argument { + public: + template + BadRtpException(Type &&type) : invalid_argument(std::forward(type)) {} + }; + + RtpTrack(); + + void clear(); + uint32_t getSSRC() const; + RtpPacket::Ptr inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len); + void setNtpStamp(uint32_t rtp_stamp, uint64_t ntp_stamp_ms); + void setPayloadType(uint8_t pt); + +protected: + virtual void onRtpSorted(RtpPacket::Ptr rtp) {} + virtual void onBeforeRtpSorted(const RtpPacket::Ptr &rtp) {} + +private: + bool _disable_ntp = false; + uint8_t _pt = 0xFF; + uint32_t _ssrc = 0; + toolkit::Ticker _ssrc_alive; + NtpStamp _ntp_stamp; +}; + +class RtpTrackImp : public RtpTrack{ +public: + using OnSorted = std::function; + using BeforeSorted = std::function; + + void setOnSorted(OnSorted cb); + void setBeforeSorted(BeforeSorted cb); + +protected: + void onRtpSorted(RtpPacket::Ptr rtp) override; + void onBeforeRtpSorted(const RtpPacket::Ptr &rtp) override; + +private: + OnSorted _on_sorted; + BeforeSorted _on_before_sorted; +}; + +template +class RtpMultiReceiver { +public: + RtpMultiReceiver() { + int index = 0; + for (auto &track : _track) { + track.setOnSorted([this, index](RtpPacket::Ptr rtp) { + onRtpSorted(std::move(rtp), index); + }); + track.setBeforeSorted([this, index](const RtpPacket::Ptr &rtp) { + onBeforeRtpSorted(rtp, index); + }); + ++index; + } + } + + virtual ~RtpMultiReceiver() = default; + + /** + * 输入数据指针生成并排序rtp包 + * @param index track下标索引 + * @param type track类型 + * @param samplerate rtp时间戳基准时钟,视频为90000,音频为采样率 + * @param ptr rtp数据指针 + * @param len rtp数据指针长度 + * @return 解析成功返回true + */ + bool handleOneRtp(int index, TrackType type, int sample_rate, uint8_t *ptr, size_t len) { + assert(index < kCount && index >= 0); + return _track[index].inputRtp(type, sample_rate, ptr, len).operator bool(); + } + + /** + * 设置ntp时间戳,在收到rtcp sender report时设置 + * 如果rtp_stamp/sample_rate/ntp_stamp_ms都为0,那么采用rtp时间戳为ntp时间戳 + * @param index track下标索引 + * @param rtp_stamp rtp时间戳 + * @param ntp_stamp_ms ntp时间戳 + */ + void setNtpStamp(int index, uint32_t rtp_stamp, uint64_t ntp_stamp_ms) { + assert(index < kCount && index >= 0); + _track[index].setNtpStamp(rtp_stamp, ntp_stamp_ms); + } + + void setPayloadType(int index, uint8_t pt){ + assert(index < kCount && index >= 0); + _track[index].setPayloadType(pt); + } + + void clear() { + for (auto &track : _track) { + track.clear(); + } + } + + size_t getJitterSize(int index) const { + assert(index < kCount && index >= 0); + return _track[index].getJitterSize(); + } + + uint32_t getSSRC(int index) const { + assert(index < kCount && index >= 0); + return _track[index].getSSRC(); + } + +protected: + /** + * rtp数据包排序后输出 + * @param rtp rtp数据包 + * @param track_index track索引 + */ + virtual void onRtpSorted(RtpPacket::Ptr rtp, int index) {} + + /** + * 解析出rtp但还未排序 + * @param rtp rtp数据包 + * @param track_index track索引 + */ + virtual void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int index) {} + +private: + RtpTrackImp _track[kCount]; +}; + +using RtpReceiver = RtpMultiReceiver<2>; + +}//namespace mediakit + + +#endif //ZLMEDIAKIT_RTPRECEIVER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/Rtsp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/Rtsp.h new file mode 100644 index 00000000..fde5caea --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/Rtsp.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTSP_RTSP_H_ +#define RTSP_RTSP_H_ + +#include "Common/macros.h" +#include "Extension/Frame.h" +#include "Network/Socket.h" +#include +#include +#include +#include + +namespace mediakit { + +namespace Rtsp { +typedef enum { + RTP_Invalid = -1, + RTP_TCP = 0, + RTP_UDP = 1, + RTP_MULTICAST = 2, +} eRtpType; + +#define RTP_PT_MAP(XX) \ + XX(PCMU, TrackAudio, 0, 8000, 1, CodecG711U) \ + XX(GSM, TrackAudio, 3, 8000, 1, CodecInvalid) \ + XX(G723, TrackAudio, 4, 8000, 1, CodecInvalid) \ + XX(DVI4_8000, TrackAudio, 5, 8000, 1, CodecInvalid) \ + XX(DVI4_16000, TrackAudio, 6, 16000, 1, CodecInvalid) \ + XX(LPC, TrackAudio, 7, 8000, 1, CodecInvalid) \ + XX(PCMA, TrackAudio, 8, 8000, 1, CodecG711A) \ + XX(G722, TrackAudio, 9, 8000, 1, CodecInvalid) \ + XX(L16_Stereo, TrackAudio, 10, 44100, 2, CodecInvalid) \ + XX(L16_Mono, TrackAudio, 11, 44100, 1, CodecInvalid) \ + XX(QCELP, TrackAudio, 12, 8000, 1, CodecInvalid) \ + XX(CN, TrackAudio, 13, 8000, 1, CodecInvalid) \ + XX(MPA, TrackAudio, 14, 90000, 1, CodecInvalid) \ + XX(G728, TrackAudio, 15, 8000, 1, CodecInvalid) \ + XX(DVI4_11025, TrackAudio, 16, 11025, 1, CodecInvalid) \ + XX(DVI4_22050, TrackAudio, 17, 22050, 1, CodecInvalid) \ + XX(G729, TrackAudio, 18, 8000, 1, CodecInvalid) \ + XX(CelB, TrackVideo, 25, 90000, 1, CodecInvalid) \ + XX(JPEG, TrackVideo, 26, 90000, 1, CodecJPEG) \ + XX(nv, TrackVideo, 28, 90000, 1, CodecInvalid) \ + XX(H261, TrackVideo, 31, 90000, 1, CodecInvalid) \ + XX(MPV, TrackVideo, 32, 90000, 1, CodecInvalid) \ + XX(MP2T, TrackVideo, 33, 90000, 1, CodecInvalid) \ + XX(H263, TrackVideo, 34, 90000, 1, CodecInvalid) + +typedef enum { +#define ENUM_DEF(name, type, value, clock_rate, channel, codec_id) PT_##name = value, + RTP_PT_MAP(ENUM_DEF) +#undef ENUM_DEF + PT_MAX + = 128 +} PayloadType; + +}; // namespace Rtsp + +#pragma pack(push, 1) + +class RtpHeader { +public: +#if __BYTE_ORDER == __BIG_ENDIAN + // 版本号,固定为2 + uint32_t version : 2; + // padding + uint32_t padding : 1; + // 扩展 + uint32_t ext : 1; + // csrc + uint32_t csrc : 4; + // mark + uint32_t mark : 1; + // 负载类型 + uint32_t pt : 7; +#else + // csrc + uint32_t csrc : 4; + // 扩展 + uint32_t ext : 1; + // padding + uint32_t padding : 1; + // 版本号,固定为2 + uint32_t version : 2; + // 负载类型 + uint32_t pt : 7; + // mark + uint32_t mark : 1; +#endif + // 序列号 + uint32_t seq : 16; + // 时间戳 + uint32_t stamp; + // ssrc + uint32_t ssrc; + // 负载,如果有csrc和ext,前面为 4 * csrc + (4 + 4 * ext_len) + uint8_t payload; + +public: + // 返回csrc字段字节长度 + size_t getCsrcSize() const; + // 返回csrc字段首地址,不存在时返回nullptr + uint8_t *getCsrcData(); + + // 返回ext字段字节长度 + size_t getExtSize() const; + // 返回ext reserved值 + uint16_t getExtReserved() const; + // 返回ext段首地址,不存在时返回nullptr + uint8_t *getExtData(); + + // 返回有效负载指针,跳过csrc、ext + uint8_t *getPayloadData(); + // 返回有效负载总长度,不包括csrc、ext、padding + ssize_t getPayloadSize(size_t rtp_size) const; + // 打印调试信息 + std::string dumpString(size_t rtp_size) const; + +private: + // 返回有效负载偏移量 + size_t getPayloadOffset() const; + // 返回padding长度 + size_t getPaddingSize(size_t rtp_size) const; +}; + +#pragma pack(pop) + +// 此rtp为rtp over tcp形式,需要忽略前4个字节 +class RtpPacket : public toolkit::BufferRaw { +public: + using Ptr = std::shared_ptr; + enum { kRtpVersion = 2, kRtpHeaderSize = 12, kRtpTcpHeaderSize = 4 }; + + // 获取rtp头 + RtpHeader *getHeader(); + const RtpHeader *getHeader() const; + + // 打印调试信息 + std::string dumpString() const; + + // 主机字节序的seq + uint16_t getSeq() const; + uint32_t getStamp() const; + // 主机字节序的时间戳,已经转换为毫秒 + uint64_t getStampMS(bool ntp = true) const; + // 主机字节序的ssrc + uint32_t getSSRC() const; + // 有效负载,跳过csrc、ext + uint8_t *getPayload(); + // 有效负载长度,不包括csrc、ext、padding + size_t getPayloadSize() const; + + // 音视频类型 + TrackType type; + // 音频为采样率,视频一般为90000 + uint32_t sample_rate; + // ntp时间戳 + uint64_t ntp_stamp; + + int track_index; + + static Ptr create(); + +private: + friend class toolkit::ResourcePool_l; + RtpPacket() = default; + +private: + // 对象个数统计 + toolkit::ObjectStatistic _statistic; +}; + +class RtpPayload { +public: + static int getClockRate(int pt); + static TrackType getTrackType(int pt); + static int getAudioChannel(int pt); + static const char *getName(int pt); + static CodecId getCodecId(int pt); + +private: + RtpPayload() = delete; + ~RtpPayload() = delete; +}; + +class SdpTrack { +public: + using Ptr = std::shared_ptr; + std::string _t; + std::string _b; + uint16_t _port; + + float _duration = 0; + float _start = 0; + float _end = 0; + + std::map _other; + std::multimap _attr; + + std::string toString(uint16_t port = 0) const; + std::string getName() const; + std::string getControlUrl(const std::string &base_url) const; + +public: + int _pt = 0xff; + int _channel; + int _samplerate; + TrackType _type; + std::string _codec; + std::string _fmtp; + std::string _control; + +public: + bool _inited = false; + uint8_t _interleaved = 0; + uint16_t _seq = 0; + uint32_t _ssrc = 0; + // 时间戳,单位毫秒 + uint32_t _time_stamp = 0; +}; + +class SdpParser { +public: + using Ptr = std::shared_ptr; + + SdpParser() = default; + SdpParser(const std::string &sdp) { load(sdp); } + + void load(const std::string &sdp); + bool available() const; + SdpTrack::Ptr getTrack(TrackType type) const; + std::vector getAvailableTrack() const; + std::string toString() const; + std::string getControlUrl(const std::string &url) const; + +private: + std::vector _track_vec; +}; + +/** + * rtsp sdp基类 + */ +class Sdp { +public: + using Ptr = std::shared_ptr; + + /** + * 构造sdp + * @param sample_rate 采样率 + * @param payload_type pt类型 + */ + Sdp(uint32_t sample_rate, uint8_t payload_type) { + _sample_rate = sample_rate; + _payload_type = payload_type; + } + + virtual ~Sdp() = default; + + /** + * 获取sdp字符串 + * @return + */ + virtual std::string getSdp() const = 0; + + /** + * 获取pt + * @return + */ + uint8_t getPayloadType() const { return _payload_type; } + + /** + * 获取采样率 + * @return + */ + uint32_t getSampleRate() const { return _sample_rate; } + +private: + uint8_t _payload_type; + uint32_t _sample_rate; +}; + +/** + * sdp中除音视频外的其他描述部分 + */ +class TitleSdp : public Sdp { +public: + using Ptr = std::shared_ptr; + /** + * 构造title类型sdp + * @param dur_sec rtsp点播时长,0代表直播,单位秒 + * @param header 自定义sdp描述 + * @param version sdp版本 + */ + TitleSdp(float dur_sec = 0, const std::map &header = std::map(), int version = 0); + + std::string getSdp() const override { return _printer; } + + float getDuration() const { return _dur_sec; } + +private: + float _dur_sec = 0; + toolkit::_StrPrinter _printer; +}; + +// 创建rtp over tcp4个字节的头 +toolkit::Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved); +// 创建rtp-rtcp端口对 +void makeSockPair(std::pair &pair, const std::string &local_ip, bool re_use_port = false, bool is_udp = true); +// 十六进制方式打印ssrc +std::string printSSRC(uint32_t ui32Ssrc); + +bool isRtp(const char *buf, size_t size); +bool isRtcp(const char *buf, size_t size); + +} // namespace mediakit +#endif // RTSP_RTSP_H_ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspDemuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspDemuxer.h new file mode 100644 index 00000000..636b6986 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspDemuxer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTP_RTSPDEMUXER_H_ +#define SRC_RTP_RTSPDEMUXER_H_ + +#include +#include "Rtsp/RtpCodec.h" +#include "Common/MediaSink.h" + +namespace mediakit { + +class RtspDemuxer : public Demuxer { +public: + using Ptr = std::shared_ptr; + + /** + * 加载sdp + */ + void loadSdp(const std::string &sdp); + + /** + * 开始解复用 + * @param rtp rtp包 + * @return true 代表是i帧第一个rtp包 + */ + bool inputRtp(const RtpPacket::Ptr &rtp); + + /** + * 获取节目总时长 + * @return 节目总时长,单位秒 + */ + float getDuration() const; + +private: + void makeAudioTrack(const SdpTrack::Ptr &audio); + void makeVideoTrack(const SdpTrack::Ptr &video); + void loadSdp(const SdpParser &parser); + +private: + float _duration = 0; + AudioTrack::Ptr _audio_track; + VideoTrack::Ptr _video_track; + RtpCodec::Ptr _audio_rtp_decoder; + RtpCodec::Ptr _video_rtp_decoder; +}; + +} /* namespace mediakit */ + +#endif /* SRC_RTP_RTSPDEMUXER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSource.h new file mode 100644 index 00000000..11253817 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSource.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTSP_RTSPMEDIASOURCE_H_ +#define SRC_RTSP_RTSPMEDIASOURCE_H_ + +#include +#include +#include +#include +#include "Common/MediaSource.h" +#include "Common/PacketCache.h" +#include "Util/RingBuffer.h" + +#define RTP_GOP_SIZE 512 + +namespace mediakit { + +/** + * rtsp媒体源的数据抽象 + * rtsp有关键的两要素,分别是sdp、rtp包 + * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了 + * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp + */ +class RtspMediaSource : public MediaSource, public toolkit::RingDelegate, private PacketCache { +public: + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = toolkit::RingBuffer; + + /** + * 构造函数 + * @param vhost 虚拟主机名 + * @param app 应用名 + * @param stream_id 流id + * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + */ + RtspMediaSource(const MediaTuple& tuple, int ring_size = RTP_GOP_SIZE): MediaSource(RTSP_SCHEMA, tuple), _ring_size(ring_size) {} + + ~RtspMediaSource() override { flush(); } + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) override { + assert(_ring); + _ring->getInfoList(cb, on_change); + } + + bool broadcastMessage(const toolkit::Any &data) override { + assert(_ring); + _ring->sendMessage(data); + return true; + } + + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 获取该源的sdp + */ + const std::string &getSdp() const { + return _sdp; + } + + virtual RtspMediaSource::Ptr clone(const std::string& stream) { + return nullptr; + } + + /** + * 获取相应轨道的ssrc + */ + virtual uint32_t getSsrc(TrackType trackType) { + assert(trackType >= 0 && trackType < TrackMax); + auto &track = _tracks[trackType]; + if (!track) { + return 0; + } + return track->_ssrc; + } + + /** + * 获取相应轨道的seqence + */ + virtual uint16_t getSeqence(TrackType trackType) { + assert(trackType >= 0 && trackType < TrackMax); + auto &track = _tracks[trackType]; + if (!track) { + return 0; + } + return track->_seq; + } + + /** + * 获取相应轨道的时间戳,单位毫秒 + */ + uint32_t getTimeStamp(TrackType trackType) override; + + /** + * 更新时间戳 + */ + void setTimeStamp(uint32_t stamp) override; + + /** + * 设置sdp + */ + virtual void setSdp(const std::string &sdp); + + /** + * 输入rtp + * @param rtp rtp包 + * @param keyPos 该包是否为关键帧的第一个包 + */ + void onWrite(RtpPacket::Ptr rtp, bool keyPos) override; + + void clearCache() override{ + PacketCache::clearCache(); + _ring->clearCache(); + } + +private: + /** + * 批量flush rtp包时触发该函数 + * @param rtp_list rtp包列表 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > rtp_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + _ring->write(std::move(rtp_list), _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + int _ring_size; + std::string _sdp; + RingType::Ptr _ring; + SdpTrack::Ptr _tracks[TrackMax]; +}; + +} /* namespace mediakit */ + +#endif /* SRC_RTSP_RTSPMEDIASOURCE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceImp.h new file mode 100644 index 00000000..149bf43a --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceImp.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTSP_RTSPTORTMPMEDIASOURCE_H_ +#define SRC_RTSP_RTSPTORTMPMEDIASOURCE_H_ + +#include "RtspMediaSource.h" +#include "RtspDemuxer.h" +#include "Common/MultiMediaSourceMuxer.h" + +namespace mediakit { +class RtspDemuxer; +class RtspMediaSourceImp final : public RtspMediaSource, private TrackListener, public MultiMediaSourceMuxer::Listener { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param id 流id + * @param ringSize 环形缓存大小 + */ + RtspMediaSourceImp(const MediaTuple& tuple, int ringSize = RTP_GOP_SIZE); + + /** + * 设置sdp + */ + void setSdp(const std::string &strSdp) override; + + /** + * 输入rtp并解析 + */ + void onWrite(RtpPacket::Ptr rtp, bool key_pos) override; + + /** + * 获取观看总人数,包括(hls/rtsp/rtmp) + */ + int totalReaderCount() override { + return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); + } + + /** + * 设置协议转换选项 + */ + void setProtocolOption(const ProtocolOption &option); + + const ProtocolOption &getProtocolOption() const { + return _option; + } + + /** + * _demuxer触发的添加Track事件 + */ + bool addTrack(const Track::Ptr &track) override { + if (_muxer) { + if (_muxer->addTrack(track)) { + track->addDelegate(_muxer); + return true; + } + } + return false; + } + + /** + * _demuxer触发的Track添加完毕事件 + */ + void addTrackCompleted() override { + if (_muxer) { + _muxer->addTrackCompleted(); + } + } + + void resetTracks() override { + if (_muxer) { + _muxer->resetTracks(); + } + } + + /** + * _muxer触发的所有Track就绪的事件 + */ + void onAllTrackReady() override{ + _all_track_ready = true; + } + + /** + * 设置事件监听器 + * @param listener 监听器 + */ + void setListener(const std::weak_ptr &listener) override{ + if (_muxer) { + //_muxer对象不能处理的事件再给listener处理 + _muxer->setMediaListener(listener); + } else { + //未创建_muxer对象,事件全部给listener处理 + MediaSource::setListener(listener); + } + } + + RtspMediaSource::Ptr clone(const std::string& stream) override; +private: + bool _all_track_ready = false; + ProtocolOption _option; + RtspDemuxer::Ptr _demuxer; + MultiMediaSourceMuxer::Ptr _muxer; +}; +} /* namespace mediakit */ + +#endif /* SRC_RTSP_RTSPTORTMPMEDIASOURCE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceMuxer.h new file mode 100644 index 00000000..1954344f --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMediaSourceMuxer.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTSPMEDIASOURCEMUXER_H +#define ZLMEDIAKIT_RTSPMEDIASOURCEMUXER_H + +#include "RtspMuxer.h" +#include "Rtsp/RtspMediaSource.h" + +namespace mediakit { + +class RtspMediaSourceMuxer final : public RtspMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + RtspMediaSourceMuxer(const MediaTuple& tuple, + const ProtocolOption &option, + const TitleSdp::Ptr &title = nullptr) : RtspMuxer(title) { + _option = option; + _media_src = std::make_shared(tuple); + getRtpRing()->setDelegate(_media_src); + } + + ~RtspMediaSourceMuxer() override { RtspMuxer::flush(); } + + void setListener(const std::weak_ptr &listener){ + setDelegate(listener); + _media_src->setListener(shared_from_this()); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void setTimeStamp(uint32_t stamp){ + _media_src->setTimeStamp(stamp); + } + + void addTrackCompleted() override { + RtspMuxer::addTrackCompleted(); + _media_src->setSdp(getSdp()); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = _option.rtsp_demand ? size : true; + if (!size && _option.rtsp_demand) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache && _option.rtsp_demand) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled || !_option.rtsp_demand) { + return RtspMuxer::inputFrame(frame); + } + return false; + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _option.rtsp_demand ? (_clear_cache ? true : _enabled) : true; + } + +private: + bool _enabled = true; + bool _clear_cache = false; + ProtocolOption _option; + RtspMediaSource::Ptr _media_src; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_RTSPMEDIASOURCEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMuxer.h new file mode 100644 index 00000000..00f356e0 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspMuxer.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTSPMUXER_H +#define ZLMEDIAKIT_RTSPMUXER_H + +#include "Extension/Frame.h" +#include "Common/MediaSink.h" +#include "Common/Stamp.h" +#include "RtpCodec.h" + +namespace mediakit{ + +class RingDelegateHelper : public toolkit::RingDelegate { +public: + using onRtp = std::function ; + + RingDelegateHelper(onRtp on_rtp) { + _on_rtp = std::move(on_rtp); + } + + void onWrite(RtpPacket::Ptr in, bool is_key) override { + _on_rtp(std::move(in), is_key); + } + +private: + onRtp _on_rtp; +}; + +/** +* rtsp生成器 +*/ +class RtspMuxer : public MediaSinkInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + */ + RtspMuxer(const TitleSdp::Ptr &title = nullptr); + + /** + * 获取完整的SDP字符串 + * @return SDP字符串 + */ + std::string getSdp() ; + + /** + * 获取rtp环形缓存 + * @return + */ + RtpRing::RingType::Ptr getRtpRing() const; + + /** + * 添加ready状态的track + */ + bool addTrack(const Track::Ptr & track) override; + + /** + * 写入帧数据 + * @param frame 帧 + */ + bool inputFrame(const Frame::Ptr &frame) override; + + /** + * 刷新输出所有frame缓存 + */ + void flush() override; + + /** + * 重置所有track + */ + void resetTracks() override ; + +private: + void onRtp(RtpPacket::Ptr in, bool is_key); + void trySyncTrack(); + +private: + bool _live = true; + bool _track_existed[2] = { false, false }; + + uint8_t _index {0}; + uint64_t _ntp_stamp_start; + std::string _sdp; + + struct TrackInfo { + Stamp stamp; + uint32_t rtp_stamp { 0 }; + uint64_t ntp_stamp { 0 }; + RtpCodec::Ptr encoder; + }; + + std::unordered_map _tracks; + RtpRing::RingType::Ptr _rtpRing; + RtpRing::RingType::Ptr _rtpInterceptor; +}; + + +} /* namespace mediakit */ + +#endif //ZLMEDIAKIT_RTSPMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayer.h new file mode 100644 index 00000000..66882c20 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayer.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTSPPLAYER_RTSPPLAYER_H_TXT_ +#define SRC_RTSPPLAYER_RTSPPLAYER_H_TXT_ + +#include +#include +#include "Util/TimeTicker.h" +#include "Poller/Timer.h" +#include "Network/Socket.h" +#include "Player/PlayerBase.h" +#include "Network/TcpClient.h" +#include "RtspSplitter.h" +#include "RtpReceiver.h" +#include "Rtcp/RtcpContext.h" + +namespace mediakit { + +//实现了rtsp播放器协议部分的功能,及数据接收功能 +class RtspPlayer : public PlayerBase, public toolkit::TcpClient, public RtspSplitter, public RtpReceiver { +public: + using Ptr = std::shared_ptr; + + RtspPlayer(const toolkit::EventPoller::Ptr &poller); + ~RtspPlayer() override; + + void play(const std::string &strUrl) override; + void pause(bool pause) override; + void speed(float speed) override; + void teardown() override; + float getPacketLossRate(TrackType type) const override; + +protected: + //派生类回调函数 + virtual bool onCheckSDP(const std::string &sdp) = 0; + virtual void onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) = 0; + uint32_t getProgressMilliSecond() const; + void seekToMilliSecond(uint32_t ms); + + /** + * 收到完整的rtsp包回调,包括sdp等content数据 + * @param parser rtsp包 + */ + void onWholeRtspPacket(Parser &parser) override ; + + /** + * 收到rtp包回调 + * @param data + * @param len + */ + void onRtpPacket(const char *data,size_t len) override ; + + /** + * rtp数据包排序后输出 + * @param rtp rtp数据包 + * @param track_idx track索引 + */ + void onRtpSorted(RtpPacket::Ptr rtp, int track_idx) override; + + /** + * 解析出rtp但还未排序 + * @param rtp rtp数据包 + * @param track_index track索引 + */ + void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; + + /** + * 收到RTCP包回调 + * @param track_idx track索引 + * @param track sdp相关信息 + * @param data rtcp内容 + * @param len rtcp内容长度 + */ + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len); + + /////////////TcpClient override///////////// + void onConnect(const toolkit::SockException &err) override; + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onError(const toolkit::SockException &ex) override; + +private: + void onPlayResult_l(const toolkit::SockException &ex , bool handshake_done); + + int getTrackIndexByInterleaved(int interleaved) const; + int getTrackIndexByTrackType(TrackType track_type) const; + + void handleResSETUP(const Parser &parser, unsigned int track_idx); + void handleResDESCRIBE(const Parser &parser); + bool handleAuthenticationFailure(const std::string &wwwAuthenticateParamsStr); + void handleResPAUSE(const Parser &parser, int type); + bool handleResponse(const std::string &cmd, const Parser &parser); + + void sendOptions(); + void sendSetup(unsigned int track_idx); + void sendPause(int type , uint32_t ms); + void sendDescribe(); + void sendTeardown(); + void sendKeepAlive(); + void sendRtspRequest(const std::string &cmd, const std::string &url ,const StrCaseMap &header = StrCaseMap()); + void sendRtspRequest(const std::string &cmd, const std::string &url ,const std::initializer_list &header); + void createUdpSockIfNecessary(int track_idx); + +private: + //是否为性能测试模式 + bool _benchmark_mode = false; + //轮流发送rtcp与GET_PARAMETER保活 + bool _send_rtcp[2] = {true, true}; + + std::string _play_url; + std::vector _sdp_track; + std::function _on_response; + //RTP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtp_sock[2]; + //RTCP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtcp_sock[2]; + + //rtsp鉴权相关 + std::string _md5_nonce; + std::string _realm; + //rtsp info + std::string _session_id; + uint32_t _cseq_send = 1; + std::string _content_base; + std::string _control_url; + Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; + + //当前rtp时间戳 + uint32_t _stamp[2] = {0, 0}; + + //超时功能实现 + toolkit::Ticker _rtp_recv_ticker; + std::shared_ptr _play_check_timer; + std::shared_ptr _rtp_check_timer; + //服务器支持的命令 + std::set _supported_cmd; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + toolkit::Ticker _rtcp_send_ticker[2]; + //统计rtp并发送rtcp + std::vector _rtcp_context; +}; + +} /* namespace mediakit */ +#endif /* SRC_RTSPPLAYER_RTSPPLAYER_H_TXT_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayerImp.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayerImp.h new file mode 100644 index 00000000..6c8818e3 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPlayerImp.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_RTP_RTPPARSERTESTER_H_ +#define SRC_RTP_RTPPARSERTESTER_H_ + +#include +#include +#include +#include "RtspPlayer.h" +#include "RtspDemuxer.h" +#include "RtspMediaSource.h" + +namespace mediakit { + +class RtspPlayerImp : public PlayerImp ,private TrackListener { +public: + using Ptr = std::shared_ptr; + using Super = PlayerImp; + + RtspPlayerImp(const toolkit::EventPoller::Ptr &poller) : Super(poller) {} + + ~RtspPlayerImp() override { + DebugL; + } + + float getProgress() const override { + if (getDuration() > 0) { + return getProgressMilliSecond() / (getDuration() * 1000); + } + return PlayerBase::getProgress(); + } + + uint32_t getProgressPos() const override { + if (getDuration() > 0) { + return getProgressMilliSecond(); + } + return PlayerBase::getProgressPos(); + } + + void seekTo(float fProgress) override { + fProgress = MAX(float(0), MIN(fProgress, float(1.0))); + seekToMilliSecond((uint32_t) (fProgress * getDuration() * 1000)); + } + + void seekTo(uint32_t seekPos) override { + uint32_t pos = MAX(float(0), MIN(seekPos, getDuration())) * 1000; + seekToMilliSecond(pos); + } + + float getDuration() const override; + + std::vector getTracks(bool ready = true) const override; + +private: + //派生类回调函数 + bool onCheckSDP(const std::string &sdp) override; + + void onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) override; + + void onPlayResult(const toolkit::SockException &ex) override; + + bool addTrack(const Track::Ptr &track) override { return true; } + + void addTrackCompleted() override; + +private: + RtspDemuxer::Ptr _demuxer; + RtspMediaSource::Ptr _rtsp_media_src; +}; + +} /* namespace mediakit */ + +#endif /* SRC_RTP_RTPPARSERTESTER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPusher.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPusher.h new file mode 100644 index 00000000..d9bfeeea --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspPusher.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTSPPUSHER_H +#define ZLMEDIAKIT_RTSPPUSHER_H + +#include +#include +#include "RtspMediaSource.h" +#include "Poller/Timer.h" +#include "Network/Socket.h" +#include "Network/TcpClient.h" +#include "RtspSplitter.h" +#include "Pusher/PusherBase.h" +#include "Rtcp/RtcpContext.h" + +namespace mediakit { + +class RtspPusher : public toolkit::TcpClient, public RtspSplitter, public PusherBase { +public: + using Ptr = std::shared_ptr; + RtspPusher(const toolkit::EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src); + ~RtspPusher() override; + void publish(const std::string &url) override; + void teardown() override; + +protected: + //for Tcpclient override + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onConnect(const toolkit::SockException &err) override; + void onError(const toolkit::SockException &ex) override; + + //RtspSplitter override + void onWholeRtspPacket(Parser &parser) override ; + void onRtpPacket(const char *data,size_t len) override; + + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len); + +private: + void onPublishResult_l(const toolkit::SockException &ex, bool handshake_done); + + void sendAnnounce(); + void sendSetup(unsigned int track_idx); + void sendRecord(); + void sendOptions(); + void sendTeardown(); + + void handleResAnnounce(const Parser &parser); + void handleResSetup(const Parser &parser, unsigned int track_idx); + bool handleAuthenticationFailure(const std::string ¶ms_str); + + int getTrackIndexByInterleaved(int interleaved) const; + int getTrackIndexByTrackType(TrackType type) const; + + void sendRtpPacket(const RtspMediaSource::RingDataType & pkt) ; + void sendRtspRequest(const std::string &cmd, const std::string &url ,const StrCaseMap &header = StrCaseMap(),const std::string &sdp = "" ); + void sendRtspRequest(const std::string &cmd, const std::string &url ,const std::initializer_list &header,const std::string &sdp = ""); + + void createUdpSockIfNecessary(int track_idx); + void setSocketFlags(); + void updateRtcpContext(const RtpPacket::Ptr &pkt); + +private: + unsigned int _cseq = 1; + Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; + + //rtsp鉴权相关 + std::string _nonce; + std::string _realm; + std::string _url; + std::string _session_id; + std::string _content_base; + SdpParser _sdp_parser; + std::vector _track_vec; + //RTP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtp_sock[2]; + //RTCP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtcp_sock[2]; + //超时功能实现 + toolkit::Timer::Ptr _publish_timer; + //心跳定时器 + toolkit::Timer::Ptr _beat_timer; + std::weak_ptr _push_src; + RtspMediaSource::RingType::RingReader::Ptr _rtsp_reader; + std::function _on_res_func; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + toolkit::Ticker _rtcp_send_ticker[2]; + //统计rtp并发送rtcp + std::vector _rtcp_context; +}; + +using RtspPusherImp = PusherImp; + +} /* namespace mediakit */ +#endif //ZLMEDIAKIT_RTSPPUSHER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSession.h new file mode 100644 index 00000000..df35a0cc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSession.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SESSION_RTSPSESSION_H_ +#define SESSION_RTSPSESSION_H_ + +#include +#include +#include +#include "Network/Session.h" +#include "RtspSplitter.h" +#include "RtpReceiver.h" +#include "Rtcp/RtcpContext.h" +#include "RtspMediaSource.h" +#include "RtspMediaSourceImp.h" +#include "RtpMultiCaster.h" + +namespace mediakit { + +using BufferRtp = toolkit::BufferOffset; +class RtspSession : public toolkit::Session, public RtspSplitter, public RtpReceiver, public MediaSourceEvent { +public: + using Ptr = std::shared_ptr; + using onGetRealm = std::function; + //encrypted为true是则表明是md5加密的密码,否则是明文密码 + //在请求明文密码时如果提供md5密码者则会导致认证失败 + using onAuth = std::function; + + RtspSession(const toolkit::Socket::Ptr &sock); + ////Session override//// + void onRecv(const toolkit::Buffer::Ptr &buf) override; + void onError(const toolkit::SockException &err) override; + void onManager() override; + +protected: + /////RtspSplitter override///// + //收到完整的rtsp包回调,包括sdp等content数据 + void onWholeRtspPacket(Parser &parser) override; + //收到rtp包回调 + void onRtpPacket(const char *data, size_t len) override; + //从rtsp头中获取Content长度 + ssize_t getContentLength(Parser &parser) override; + + ////RtpReceiver override//// + void onRtpSorted(RtpPacket::Ptr rtp, int track_idx) override; + void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; + + ///////MediaSourceEvent override/////// + // 关闭 + bool close(MediaSource &sender) override; + // 播放总人数 + int totalReaderCount(MediaSource &sender) override; + // 获取媒体源类型 + MediaOriginType getOriginType(MediaSource &sender) const override; + // 获取媒体源url或者文件路径 + std::string getOriginUrl(MediaSource &sender) const override; + // 获取媒体源客户端相关信息 + std::shared_ptr getOriginSock(MediaSource &sender) const override; + // 由于支持断连续推,存在OwnerPoller变更的可能 + toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override; + + /////Session override//// + ssize_t send(toolkit::Buffer::Ptr pkt) override; + //收到RTCP包回调 + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len); + + //回复客户端 + virtual bool sendRtspResponse(const std::string &res_code, const StrCaseMap &header = StrCaseMap(), const std::string &sdp = "", const char *protocol = "RTSP/1.0"); + +protected: + //url解析后保存的相关信息 + MediaInfo _media_info; + + ////////RTP over udp_multicast//////// + //共享的rtp组播对象 + RtpMultiCaster::Ptr _multicaster; + + //Session号 + std::string _sessionid; + + uint32_t _multicast_ip = 0; + uint16_t _multicast_video_port = 0; + uint16_t _multicast_audio_port = 0; + +private: + //处理options方法,获取服务器能力 + void handleReq_Options(const Parser &parser); + //处理describe方法,请求服务器rtsp sdp信息 + void handleReq_Describe(const Parser &parser); + //处理ANNOUNCE方法,请求推流,附带sdp + void handleReq_ANNOUNCE(const Parser &parser); + //处理record方法,开始推流 + void handleReq_RECORD(const Parser &parser); + //处理setup方法,播放和推流协商rtp传输方式用 + void handleReq_Setup(const Parser &parser); + //处理play方法,开始或恢复播放 + void handleReq_Play(const Parser &parser); + //处理pause方法,暂停播放 + void handleReq_Pause(const Parser &parser); + //处理teardown方法,结束播放 + void handleReq_Teardown(const Parser &parser); + //处理Get方法,rtp over http才用到 + void handleReq_Get(const Parser &parser); + //处理Post方法,rtp over http才用到 + void handleReq_Post(const Parser &parser); + //处理SET_PARAMETER、GET_PARAMETER方法,一般用于心跳 + void handleReq_SET_PARAMETER(const Parser &parser); + //rtsp资源未找到 + void send_StreamNotFound(); + //不支持的传输模式 + void send_UnsupportedTransport(); + //会话id错误 + void send_SessionNotFound(); + //一般rtsp服务器打开端口失败时触发 + void send_NotAcceptable(); + //获取track下标 + int getTrackIndexByTrackType(TrackType type); + int getTrackIndexByControlUrl(const std::string &control_url); + int getTrackIndexByInterleaved(int interleaved); + //一般用于接收udp打洞包,也用于rtsp推流 + void onRcvPeerUdpData(int interleaved, const toolkit::Buffer::Ptr &buf, const struct sockaddr_storage &addr); + //配合onRcvPeerUdpData使用 + void startListenPeerUdpData(int track_idx); + ////rtsp专有认证相关//// + //认证成功 + void onAuthSuccess(); + //认证失败 + void onAuthFailed(const std::string &realm, const std::string &why, bool close = true); + //开始走rtsp专有认证流程 + void onAuthUser(const std::string &realm, const std::string &authorization); + //校验base64方式的认证加密 + void onAuthBasic(const std::string &realm, const std::string &auth_base64); + //校验md5方式的认证加密 + void onAuthDigest(const std::string &realm, const std::string &auth_md5); + //触发url鉴权事件 + void emitOnPlay(); + //发送rtp给客户端 + void sendRtpPacket(const RtspMediaSource::RingDataType &pkt); + //触发rtcp发送 + void updateRtcpContext(const RtpPacket::Ptr &rtp); + //回复客户端 + bool sendRtspResponse(const std::string &res_code, const std::initializer_list &header, const std::string &sdp = "", const char *protocol = "RTSP/1.0"); + + //设置socket标志 + void setSocketFlags(); + +private: + //是否已经触发on_play事件 + bool _emit_on_play = false; + bool _send_sr_rtcp[2] = {true, true}; + //断连续推延时 + uint32_t _continue_push_ms = 0; + //推流或拉流客户端采用的rtp传输方式 + Rtsp::eRtpType _rtp_type = Rtsp::RTP_Invalid; + //收到的seq,回复时一致 + int _cseq = 0; + //消耗的总流量 + uint64_t _bytes_usage = 0; + //ContentBase + std::string _content_base; + //记录是否需要rtsp专属鉴权,防止重复触发事件 + std::string _rtsp_realm; + //登录认证 + std::string _auth_nonce; + //用于判断客户端是否超时 + toolkit::Ticker _alive_ticker; + + //rtsp推流相关绑定的源 + RtspMediaSourceImp::Ptr _push_src; + //推流器所有权 + std::shared_ptr _push_src_ownership; + //rtsp播放器绑定的直播源 + std::weak_ptr _play_src; + //直播源读取器 + RtspMediaSource::RingType::RingReader::Ptr _play_reader; + //sdp里面有效的track,包含音频或视频 + std::vector _sdp_track; + //播放器setup指定的播放track,默认为TrackInvalid表示不指定即音视频都推 + TrackType _target_play_track = TrackInvalid; + + ////////RTP over udp//////// + //RTP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtp_socks[2]; + //RTCP端口,trackid idx 为数组下标 + toolkit::Socket::Ptr _rtcp_socks[2]; + //标记是否收到播放的udp打洞包,收到播放的udp打洞包后才能知道其外网udp端口号 + std::unordered_set _udp_connected_flags; + ////////RTSP over HTTP //////// + //quicktime 请求rtsp会产生两次tcp连接, + //一次发送 get 一次发送post,需要通过x-sessioncookie关联起来 + std::string _http_x_sessioncookie; + std::function _on_recv; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + toolkit::Ticker _rtcp_send_tickers[2]; + //统计rtp并发送rtcp + std::vector _rtcp_context; +}; + +/** + * 支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 + */ +using RtspSessionWithSSL = toolkit::SessionWithSSL; + +} /* namespace mediakit */ + +#endif /* SESSION_RTSPSESSION_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSplitter.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSplitter.h new file mode 100644 index 00000000..de268975 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/RtspSplitter.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_RTSPSPLITTER_H +#define ZLMEDIAKIT_RTSPSPLITTER_H + +#include "Common/Parser.h" +#include "Http/HttpRequestSplitter.h" + +namespace mediakit{ + +class RtspSplitter : public HttpRequestSplitter{ +public: + /** + * 是否允许接收rtp包 + * @param enable + */ + void enableRecvRtp(bool enable); +protected: + /** + * 收到完整的rtsp包回调,包括sdp等content数据 + * @param parser rtsp包 + */ + virtual void onWholeRtspPacket(Parser &parser) = 0; + + /** + * 收到rtp包回调 + * @param data + * @param len + */ + virtual void onRtpPacket(const char *data,size_t len) = 0; + + /** + * 从rtsp头中获取Content长度 + * @param parser + * @return + */ + virtual ssize_t getContentLength(Parser &parser); + +protected: + const char *onSearchPacketTail(const char *data,size_t len) override ; + const char *onSearchPacketTail_l(const char *data,size_t len) ; + ssize_t onRecvHeader(const char *data,size_t len) override; + void onRecvContent(const char *data,size_t len) override; + +private: + bool _enableRecvRtp = false; + bool _isRtpPacket = false; + Parser _parser; +}; + +}//namespace mediakit + + + +#endif //ZLMEDIAKIT_RTSPSPLITTER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/UDPServer.h b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/UDPServer.h new file mode 100644 index 00000000..90f735d4 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Rtsp/UDPServer.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef RTSP_UDPSERVER_H_ +#define RTSP_UDPSERVER_H_ + +#include +#include +#include +#include +#include "Network/Socket.h" + +namespace mediakit { + +class UDPServer : public std::enable_shared_from_this { +public: + using onRecvData = std::function ; + ~UDPServer(); + static UDPServer &Instance(); + toolkit::Socket::Ptr getSock(toolkit::SocketHelper &helper, const char *local_ip, int interleaved, uint16_t local_port = 0); + void listenPeer(const char *peer_ip, void *obj, const onRecvData &cb); + void stopListenPeer(const char *peer_ip, void *obj); + +private: + UDPServer(); + void onRecv(int interleaved, const toolkit::Buffer::Ptr &buf, struct sockaddr *peer_addr); + void onErr(const std::string &strKey, const toolkit::SockException &err); + +private: + std::mutex _mtx_udp_sock; + std::mutex _mtx_on_recv; + std::unordered_map _udp_sock_map; + std::unordered_map > _on_recv_map; +}; + +} /* namespace mediakit */ + +#endif /* RTSP_UDPSERVER_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellCMD.h b/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellCMD.h new file mode 100644 index 00000000..080c4ff1 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellCMD.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_SHELL_SHELLCMD_H_ +#define SRC_SHELL_SHELLCMD_H_ + +#include "Util/CMD.h" +#include "Common/MediaSource.h" + +namespace mediakit { + +class CMD_media : public toolkit::CMD { +public: + CMD_media() { + _parser.reset(new toolkit::OptionParser([](const std::shared_ptr &stream, toolkit::mINI &ini) { + MediaSource::for_each_media([&](const MediaSource::Ptr &media) { + if (ini.find("list") != ini.end()) { + //列出源 + (*stream) << "\t" << media->getUrl() << "\r\n"; + return; + } + + toolkit::EventPollerPool::Instance().getPoller()->async([ini, media, stream]() { + if (ini.find("kick") != ini.end()) { + //踢出源 + do { + if (!media) { + break; + } + if (!media->close(true)) { + break; + } + (*stream) << "\t踢出成功:" << media->getUrl() << "\r\n"; + return; + } while (0); + (*stream) << "\t踢出失败:" << media->getUrl() << "\r\n"; + } + }, false); + + + }, ini["schema"], ini["vhost"], ini["app"], ini["stream"]); + })); + (*_parser) << toolkit::Option('k', "kick", toolkit::Option::ArgNone, nullptr, false, "踢出媒体源", nullptr); + (*_parser) << toolkit::Option('l', "list", toolkit::Option::ArgNone, nullptr, false, "列出媒体源", nullptr); + (*_parser) << toolkit::Option('S', "schema", toolkit::Option::ArgRequired, nullptr, false, "协议筛选", nullptr); + (*_parser) << toolkit::Option('v', "vhost", toolkit::Option::ArgRequired, nullptr, false, "虚拟主机筛选", nullptr); + (*_parser) << toolkit::Option('a', "app", toolkit::Option::ArgRequired, nullptr, false, "应用名筛选", nullptr); + (*_parser) << toolkit::Option('s', "stream", toolkit::Option::ArgRequired, nullptr, false, "流id筛选", nullptr); + } + + const char *description() const override { + return "媒体源相关操作."; + } +}; + +} /* namespace mediakit */ + +#endif //SRC_SHELL_SHELLCMD_H_ \ No newline at end of file diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellSession.h b/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellSession.h new file mode 100644 index 00000000..47b2a8fc --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Shell/ShellSession.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_SHELL_SHELLSESSION_H_ +#define SRC_SHELL_SHELLSESSION_H_ + +#include +#include "Util/TimeTicker.h" +#include "Network/Session.h" + +namespace mediakit { + +class ShellSession: public toolkit::Session { +public: + ShellSession(const toolkit::Socket::Ptr &_sock); + + void onRecv(const toolkit::Buffer::Ptr &) override; + void onError(const toolkit::SockException &err) override; + void onManager() override; + +private: + inline bool onCommandLine(const std::string &); + inline void pleaseInputUser(); + inline void pleaseInputPasswd(); + inline void printShellPrefix(); + + std::function _loginInterceptor; + std::string _strRecvBuf; + toolkit::Ticker _beatTicker; + std::string _strUserName; +}; + +} /* namespace mediakit */ + +#endif /* SRC_SHELL_SHELLSESSION_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSource.h b/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSource.h new file mode 100644 index 00000000..5900ec56 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSource.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TSMEDIASOURCE_H +#define ZLMEDIAKIT_TSMEDIASOURCE_H + +#include "Common/MediaSource.h" +#include "Common/PacketCache.h" +#include "Util/RingBuffer.h" + +#define TS_GOP_SIZE 512 + +namespace mediakit { + +//TS直播数据包 +class TSPacket : public toolkit::BufferOffset{ +public: + using Ptr = std::shared_ptr; + + template + TSPacket(ARGS && ...args) : BufferOffset(std::forward(args)...) {}; + +public: + uint64_t time_stamp = 0; +}; + +//TS直播源 +class TSMediaSource final : public MediaSource, public toolkit::RingDelegate, private PacketCache{ +public: + using Ptr = std::shared_ptr; + using RingDataType = std::shared_ptr >; + using RingType = toolkit::RingBuffer; + + TSMediaSource(const MediaTuple& tuple, int ring_size = TS_GOP_SIZE): MediaSource(TS_SCHEMA, tuple), _ring_size(ring_size) {} + + ~TSMediaSource() override { flush(); } + + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + + void getPlayerList(const std::function &info_list)> &cb, + const std::function &on_change) override { + _ring->getInfoList(cb, on_change); + } + + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; + } + + /** + * 输入TS包 + * @param packet TS包 + * @param key 是否为关键帧第一个包 + */ + void onWrite(TSPacket::Ptr packet, bool key) override { + _speed[TrackVideo] += packet->size(); + if (!_ring) { + createRing(); + } + if (key) { + _have_video = true; + } + auto stamp = packet->time_stamp; + PacketCache::inputPacket(stamp, true, std::move(packet), key); + } + + /** + * 情况GOP缓存 + */ + void clearCache() override { + PacketCache::clearCache(); + _ring->clearCache(); + } + +private: + void createRing(){ + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _ring = std::make_shared(_ring_size, [weak_self](int size) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->onReaderChanged(size); + }); + //注册媒体源 + regist(); + } + + /** + * 合并写回调 + * @param packet_list 合并写缓存列队 + * @param key_pos 是否包含关键帧 + */ + void onFlush(std::shared_ptr > packet_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以确保一直清空GOP缓存 + _ring->write(std::move(packet_list), _have_video ? key_pos : true); + } + +private: + bool _have_video = false; + int _ring_size; + RingType::Ptr _ring; +}; + + +}//namespace mediakit +#endif //ZLMEDIAKIT_TSMEDIASOURCE_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSourceMuxer.h b/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSourceMuxer.h new file mode 100644 index 00000000..6b8d69ec --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/TS/TSMediaSourceMuxer.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). + * + * Use of this source code is governed by MIT-like license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_TSMEDIASOURCEMUXER_H +#define ZLMEDIAKIT_TSMEDIASOURCEMUXER_H + +#include "TSMediaSource.h" +#include "Record/MPEG.h" + +namespace mediakit { + +class TSMediaSourceMuxer final : public MpegMuxer, public MediaSourceEventInterceptor, + public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + TSMediaSourceMuxer(const MediaTuple& tuple, const ProtocolOption &option) : MpegMuxer(false) { + _option = option; + _media_src = std::make_shared(tuple); + } + + ~TSMediaSourceMuxer() override { + try { + MpegMuxer::flush(); + } catch (std::exception &ex) { + WarnL << ex.what(); + } + }; + + void setListener(const std::weak_ptr &listener){ + setDelegate(listener); + _media_src->setListener(shared_from_this()); + } + + int readerCount() const{ + return _media_src->readerCount(); + } + + void onReaderChanged(MediaSource &sender, int size) override { + _enabled = _option.ts_demand ? size : true; + if (!size && _option.ts_demand) { + _clear_cache = true; + } + MediaSourceEventInterceptor::onReaderChanged(sender, size); + } + + bool inputFrame(const Frame::Ptr &frame) override { + if (_clear_cache && _option.ts_demand) { + _clear_cache = false; + _media_src->clearCache(); + } + if (_enabled || !_option.ts_demand) { + return MpegMuxer::inputFrame(frame); + } + return false; + } + + bool isEnabled() { + //缓存尚未清空时,还允许触发inputFrame函数,以便及时清空缓存 + return _option.ts_demand ? (_clear_cache ? true : _enabled) : true; + } + +protected: + void onWrite(std::shared_ptr buffer, uint64_t timestamp, bool key_pos) override { + if (!buffer) { + return; + } + auto packet = std::make_shared(std::move(buffer)); + packet->time_stamp = timestamp; + _media_src->onWrite(std::move(packet), key_pos); + } + +private: + bool _enabled = true; + bool _clear_cache = false; + ProtocolOption _option; + TSMediaSource::Ptr _media_src; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_TSMEDIASOURCEMUXER_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskExecutor.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskExecutor.h new file mode 100644 index 00000000..efbbfc84 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskExecutor.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLTOOLKIT_TASKEXECUTOR_H +#define ZLTOOLKIT_TASKEXECUTOR_H + +#include +#include +#include +#include "Util/List.h" +#include "Util/util.h" + +namespace toolkit { + +/** +* cpu负载计算器 +*/ +class ThreadLoadCounter { +public: + /** + * 构造函数 + * @param max_size 统计样本数量 + * @param max_usec 统计时间窗口,亦即最近{max_usec}的cpu负载率 + */ + ThreadLoadCounter(uint64_t max_size, uint64_t max_usec); + ~ThreadLoadCounter() = default; + + /** + * 线程进入休眠 + */ + void startSleep(); + + /** + * 休眠唤醒,结束休眠 + */ + void sleepWakeUp(); + + /** + * 返回当前线程cpu使用率,范围为 0 ~ 100 + * @return 当前线程cpu使用率 + */ + int load(); + +private: + struct TimeRecord { + TimeRecord(uint64_t tm, bool slp) { + _time = tm; + _sleep = slp; + } + + bool _sleep; + uint64_t _time; + }; + +private: + bool _sleeping = true; + uint64_t _last_sleep_time; + uint64_t _last_wake_time; + uint64_t _max_size; + uint64_t _max_usec; + std::mutex _mtx; + List _time_list; +}; + +class TaskCancelable : public noncopyable { +public: + TaskCancelable() = default; + virtual ~TaskCancelable() = default; + virtual void cancel() = 0; +}; + +template +class TaskCancelableImp; + +template +class TaskCancelableImp : public TaskCancelable { +public: + using Ptr = std::shared_ptr; + using func_type = std::function; + + ~TaskCancelableImp() = default; + + template + TaskCancelableImp(FUNC &&task) { + _strongTask = std::make_shared(std::forward(task)); + _weakTask = _strongTask; + } + + void cancel() override { + _strongTask = nullptr; + } + + operator bool() { + return _strongTask && *_strongTask; + } + + void operator=(std::nullptr_t) { + _strongTask = nullptr; + } + + R operator()(ArgTypes ...args) const { + auto strongTask = _weakTask.lock(); + if (strongTask && *strongTask) { + return (*strongTask)(std::forward(args)...); + } + return defaultValue(); + } + + template + static typename std::enable_if::value, void>::type + defaultValue() {} + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return nullptr; + } + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return 0; + } + +protected: + std::weak_ptr _weakTask; + std::shared_ptr _strongTask; +}; + +using TaskIn = std::function; +using Task = TaskCancelableImp; + +class TaskExecutorInterface { +public: + TaskExecutorInterface() = default; + virtual ~TaskExecutorInterface() = default; + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + */ + virtual Task::Ptr async(TaskIn task, bool may_sync = true) = 0; + + /** + * 最高优先级方式异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + */ + virtual Task::Ptr async_first(TaskIn task, bool may_sync = true); + + /** + * 同步执行任务 + * @param task + * @return + */ + void sync(const TaskIn &task); + + /** + * 最高优先级方式同步执行任务 + * @param task + * @return + */ + void sync_first(const TaskIn &task); +}; + +/** +* 任务执行器 +*/ +class TaskExecutor : public ThreadLoadCounter, public TaskExecutorInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param max_size cpu负载统计样本数 + * @param max_usec cpu负载统计时间窗口大小 + */ + TaskExecutor(uint64_t max_size = 32, uint64_t max_usec = 2 * 1000 * 1000); + ~TaskExecutor() = default; +}; + +class TaskExecutorGetter { +public: + using Ptr = std::shared_ptr; + + virtual ~TaskExecutorGetter() = default; + + /** + * 获取任务执行器 + * @return 任务执行器 + */ + virtual TaskExecutor::Ptr getExecutor() = 0; + + /** + * 获取执行器个数 + */ + virtual size_t getExecutorSize() const = 0; +}; + +class TaskExecutorGetterImp : public TaskExecutorGetter { +public: + TaskExecutorGetterImp() = default; + ~TaskExecutorGetterImp() = default; + + /** + * 根据线程负载情况,获取最空闲的任务执行器 + * @return 任务执行器 + */ + TaskExecutor::Ptr getExecutor() override; + + /** + * 获取所有线程的负载率 + * @return 所有线程的负载率 + */ + std::vector getExecutorLoad(); + + /** + * 获取所有线程任务执行延时,单位毫秒 + * 通过此函数也可以大概知道线程负载情况 + * @return + */ + void getExecutorDelay(const std::function &)> &callback); + + /** + * 遍历所有线程 + */ + void for_each(const std::function &cb); + + /** + * 获取线程数 + */ + size_t getExecutorSize() const override; + +protected: + size_t addPoller(const std::string &name, size_t size, int priority, bool register_thread, bool enable_cpu_affinity = true); + +protected: + size_t _thread_pos = 0; + std::vector _threads; +}; + +}//toolkit +#endif //ZLTOOLKIT_TASKEXECUTOR_H diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskQueue.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskQueue.h new file mode 100644 index 00000000..584e05ab --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/TaskQueue.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TASKQUEUE_H_ +#define TASKQUEUE_H_ + +#include +#include "Util/List.h" +#include "semaphore.h" + +namespace toolkit { + +//实现了一个基于函数对象的任务列队,该列队是线程安全的,任务列队任务数由信号量控制 +template +class TaskQueue { +public: + //打入任务至列队 + template + void push_task(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_back(std::forward(task_func)); + } + _sem.post(); + } + + template + void push_task_first(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_front(std::forward(task_func)); + } + _sem.post(); + } + + //清空任务列队 + void push_exit(size_t n) { + _sem.post(n); + } + + //从列队获取一个任务,由执行线程执行 + bool get_task(T &tsk) { + _sem.wait(); + std::lock_guard lock(_mutex); + if (_queue.empty()) { + return false; + } + tsk = std::move(_queue.front()); + _queue.pop_front(); + return true; + } + + size_t size() const { + std::lock_guard lock(_mutex); + return _queue.size(); + } + +private: + List _queue; + mutable std::mutex _mutex; + semaphore _sem; +}; + +} /* namespace toolkit */ +#endif /* TASKQUEUE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/ThreadPool.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/ThreadPool.h new file mode 100644 index 00000000..cab7996c --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/ThreadPool.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef THREADPOOL_H_ +#define THREADPOOL_H_ + +#include "threadgroup.h" +#include "TaskQueue.h" +#include "TaskExecutor.h" +#include "Util/util.h" +#include "Util/logger.h" + +namespace toolkit { + +class ThreadPool : public TaskExecutor { +public: + enum Priority { + PRIORITY_LOWEST = 0, + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH, + PRIORITY_HIGHEST + }; + + ThreadPool(int num = 1, Priority priority = PRIORITY_HIGHEST, bool auto_run = true, bool set_affinity = true, + const std::string &pool_name = "thread pool") { + _thread_num = num; + _on_setup = [pool_name, priority, set_affinity](int index) { + std::string name = pool_name + ' ' + std::to_string(index); + setPriority(priority); + setThreadName(name.data()); + if (set_affinity) { + setThreadAffinity(index % std::thread::hardware_concurrency()); + } + }; + _logger = Logger::Instance().shared_from_this(); + if (auto_run) { + start(); + } + } + + ~ThreadPool() { + shutdown(); + wait(); + } + + //把任务打入线程池并异步执行 + Task::Ptr async(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + auto ret = std::make_shared(std::move(task)); + _queue.push_task(ret); + return ret; + } + + Task::Ptr async_first(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + + auto ret = std::make_shared(std::move(task)); + _queue.push_task_first(ret); + return ret; + } + + size_t size() { + return _queue.size(); + } + + static bool setPriority(Priority priority = PRIORITY_NORMAL, std::thread::native_handle_type threadId = 0) { + // set priority +#if defined(_WIN32) + static int Priorities[] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST }; + if (priority != PRIORITY_NORMAL && SetThreadPriority(GetCurrentThread(), Priorities[priority]) == 0) { + return false; + } + return true; +#else + static int Min = sched_get_priority_min(SCHED_FIFO); + if (Min == -1) { + return false; + } + static int Max = sched_get_priority_max(SCHED_FIFO); + if (Max == -1) { + return false; + } + static int Priorities[] = {Min, Min + (Max - Min) / 4, Min + (Max - Min) / 2, Min + (Max - Min) * 3 / 4, Max}; + + if (threadId == 0) { + threadId = pthread_self(); + } + struct sched_param params; + params.sched_priority = Priorities[priority]; + return pthread_setschedparam(threadId, SCHED_FIFO, ¶ms) == 0; +#endif + } + + void start() { + if (_thread_num <= 0) { + return; + } + size_t total = _thread_num - _thread_group.size(); + for (size_t i = 0; i < total; ++i) { + _thread_group.create_thread([this, i]() {run(i);}); + } + } + +private: + void run(size_t index) { + _on_setup(index); + Task::Ptr task; + while (true) { + startSleep(); + if (!_queue.get_task(task)) { + //空任务,退出线程 + break; + } + sleepWakeUp(); + try { + (*task)(); + task = nullptr; + } catch (std::exception &ex) { + ErrorL << "ThreadPool catch a exception: " << ex.what(); + } + } + } + + void wait() { + _thread_group.join_all(); + } + + void shutdown() { + _queue.push_exit(_thread_num); + } + +private: + size_t _thread_num; + Logger::Ptr _logger; + thread_group _thread_group; + TaskQueue _queue; + std::function _on_setup; +}; + +} /* namespace toolkit */ +#endif /* THREADPOOL_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/WorkThreadPool.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/WorkThreadPool.h new file mode 100644 index 00000000..ae7f0a6a --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/WorkThreadPool.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef UTIL_WORKTHREADPOOL_H_ +#define UTIL_WORKTHREADPOOL_H_ + +#include +#include "Poller/EventPoller.h" + +namespace toolkit { + +class WorkThreadPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + + ~WorkThreadPool() override = default; + + /** + * 获取单例 + */ + static WorkThreadPool &Instance(); + + /** + * 设置EventPoller个数,在WorkThreadPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @return + */ + EventPoller::Ptr getPoller(); + +protected: + WorkThreadPool(); +}; + +} /* namespace toolkit */ +#endif /* UTIL_WORKTHREADPOOL_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/semaphore.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/semaphore.h new file mode 100644 index 00000000..46f31552 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/semaphore.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SEMAPHORE_H_ +#define SEMAPHORE_H_ + +/* + * 目前发现信号量在32位的系统上有问题, + * 休眠的线程无法被正常唤醒,先禁用之 +#if defined(__linux__) +#include +#define HAVE_SEM +#endif //HAVE_SEM +*/ + +#include +#include + +namespace toolkit { + +class semaphore { +public: + explicit semaphore(size_t initial = 0) { +#if defined(HAVE_SEM) + sem_init(&_sem, 0, initial); +#else + _count = 0; +#endif + } + + ~semaphore() { +#if defined(HAVE_SEM) + sem_destroy(&_sem); +#endif + } + + void post(size_t n = 1) { +#if defined(HAVE_SEM) + while (n--) { + sem_post(&_sem); + } +#else + std::unique_lock lock(_mutex); + _count += n; + if (n == 1) { + _condition.notify_one(); + } else { + _condition.notify_all(); + } +#endif + } + + void wait() { +#if defined(HAVE_SEM) + sem_wait(&_sem); +#else + std::unique_lock lock(_mutex); + while (_count == 0) { + _condition.wait(lock); + } + --_count; +#endif + } + +private: +#if defined(HAVE_SEM) + sem_t _sem; +#else + size_t _count; + std::recursive_mutex _mutex; + std::condition_variable_any _condition; +#endif +}; + +} /* namespace toolkit */ +#endif /* SEMAPHORE_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Thread/threadgroup.h b/webrtc_player/android/zlm/src/main/cpp/include/Thread/threadgroup.h new file mode 100644 index 00000000..21d689ea --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Thread/threadgroup.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef THREADGROUP_H_ +#define THREADGROUP_H_ + +#include +#include +#include + +namespace toolkit { + +class thread_group { +private: + thread_group(thread_group const &); + thread_group &operator=(thread_group const &); + +public: + thread_group() {} + + ~thread_group() { + _threads.clear(); + } + + bool is_this_thread_in() { + auto thread_id = std::this_thread::get_id(); + if (_thread_id == thread_id) { + return true; + } + return _threads.find(thread_id) != _threads.end(); + } + + bool is_thread_in(std::thread *thrd) { + if (!thrd) { + return false; + } + auto it = _threads.find(thrd->get_id()); + return it != _threads.end(); + } + + template + std::thread *create_thread(F &&threadfunc) { + auto thread_new = std::make_shared(std::forward(threadfunc)); + _thread_id = thread_new->get_id(); + _threads[_thread_id] = thread_new; + return thread_new.get(); + } + + void remove_thread(std::thread *thrd) { + auto it = _threads.find(thrd->get_id()); + if (it != _threads.end()) { + _threads.erase(it); + } + } + + void join_all() { + if (is_this_thread_in()) { + throw std::runtime_error("Trying joining itself in thread_group"); + } + for (auto &it : _threads) { + if (it.second->joinable()) { + it.second->join(); //等待线程主动退出 + } + } + _threads.clear(); + } + + size_t size() { + return _threads.size(); + } + +private: + std::thread::id _thread_id; + std::unordered_map> _threads; +}; + +} /* namespace toolkit */ +#endif /* THREADGROUP_H_ */ diff --git a/webrtc_player/android/zlm/src/main/cpp/include/Util/CMD.h b/webrtc_player/android/zlm/src/main/cpp/include/Util/CMD.h new file mode 100644 index 00000000..c4e83644 --- /dev/null +++ b/webrtc_player/android/zlm/src/main/cpp/include/Util/CMD.h @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved. + * + * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef SRC_UTIL_CMD_H_ +#define SRC_UTIL_CMD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "mini.h" + +namespace toolkit{ + +class Option { +public: + using OptionHandler = std::function &stream, const std::string &arg)>; + + enum ArgType { + ArgNone = 0,//no_argument, + ArgRequired = 1,//required_argument, + ArgOptional = 2,//optional_argument + }; + + Option() = default; + + Option(char short_opt, const char *long_opt, enum ArgType type, const char *default_value, bool must_exist, + const char *des, const OptionHandler &cb) { + _short_opt = short_opt; + _long_opt = long_opt; + _type = type; + if (type != ArgNone) { + if (default_value) { + _default_value = std::make_shared(default_value); + } + if (!_default_value && must_exist) { + _must_exist = true; + } + } + _des = des; + _cb = cb; + } + + bool operator()(const std::shared_ptr &stream, const std::string &arg) { + return _cb ? _cb(stream, arg) : true; + } + +private: + friend class OptionParser; + bool _must_exist = false; + char _short_opt; + enum ArgType _type; + std::string _des; + std::string _long_opt; + OptionHandler _cb; + std::shared_ptr _default_value; +}; + +class OptionParser { +public: + using OptionCompleted = std::function &, mINI &)>; + + OptionParser(const OptionCompleted &cb = nullptr, bool enable_empty_args = true) { + _on_completed = cb; + _enable_empty_args = enable_empty_args; + _helper = Option('h', "help", Option::ArgNone, nullptr, false, "打印此信息", + [this](const std::shared_ptr &stream,const std::string &arg)->bool { + static const char *argsType[] = {"无参", "有参", "选参"}; + static const char *mustExist[] = {"选填", "必填"}; + static std::string defaultPrefix = "默认:"; + static std::string defaultNull = "null"; + + std::stringstream printer; + size_t maxLen_longOpt = 0; + auto maxLen_default = defaultNull.size(); + + for (auto &pr : _map_options) { + auto &opt = pr.second; + if (opt._long_opt.size() > maxLen_longOpt) { + maxLen_longOpt = opt._long_opt.size(); + } + if (opt._default_value) { + if (opt._default_value->size() > maxLen_default) { + maxLen_default = opt._default_value->size(); + } + } + } + for (auto &pr : _map_options) { + auto &opt = pr.second; + //打印短参和长参名 + if (opt._short_opt) { + printer << " -" << opt._short_opt << " --" << opt._long_opt; + } else { + printer << " " << " " << " --" << opt._long_opt; + } + for (size_t i = 0; i < maxLen_longOpt - opt._long_opt.size(); ++i) { + printer << " "; + } + //打印是否有参 + printer << " " << argsType[opt._type]; + //打印默认参数 + std::string defaultValue = defaultNull; + if (opt._default_value) { + defaultValue = *opt._default_value; + } + printer << " " << defaultPrefix << defaultValue; + for (size_t i = 0; i < maxLen_default - defaultValue.size(); ++i) { + printer << " "; + } + //打印是否必填参数 + printer << " " << mustExist[opt._must_exist]; + //打印描述 + printer << " " << opt._des << std::endl; + } + throw std::invalid_argument(printer.str()); + }); + (*this) << _helper; + } + + OptionParser &operator<<(Option &&option) { + int index = 0xFF + (int) _map_options.size(); + if (option._short_opt) { + _map_char_index.emplace(option._short_opt, index); + } + _map_options.emplace(index, std::forward