init
This commit is contained in:
parent
58d273e04e
commit
0dcf083b4f
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".PlayDemoActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity android:name=".PlayerDemoActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.zlmediakit.webrtc.WebRTCSurfaceView
|
||||
android:id="@+id/web_rtc_sv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/url"
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/web_rtc_sv"
|
||||
android:text=""/>
|
||||
<LinearLayout
|
||||
android:id="@+id/ll"
|
||||
android:onClick="toPlayActivity"
|
||||
android:text="播放" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="30dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/url">
|
||||
android:onClick="toPushActivity"
|
||||
android:text="推流" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="播放" />
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_pause"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂停" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_resume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="恢复" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_speaker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="扬声器" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_mute"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="静音" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll2"
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ll">
|
||||
android:onClick="toDataChannelActivity"
|
||||
android:text="DataChannel"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_screenshot"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="截图" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_screen_record"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="录制" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/iv_screen"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PlayDemoActivity">
|
||||
|
||||
<com.zlmediakit.webrtc.WebRTCSurfaceView
|
||||
android:id="@+id/web_rtc_sv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/url"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/web_rtc_sv"
|
||||
android:text=""/>
|
||||
<LinearLayout
|
||||
android:id="@+id/ll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="30dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/url">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="播放" />
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_pause"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂停" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_resume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="恢复" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_speaker"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="扬声器" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_mute"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="静音" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ll">
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_screenshot"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="截图" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_screen_record"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="录制" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/iv_screen"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
android:id="@+id/surface_view_renderer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="240dp"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">AndroidWebRTC</string>
|
||||
<string name="app_name">ZLMediakit WebRTC</string>
|
||||
</resources>
|
||||
|
|
@ -22,3 +22,4 @@ dependencyResolutionManagement {
|
|||
}
|
||||
rootProject.name = "android"
|
||||
include ':app'
|
||||
include ':zlm'
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -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'
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
|
|
@ -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)
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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 <cstdint>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
#include <x264.h>
|
||||
#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_ */
|
||||
|
|
@ -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>;
|
||||
|
||||
FFmpegFrame(std::shared_ptr<AVFrame> frame = nullptr);
|
||||
~FFmpegFrame();
|
||||
|
||||
AVFrame *get() const;
|
||||
void fillPicture(AVPixelFormat target_format, int target_width, int target_height);
|
||||
|
||||
private:
|
||||
char *_data = nullptr;
|
||||
std::shared_ptr<AVFrame> _frame;
|
||||
};
|
||||
|
||||
class FFmpegSwr {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegSwr>;
|
||||
|
||||
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<void()> task);
|
||||
bool addDecodeTask(bool key_frame, std::function<void()> 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<std::function<void()> > _task;
|
||||
std::shared_ptr<std::thread> _thread;
|
||||
};
|
||||
|
||||
class FFmpegDecoder : public TaskManager {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegDecoder>;
|
||||
using onDec = std::function<void(const FFmpegFrame::Ptr &)>;
|
||||
|
||||
FFmpegDecoder(const Track::Ptr &track, int thread_num = 2, const std::vector<std::string> &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<AVCodecContext> _context;
|
||||
FrameMerger _merger{FrameMerger::h264_prefix};
|
||||
};
|
||||
|
||||
class FFmpegSws {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegSws>;
|
||||
|
||||
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
|
||||
|
|
@ -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 <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#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<DevChannel>;
|
||||
|
||||
//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<H264Encoder> _pH264Enc;
|
||||
std::shared_ptr<AACEncoder> _pAacEnc;
|
||||
std::shared_ptr<VideoInfo> _video;
|
||||
std::shared_ptr<AudioInfo> _audio;
|
||||
toolkit::SmoothTicker _aTicker[2];
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif /* DEVICE_DEVICE_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 <functional>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
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<void(const char *, uint64_t)> &fn);
|
||||
};
|
||||
} // namespace mediakit
|
||||
#endif // ZLMEDIAKIT_JEMALLOCUTIL_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 <mutex>
|
||||
#include <memory>
|
||||
#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<MediaSinkInterface>;
|
||||
};
|
||||
|
||||
/**
|
||||
* aac静音音频添加器
|
||||
*/
|
||||
class MuteAudioMaker : public FrameDispatcher {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<MuteAudioMaker>;
|
||||
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<MediaSink>;
|
||||
/**
|
||||
* 输入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<Track::Ptr> 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<int, toolkit::List<Frame::Ptr> > _frame_unread;
|
||||
std::unordered_map<int, std::function<void()> > _track_ready_callback;
|
||||
std::unordered_map<int, std::pair<Track::Ptr, bool/*got frame*/> > _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<Track::Ptr> 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<Track::Ptr> _origin_track;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
#endif //ZLMEDIAKIT_MEDIASINK_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 <string>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#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<typename ...T>
|
||||
NotImplemented(T && ...args) : std::runtime_error(std::forward<T>(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<toolkit::SockInfo> 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<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector<Track::Ptr>(); };
|
||||
// 获取MultiMediaSourceMuxer对象
|
||||
virtual std::shared_ptr<MultiMediaSourceMuxer> 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<void(uint16_t, const toolkit::SockException &)> 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 <typename MAP, typename KEY, typename TYPE>
|
||||
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 <typename MAP>
|
||||
ProtocolOption(const MAP &allArgs) : ProtocolOption() {
|
||||
load(allArgs);
|
||||
}
|
||||
|
||||
template <typename MAP>
|
||||
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<MediaSourceEvent> &listener);
|
||||
std::shared_ptr<MediaSourceEvent> getDelegate() const;
|
||||
|
||||
MediaOriginType getOriginType(MediaSource &sender) const override;
|
||||
std::string getOriginUrl(MediaSource &sender) const override;
|
||||
std::shared_ptr<toolkit::SockInfo> 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<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
|
||||
void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> 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<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) override;
|
||||
|
||||
private:
|
||||
std::weak_ptr<MediaSourceEvent> _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<MediaSource> {
|
||||
public:
|
||||
static MediaSource& NullMediaSource();
|
||||
using Ptr = std::shared_ptr<MediaSource>;
|
||||
|
||||
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<void> getOwnership();
|
||||
|
||||
// 获取所有Track
|
||||
std::vector<Track::Ptr> 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<MediaSourceEvent> &listener);
|
||||
// 获取监听者
|
||||
std::weak_ptr<MediaSourceEvent> getListener() const;
|
||||
|
||||
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
|
||||
virtual int readerCount() = 0;
|
||||
// 观看者个数,包括(hls/rtsp/rtmp)
|
||||
virtual int totalReaderCount();
|
||||
// 获取播放器列表
|
||||
virtual void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
|
||||
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) {
|
||||
assert(cb);
|
||||
cb(std::list<toolkit::Any>());
|
||||
}
|
||||
|
||||
virtual bool broadcastMessage(const toolkit::Any &data) { return false; }
|
||||
|
||||
// 获取媒体源类型
|
||||
MediaOriginType getOriginType() const;
|
||||
// 获取媒体源url或者文件路径
|
||||
std::string getOriginUrl() const;
|
||||
// 获取媒体源客户端相关信息
|
||||
std::shared_ptr<toolkit::SockInfo> 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<void(uint16_t, const toolkit::SockException &)> cb);
|
||||
// 停止发送ps-rtp
|
||||
bool stopSendRtp(const std::string &ssrc);
|
||||
// 获取丢包率
|
||||
float getLossRate(mediakit::TrackType type);
|
||||
// 获取所在线程
|
||||
toolkit::EventPoller::Ptr getOwnerPoller();
|
||||
// 获取MultiMediaSourceMuxer对象
|
||||
std::shared_ptr<MultiMediaSourceMuxer> 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<toolkit::Session> &session, const std::function<void(const Ptr &src)> &cb);
|
||||
// 遍历所有流
|
||||
static void for_each_media(const std::function<void(const Ptr &src)> &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<MediaSourceEvent> _listener;
|
||||
// 对象个数统计
|
||||
toolkit::ObjectStatistic<MediaSource> _statistic;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
#endif //ZLMEDIAKIT_MEDIASOURCE_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<MultiMediaSourceMuxer>{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<MultiMediaSourceMuxer>;
|
||||
using RingType = toolkit::RingBuffer<Frame::Ptr>;
|
||||
|
||||
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<MediaSourceEvent> &listener);
|
||||
|
||||
/**
|
||||
* 设置Track就绪事件监听器
|
||||
* @param listener 事件监听器
|
||||
*/
|
||||
void setTrackListener(const std::weak_ptr<Listener> &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<void(uint16_t, const toolkit::SockException &)> cb) override;
|
||||
|
||||
/**
|
||||
* 停止ps-rtp发送
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
|
||||
|
||||
/**
|
||||
* 获取所有Track
|
||||
* @param trackReady 是否筛选过滤未就绪的track
|
||||
* @return 所有Track
|
||||
*/
|
||||
std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
|
||||
|
||||
/**
|
||||
* 获取所属线程
|
||||
*/
|
||||
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
|
||||
|
||||
/**
|
||||
* 获取本对象
|
||||
*/
|
||||
std::shared_ptr<MultiMediaSourceMuxer> 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<class FramePacedSender> _paced_sender;
|
||||
MediaTuple _tuple;
|
||||
ProtocolOption _option;
|
||||
toolkit::Ticker _last_check;
|
||||
std::unordered_map<int, Stamp> _stamps;
|
||||
std::weak_ptr<Listener> _track_listener;
|
||||
std::unordered_multimap<std::string, RingType::RingReader::Ptr> _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<MultiMediaSourceMuxer> _statistic;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_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<typename packet, typename policy = FlushPolicy, typename packet_list = toolkit::List<std::shared_ptr<packet> > >
|
||||
class PacketCache {
|
||||
public:
|
||||
PacketCache() { _cache = std::make_shared<packet_list>(); }
|
||||
|
||||
virtual ~PacketCache() = default;
|
||||
|
||||
void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr<packet> 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<packet_list>();
|
||||
_key_pos = false;
|
||||
}
|
||||
|
||||
virtual void clearCache() {
|
||||
_cache->clear();
|
||||
}
|
||||
|
||||
virtual void onFlush(std::shared_ptr<packet_list>, 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<packet, RtpPacket>::value ? rtspLowLatency : (mergeWriteMS <= 0);
|
||||
}
|
||||
|
||||
private:
|
||||
bool _key_pos = false;
|
||||
policy _policy;
|
||||
std::shared_ptr<packet_list> _cache;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //ZLMEDIAKIT_PACKET_CACHE_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 <map>
|
||||
#include <string>
|
||||
#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<std::string, std::string, StrCaseCompare> {
|
||||
public:
|
||||
using Super = std::multimap<std::string, std::string, StrCaseCompare>;
|
||||
|
||||
std::string &operator[](const std::string &k) {
|
||||
auto it = find(k);
|
||||
if (it == end()) {
|
||||
it = Super::emplace(k, "");
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void emplace(K &&k, V &&v) {
|
||||
auto it = find(k);
|
||||
if (it != end()) {
|
||||
return;
|
||||
}
|
||||
Super::emplace(std::forward<K>(k), std::forward<V>(v));
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
void emplace_force(K &&k, V &&v) {
|
||||
Super::emplace(std::forward<K>(k), std::forward<V>(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
|
||||
|
|
@ -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 <set>
|
||||
#include <cstdint>
|
||||
#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<uint64_t> _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
|
||||
|
|
@ -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 <functional>
|
||||
|
||||
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<void(const std::string &err, const ProtocolOption &option)>;
|
||||
|
||||
// 收到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<void(const std::string &err)>;
|
||||
|
||||
// 播放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<void()> &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 */
|
||||
|
|
@ -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 <iostream>
|
||||
#include <sstream>
|
||||
#if defined(__MACH__)
|
||||
#include <arpa/inet.h>
|
||||
#include <machine/endian.h>
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#elif defined(__linux__)
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#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<typename ...T>
|
||||
AssertFailedException(T && ...args) : std::runtime_error(std::forward<T>(args)...) {}
|
||||
};
|
||||
|
||||
extern const char kServerName[];
|
||||
|
||||
template <typename... ARGS>
|
||||
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>(args)...);
|
||||
Assert_Throw(failed, exp, func, file, line, ss.str().data());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mediakit
|
||||
#endif // ZLMEDIAKIT_MACROS_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 <iostream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
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_ */
|
||||
|
|
@ -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>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
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>;
|
||||
|
||||
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
|
||||
|
|
@ -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 <CommonRtpDecoder>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @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 <CommonRtpEncoder>;
|
||||
|
||||
/**
|
||||
* 输入帧数据并编码成rtp
|
||||
*/
|
||||
bool inputFrame(const Frame::Ptr &frame) override;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
#endif //ZLMEDIAKIT_COMMONRTP_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 <string>
|
||||
#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
|
||||
|
|
@ -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 <map>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#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<CodecInfo>;
|
||||
|
||||
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<Frame>;
|
||||
|
||||
/**
|
||||
* 返回解码时间戳,单位毫秒
|
||||
*/
|
||||
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<Frame> _statistic;
|
||||
};
|
||||
|
||||
class FrameImp : public Frame {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameImp>;
|
||||
|
||||
template <typename C = FrameImp>
|
||||
static std::shared_ptr<C> create() {
|
||||
#if 0
|
||||
static ResourcePool<C> 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<C>(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<FrameImp> _statistic;
|
||||
|
||||
protected:
|
||||
friend class toolkit::ResourcePool_l<FrameImp>;
|
||||
FrameImp() = default;
|
||||
};
|
||||
|
||||
// 包装一个指针成不可缓存的frame
|
||||
class FrameFromPtr : public Frame {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameFromPtr>;
|
||||
|
||||
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 <typename Parent>
|
||||
class FrameInternalBase : public Parent {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameInternalBase>;
|
||||
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 <typename Parent>
|
||||
class FrameInternal : public FrameInternalBase<Parent> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameInternal>;
|
||||
FrameInternal(const Frame::Ptr &parent_frame, char *ptr, size_t size, size_t prefix_size)
|
||||
: FrameInternalBase<Parent>(parent_frame, ptr, size, parent_frame->dts(), parent_frame->pts(), prefix_size) {}
|
||||
};
|
||||
|
||||
// 管理一个指针生命周期并生产一个frame
|
||||
class FrameAutoDelete : public FrameFromPtr {
|
||||
public:
|
||||
template <typename... ARGS>
|
||||
FrameAutoDelete(ARGS &&...args) : FrameFromPtr(std::forward<ARGS>(args)...) {}
|
||||
|
||||
~FrameAutoDelete() override { delete[] _ptr; };
|
||||
|
||||
bool cacheAble() const override { return true; }
|
||||
};
|
||||
|
||||
// 把一个不可缓存的frame声明为可缓存的
|
||||
template <typename Parent>
|
||||
class FrameToCache : public Parent {
|
||||
public:
|
||||
template<typename ... ARGS>
|
||||
FrameToCache(ARGS &&...args) : Parent(std::forward<ARGS>(args)...) {};
|
||||
|
||||
bool cacheAble() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 该对象的功能是把一个不可缓存的帧转换成可缓存的帧
|
||||
class FrameCacheAble : public FrameFromPtr {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameCacheAble>;
|
||||
|
||||
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<toolkit::BufferLikeString>();
|
||||
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>;
|
||||
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 <typename Parent>
|
||||
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<void(uint64_t dts, uint64_t pts, const toolkit::Buffer::Ptr &buffer, bool have_key_frame)>;
|
||||
using Ptr = std::shared_ptr<FrameMerger>;
|
||||
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::Ptr> _frame_cache;
|
||||
};
|
||||
|
||||
/**
|
||||
* 写帧接口的抽象接口类
|
||||
*/
|
||||
class FrameWriterInterface {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FrameWriterInterface>;
|
||||
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<FrameDispatcher>;
|
||||
|
||||
/**
|
||||
* 添加代理
|
||||
*/
|
||||
FrameWriterInterface* addDelegate(FrameWriterInterface::Ptr delegate) {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
return _delegates.emplace(delegate.get(), std::move(delegate)).first->second.get();
|
||||
}
|
||||
|
||||
FrameWriterInterface* addDelegate(std::function<bool(const Frame::Ptr &frame)> cb);
|
||||
|
||||
/**
|
||||
* 删除代理
|
||||
*/
|
||||
void delDelegate(FrameWriterInterface *ptr) {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
_delegates.erase(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入帧并派发
|
||||
*/
|
||||
bool inputFrame(const Frame::Ptr &frame) override {
|
||||
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> lck(_mtx);
|
||||
return _delegates.size();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
_delegates.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取累计关键帧数
|
||||
*/
|
||||
uint64_t getVideoKeyFrames() const {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
return _video_key_frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取帧数
|
||||
*/
|
||||
uint64_t getFrames() const {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
return _frames;
|
||||
}
|
||||
|
||||
size_t getVideoGopSize() const {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
return _gop_size;
|
||||
}
|
||||
|
||||
size_t getVideoGopInterval() const {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mtx);
|
||||
return _gop_interval_ms;
|
||||
}
|
||||
|
||||
int64_t getDuration() const {
|
||||
std::lock_guard<std::recursive_mutex> 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<void *, FrameWriterInterface::Ptr> _delegates;
|
||||
};
|
||||
|
||||
} // namespace mediakit
|
||||
#endif // ZLMEDIAKIT_FRAME_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 <memory>
|
||||
#include <string>
|
||||
#include "Frame.h"
|
||||
#include "Rtsp/Rtsp.h"
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
/**
|
||||
* 媒体通道描述类,也支持帧输入输出
|
||||
*/
|
||||
class Track : public FrameDispatcher, public CodecInfo {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Track>;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*/
|
||||
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<VideoTrack>;
|
||||
|
||||
/**
|
||||
* 返回视频高度
|
||||
*/
|
||||
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<VideoTrackImp>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @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<VideoTrackImp>(*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<AudioTrack>;
|
||||
|
||||
/**
|
||||
* 返回音频采样率
|
||||
*/
|
||||
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<AudioTrackImp>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @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<AudioTrackImp>(*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<Track::Ptr> 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
|
||||
|
|
@ -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<FMP4Packet>;
|
||||
|
||||
template<typename ...ARGS>
|
||||
FMP4Packet(ARGS && ...args) : toolkit::BufferString(std::forward<ARGS>(args)...) {};
|
||||
|
||||
public:
|
||||
uint64_t time_stamp = 0;
|
||||
};
|
||||
|
||||
//FMP4直播源
|
||||
class FMP4MediaSource final : public MediaSource, public toolkit::RingDelegate<FMP4Packet::Ptr>, private PacketCache<FMP4Packet>{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FMP4MediaSource>;
|
||||
using RingDataType = std::shared_ptr<toolkit::List<FMP4Packet::Ptr> >;
|
||||
using RingType = toolkit::RingBuffer<RingDataType>;
|
||||
|
||||
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<void(const std::list<toolkit::Any> &info_list)> &cb,
|
||||
const std::function<toolkit::Any(toolkit::Any &&info)> &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<FMP4Packet>::inputPacket(stamp, true, std::move(packet), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 情况GOP缓存
|
||||
*/
|
||||
void clearCache() override {
|
||||
PacketCache<FMP4Packet>::clearCache();
|
||||
_ring->clearCache();
|
||||
}
|
||||
|
||||
private:
|
||||
void createRing(){
|
||||
std::weak_ptr<FMP4MediaSource> weak_self = std::static_pointer_cast<FMP4MediaSource>(shared_from_this());
|
||||
_ring = std::make_shared<RingType>(_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<toolkit::List<FMP4Packet::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
|
||||
|
|
@ -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<FMP4MediaSourceMuxer> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
|
||||
|
||||
FMP4MediaSourceMuxer(const MediaTuple& tuple, const ProtocolOption &option) {
|
||||
_option = option;
|
||||
_media_src = std::make_shared<FMP4MediaSource>(tuple);
|
||||
}
|
||||
|
||||
~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); };
|
||||
|
||||
void setListener(const std::weak_ptr<MediaSourceEvent> &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<FMP4Packet>(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
|
||||
|
|
@ -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 <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
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<int, ts_segment> &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
|
||||
|
|
@ -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<HlsDemuxer> {
|
||||
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<Track::Ptr> getTracks(bool ready = true) const override { return _delegate.getTracks(ready); }
|
||||
void pushTask(std::function<void()> 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<std::pair<int64_t, std::function<void()> > > _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<int, ts_segment> &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_segment> _ts_list;
|
||||
std::list<std::string> _ts_url_sort;
|
||||
std::set<std::string, UrlComp> _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<HlsPlayer, PlayerBase>, private TrackListener {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HlsPlayerImp>;
|
||||
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<Track::Ptr> 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
|
||||
|
|
@ -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 <stdlib.h>
|
||||
#include <memory>
|
||||
#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<HttpBody>{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpBody>;
|
||||
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<void(const toolkit::Buffer::Ptr &buf)> &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>;
|
||||
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>;
|
||||
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<HttpFileBody>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @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<FILE> _fp;
|
||||
std::shared_ptr<char> _map_addr;
|
||||
toolkit::ResourcePool<toolkit::BufferRaw> _pool;
|
||||
};
|
||||
|
||||
class HttpArgs;
|
||||
|
||||
/**
|
||||
* http MultiForm 方式提交的http content
|
||||
*/
|
||||
class HttpMultiFormBody : public HttpBody {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpMultiFormBody>;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @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
|
||||
|
|
@ -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 <functional>
|
||||
#include "HttpRequestSplitter.h"
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
class HttpChunkedSplitter : public HttpRequestSplitter {
|
||||
public:
|
||||
/**
|
||||
* len == 0时代表结束
|
||||
*/
|
||||
using onChunkData = std::function<void(const char *data, size_t len)>;
|
||||
|
||||
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
|
||||
|
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#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<std::string, toolkit::variant, StrCaseCompare> {
|
||||
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<HttpClient>;
|
||||
|
||||
/**
|
||||
* 发送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<HttpChunkedSplitter> _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 */
|
||||
|
|
@ -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<HttpClient> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpClientImp>;
|
||||
|
||||
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_ */
|
||||
|
|
@ -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 <string>
|
||||
|
||||
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
|
||||
|
|
@ -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 <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
/**
|
||||
* http客户端cookie对象
|
||||
*/
|
||||
class HttpCookie {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpCookie>;
|
||||
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<HttpCookie::Ptr> get(const std::string &host,const std::string &path);
|
||||
|
||||
private:
|
||||
HttpCookieStorage() = default;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string/*host*/, std::map<std::string/*cookie path*/,std::map<std::string/*cookie_key*/, HttpCookie::Ptr> > > _all_cookie;
|
||||
std::mutex _mtx_cookie;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif //ZLMEDIAKIT_HTTPCOOKIE_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 <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#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<HttpServerCookie>;
|
||||
/**
|
||||
* 构建cookie
|
||||
* @param manager cookie管理者对象
|
||||
* @param cookie_name cookie名,例如MY_SESSION
|
||||
* @param uid 用户唯一id
|
||||
* @param cookie cookie随机字符串
|
||||
* @param max_elapsed 最大过期时间,单位秒
|
||||
*/
|
||||
|
||||
HttpServerCookie(
|
||||
const std::shared_ptr<HttpCookieManager> &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 <class T>
|
||||
T& getAttach() {
|
||||
return _attach.get<T>();
|
||||
}
|
||||
|
||||
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<HttpCookieManager> _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<std::string> _obtained;
|
||||
//增长index,防止碰撞用
|
||||
int _index = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* cookie管理器,用于管理cookie的生成以及过期管理,同时实现了同账号异地挤占登录功能
|
||||
* 该对象实现了同账号最多登录若干个设备
|
||||
*/
|
||||
class HttpCookieManager : public std::enable_shared_from_this<HttpCookieManager> {
|
||||
public:
|
||||
friend class HttpServerCookie;
|
||||
using Ptr = std::shared_ptr<HttpCookieManager>;
|
||||
~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<std::string /*cookie*/, HttpServerCookie::Ptr /*cookie_data*/>>
|
||||
_map_cookie;
|
||||
std::unordered_map<
|
||||
std::string /*cookie_name*/,
|
||||
std::unordered_map<std::string /*uid*/, std::map<uint64_t /*cookie time stamp*/, std::string /*cookie*/>>>
|
||||
_map_uid_to_cookie;
|
||||
std::recursive_mutex _mtx_cookie;
|
||||
toolkit::Timer::Ptr _timer;
|
||||
RandStrGenerator _generator;
|
||||
};
|
||||
|
||||
} // namespace mediakit
|
||||
|
||||
#endif // SRC_HTTP_COOKIEMANAGER_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<HttpDownloader>;
|
||||
using onDownloadResult = std::function<void(const toolkit::SockException &ex, const std::string &filePath)>;
|
||||
|
||||
~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_ */
|
||||
|
|
@ -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<void(int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body)> HttpResponseInvokerLambda0;
|
||||
typedef std::function<void(int code, const StrCaseMap &headerOut, const std::string &body)> HttpResponseInvokerLambda1;
|
||||
|
||||
template<typename C>
|
||||
HttpResponseInvokerImp(const C &c):HttpResponseInvokerImp(typename toolkit::function_traits<C>::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<void(int code, const std::string &content_type, const StrCaseMap &responseHeader, const HttpBody::Ptr &body)> 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
|
||||
|
|
@ -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 <string>
|
||||
#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
|
||||
|
|
@ -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<HttpRequester>;
|
||||
using HttpRequesterResult = std::function<void(const toolkit::SockException &ex, const Parser &response)>;
|
||||
|
||||
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 */
|
||||
|
|
@ -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 <functional>
|
||||
#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<HttpSession>;
|
||||
using KeyValue = StrCaseMap;
|
||||
using HttpResponseInvoker = HttpResponseInvokerImp ;
|
||||
friend class AsyncSender;
|
||||
/**
|
||||
* @param errMsg 如果为空,则代表鉴权通过,否则为错误提示
|
||||
* @param accessPath 运行或禁止访问的根目录
|
||||
* @param cookieLifeSecond 鉴权cookie有效期
|
||||
**/
|
||||
using HttpAccessPathInvoker = std::function<void(const std::string &errMsg,const std::string &accessPath, int cookieLifeSecond)>;
|
||||
|
||||
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<FlvMuxer> 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<void(const MediaSource::Ptr &src)> &cb);
|
||||
|
||||
bool checkLiveStreamFlv(const std::function<void()> &cb = nullptr);
|
||||
bool checkLiveStreamTS(const std::function<void()> &cb = nullptr);
|
||||
bool checkLiveStreamFMP4(const std::function<void()> &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<bool (const char *data,size_t len) > _on_recv_body;
|
||||
};
|
||||
|
||||
using HttpsSession = toolkit::SessionWithSSL<HttpSession>;
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif /* SRC_HTTP_HTTPSESSION_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<HttpTSPlayer>;
|
||||
using onComplete = std::function<void(const toolkit::SockException &)>;
|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 <unordered_set>
|
||||
#include "TsPlayer.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class TsPlayerImp : public PlayerImp<TsPlayer, PlayerBase>, private TrackListener {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<TsPlayerImp>;
|
||||
|
||||
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<Track::Ptr> 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
|
||||
|
|
@ -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 <typename ClientType, WebSocketHeader::Type DataType>
|
||||
class HttpWsClient;
|
||||
|
||||
/**
|
||||
* 辅助类,用于拦截TcpClient数据发送前的拦截
|
||||
* @tparam ClientType TcpClient派生类
|
||||
* @tparam DataType 这里无用,为了声明友元用
|
||||
*/
|
||||
template <typename ClientType, WebSocketHeader::Type DataType>
|
||||
class ClientTypeImp : public ClientType {
|
||||
public:
|
||||
friend class HttpWsClient<ClientType, DataType>;
|
||||
|
||||
using onBeforeSendCB = std::function<ssize_t(const toolkit::Buffer::Ptr &buf)>;
|
||||
|
||||
template <typename... ArgsType>
|
||||
ClientTypeImp(ArgsType &&...args) : ClientType(std::forward<ArgsType>(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 <typename ClientType, WebSocketHeader::Type DataType = WebSocketHeader::TEXT>
|
||||
class HttpWsClient : public HttpClientImp, public WebSocketSplitter {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<HttpWsClient>;
|
||||
|
||||
HttpWsClient(const std::shared_ptr<ClientTypeImp<ClientType, DataType>> &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<HttpHeader &>(headers)["Sec-WebSocket-Accept"]) {
|
||||
// success
|
||||
onWebSocketException(toolkit::SockException());
|
||||
// 防止ws服务器返回Content-Length
|
||||
const_cast<HttpHeader &>(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<WebSocketHeader &>(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<toolkit::BufferString>(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<WebSocketBuffer>(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<WebSocketBuffer>(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<HttpWsClient> weakSelf = std::static_pointer_cast<HttpWsClient>(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<void(const char *data, size_t len)> _onRecv;
|
||||
std::weak_ptr<ClientTypeImp<ClientType, DataType>> _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 <typename ClientType, WebSocketHeader::Type DataType = WebSocketHeader::TEXT, bool useWSS = false>
|
||||
class WebSocketClient : public ClientTypeImp<ClientType, DataType> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<WebSocketClient>;
|
||||
|
||||
template <typename... ArgsType>
|
||||
WebSocketClient(ArgsType &&...args) : ClientTypeImp<ClientType, DataType>(std::forward<ArgsType>(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<HttpWsClient<ClientType, DataType>>(std::static_pointer_cast<WebSocketClient>(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<ClientType, DataType>::Ptr _wsClient;
|
||||
};
|
||||
|
||||
} // namespace mediakit
|
||||
#endif // ZLMEDIAKIT_WebSocketClient_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<ssize_t (const toolkit::Buffer::Ptr &buf)>;
|
||||
|
||||
virtual ~SendInterceptor() = default;
|
||||
virtual void setOnBeforeSendCB(const onBeforeSendCB &cb) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* 该类实现了Session派生类发送数据的截取
|
||||
* 目的是发送业务数据前进行websocket协议的打包
|
||||
*/
|
||||
template <typename SessionType>
|
||||
class SessionTypeImp : public SessionType, public SendInterceptor{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<SessionTypeImp>;
|
||||
|
||||
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 <typename SessionType>
|
||||
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<SessionTypeImp<SessionType> >(header,parent,pSock);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过该模板类可以透明化WebSocket协议,
|
||||
* 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等
|
||||
*/
|
||||
template<typename Creator, typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
|
||||
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<toolkit::Server &>(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<WebSocketSessionBase> weakSelf = std::static_pointer_cast<WebSocketSessionBase>(HttpSessionType::shared_from_this());
|
||||
std::dynamic_pointer_cast<SendInterceptor>(_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<mediakit::WebSocketHeader&>(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<toolkit::BufferString>(_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<mediakit::WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_section)));
|
||||
break;
|
||||
}
|
||||
|
||||
//这个包由多个分片组成
|
||||
_payload_cache.append(std::move(_payload_section));
|
||||
_session->onRecv(std::make_shared<mediakit::WebSocketBuffer>(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<toolkit::Server> _weak_server;
|
||||
toolkit::Session::Ptr _session;
|
||||
Creator _creator;
|
||||
toolkit::Ticker _recv_ticker;
|
||||
};
|
||||
|
||||
|
||||
template<typename SessionType,typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
|
||||
class WebSocketSession : public WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>{
|
||||
public:
|
||||
WebSocketSession(const toolkit::Socket::Ptr &pSock) : WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>(pSock){}
|
||||
};
|
||||
|
||||
#endif //ZLMEDIAKIT_WEBSOCKETSESSION_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 <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "Network/Buffer.h"
|
||||
|
||||
//websocket组合包最大不得超过4MB(防止内存爆炸)
|
||||
#define MAX_WS_PACKET (4 * 1024 * 1024)
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class WebSocketHeader {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<WebSocketHeader>;
|
||||
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<uint8_t > _mask;
|
||||
};
|
||||
|
||||
//websocket协议收到的字符串类型缓存,用户协议层获取该数据传输的方式
|
||||
class WebSocketBuffer : public toolkit::BufferString {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<WebSocketBuffer>;
|
||||
|
||||
template<typename ...ARGS>
|
||||
WebSocketBuffer(WebSocketHeader::Type headType, bool fin, ARGS &&...args)
|
||||
: toolkit::BufferString(std::forward<ARGS>(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
|
||||
|
|
@ -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 <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include "Util/util.h"
|
||||
#include "Util/ResourcePool.h"
|
||||
|
||||
namespace toolkit {
|
||||
|
||||
template <typename T> struct is_pointer : public std::false_type {};
|
||||
template <typename T> struct is_pointer<std::shared_ptr<T> > : public std::true_type {};
|
||||
template <typename T> struct is_pointer<std::shared_ptr<T const> > : public std::true_type {};
|
||||
template <typename T> struct is_pointer<T*> : public std::true_type {};
|
||||
template <typename T> struct is_pointer<const T*> : public std::true_type {};
|
||||
|
||||
//缓存基类
|
||||
class Buffer : public noncopyable {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Buffer>;
|
||||
|
||||
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<Buffer> _statistic;
|
||||
};
|
||||
|
||||
template <typename C>
|
||||
class BufferOffset : public Buffer {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<BufferOffset>;
|
||||
|
||||
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<char *>(getPointer<C>(_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<C>(_data)->size();
|
||||
assert(offset + size <= max_size);
|
||||
if (!size) {
|
||||
size = max_size - offset;
|
||||
}
|
||||
_size = size;
|
||||
_offset = offset;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename std::enable_if<::toolkit::is_pointer<T>::value, const T &>::type
|
||||
getPointer(const T &data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static typename std::enable_if<!::toolkit::is_pointer<T>::value, const T *>::type
|
||||
getPointer(const T &data) {
|
||||
return &data;
|
||||
}
|
||||
|
||||
private:
|
||||
C _data;
|
||||
size_t _size;
|
||||
size_t _offset;
|
||||
};
|
||||
|
||||
using BufferString = BufferOffset<std::string>;
|
||||
|
||||
//指针式缓存对象,
|
||||
class BufferRaw : public Buffer {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<BufferRaw>;
|
||||
|
||||
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>;
|
||||
|
||||
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<BufferRaw> _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<BufferLikeString *>(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<BufferLikeString> _statistic;
|
||||
};
|
||||
|
||||
}//namespace toolkit
|
||||
#endif //ZLTOOLKIT_BUFFER_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 <sys/uio.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#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>;
|
||||
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<BufferList>;
|
||||
using SendResult = std::function<void(const Buffer::Ptr &buffer, bool send_success)>;
|
||||
|
||||
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<std::pair<Buffer::Ptr, bool> > list, SendResult cb, bool is_udp);
|
||||
|
||||
private:
|
||||
//对象个数统计
|
||||
ObjectStatistic<BufferList> _statistic;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //ZLTOOLKIT_BUFFERSOCK_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 <unordered_map>
|
||||
#include "Util/mini.h"
|
||||
#include "Session.h"
|
||||
|
||||
namespace toolkit {
|
||||
|
||||
// 全局的 Session 记录对象, 方便后面管理
|
||||
// 线程安全的
|
||||
class SessionMap : public std::enable_shared_from_this<SessionMap> {
|
||||
public:
|
||||
friend class SessionHelper;
|
||||
using Ptr = std::shared_ptr<SessionMap>;
|
||||
|
||||
//单例
|
||||
static SessionMap &Instance();
|
||||
~SessionMap() = default;
|
||||
|
||||
//获取Session
|
||||
Session::Ptr get(const std::string &tag);
|
||||
void for_each_session(const std::function<void(const std::string &id, const Session::Ptr &session)> &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<std::string, std::weak_ptr<Session> > _map_session;
|
||||
};
|
||||
|
||||
class Server;
|
||||
|
||||
class SessionHelper {
|
||||
public:
|
||||
bool enable = true;
|
||||
|
||||
using Ptr = std::shared_ptr<SessionHelper>;
|
||||
|
||||
SessionHelper(const std::weak_ptr<Server> &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;
|
||||
};
|
||||
|
||||
// server 基类, 暂时仅用于剥离 SessionHelper 对 TcpServer 的依赖
|
||||
// 后续将 TCP 与 UDP 服务通用部分加到这里.
|
||||
class Server : public std::enable_shared_from_this<Server>, public mINI {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Server>;
|
||||
|
||||
explicit Server(EventPoller::Ptr poller = nullptr);
|
||||
virtual ~Server() = default;
|
||||
|
||||
protected:
|
||||
EventPoller::Ptr _poller;
|
||||
};
|
||||
|
||||
} // namespace toolkit
|
||||
|
||||
#endif // ZLTOOLKIT_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_SESSION_H
|
||||
#define ZLTOOLKIT_SESSION_H
|
||||
|
||||
#include <memory>
|
||||
#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>;
|
||||
|
||||
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<toolkit::ObjectStatistic<toolkit::TcpSession> > _statistic_tcp;
|
||||
std::unique_ptr<toolkit::ObjectStatistic<toolkit::UdpSession> > _statistic_udp;
|
||||
};
|
||||
|
||||
// 通过该模板可以让TCP服务器快速支持TLS
|
||||
template <typename SessionType>
|
||||
class SessionWithSSL : public SessionType {
|
||||
public:
|
||||
template <typename... ArgsType>
|
||||
SessionWithSSL(ArgsType &&...args)
|
||||
: SessionType(std::forward<ArgsType>(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
|
||||
|
|
@ -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 <memory>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#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<SockNum>;
|
||||
|
||||
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<SockFD>;
|
||||
|
||||
/**
|
||||
* 创建一个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 Mtx = std::recursive_mutex>
|
||||
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<Socket>, public noncopyable, public SockInfo {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Socket>;
|
||||
//接收数据回调
|
||||
using onReadCB = std::function<void(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len)>;
|
||||
//发生错误回调
|
||||
using onErrCB = std::function<void(const SockException &err)>;
|
||||
//tcp监听接收到连接请求
|
||||
using onAcceptCB = std::function<void(Socket::Ptr &sock, std::shared_ptr<void> &complete)>;
|
||||
//socket发送缓存清空事件,返回true代表下次继续监听该事件,否则停止
|
||||
using onFlush = std::function<bool()>;
|
||||
//在接收到连接请求前,拦截Socket默认生成方式
|
||||
using onCreateSocket = std::function<Ptr(const EventPoller::Ptr &poller)>;
|
||||
//发送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<bool> _enable_recv {true};
|
||||
//标记该socket是否可写,socket写缓存满了就不可写
|
||||
std::atomic<bool> _sendable {true};
|
||||
//是否已经触发err回调了
|
||||
bool _err_emit = false;
|
||||
//是否启用网速统计
|
||||
bool _enable_speed = false;
|
||||
// udp发送目标地址
|
||||
std::shared_ptr<struct sockaddr_storage> _udp_send_dst;
|
||||
|
||||
//接收速率统计
|
||||
BytesSpeed _recv_speed;
|
||||
//发送速率统计
|
||||
BytesSpeed _send_speed;
|
||||
|
||||
//tcp连接超时定时器
|
||||
Timer::Ptr _con_timer;
|
||||
//tcp连接结果回调对象
|
||||
std::shared_ptr<void> _async_con_cb;
|
||||
|
||||
//记录上次发送缓存(包括socket写缓存、应用层缓存)清空的计时器
|
||||
Ticker _send_flush_ticker;
|
||||
//socket fd的抽象类
|
||||
SockFD::Ptr _sock_fd;
|
||||
//本socket绑定的poller线程,事件触发于此线程
|
||||
EventPoller::Ptr _poller;
|
||||
//跨线程访问_sock_fd时需要上锁
|
||||
mutable MutexWrapper<std::recursive_mutex> _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<std::recursive_mutex> _mtx_event;
|
||||
|
||||
//一级发送缓存, socket可写时,会把一级缓存批量送入到二级缓存
|
||||
List<std::pair<Buffer::Ptr, bool> > _send_buf_waiting;
|
||||
//一级发送缓存锁
|
||||
MutexWrapper<std::recursive_mutex> _mtx_send_buf_waiting;
|
||||
//二级发送缓存, socket可写时,会把二级缓存批量写入到socket
|
||||
List<BufferList::Ptr> _send_buf_sending;
|
||||
//二级发送缓存锁
|
||||
MutexWrapper<std::recursive_mutex> _mtx_send_buf_sending;
|
||||
//发送buffer结果回调
|
||||
BufferList::SendResult _send_result;
|
||||
//对象个数统计
|
||||
ObjectStatistic<Socket> _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<typename T>
|
||||
SockSender &operator << (T &&buf) {
|
||||
std::ostringstream ss;
|
||||
ss << std::forward<T>(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<SocketHelper> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<SocketHelper>;
|
||||
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 */
|
||||
|
|
@ -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 <memory>
|
||||
#include "Socket.h"
|
||||
#include "Util/SSLBox.h"
|
||||
|
||||
namespace toolkit {
|
||||
|
||||
//Tcp客户端,Socket对象默认开始互斥锁
|
||||
class TcpClient : public SocketHelper {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<TcpClient>;
|
||||
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> _timer;
|
||||
//对象个数统计
|
||||
ObjectStatistic<TcpClient> _statistic;
|
||||
};
|
||||
|
||||
//用于实现TLS客户端的模板对象
|
||||
template<typename TcpClientType>
|
||||
class TcpClientWithSSL : public TcpClientType {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<TcpClientWithSSL>;
|
||||
|
||||
template<typename ...ArgsType>
|
||||
TcpClientWithSSL(ArgsType &&...args):TcpClientType(std::forward<ArgsType>(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<SSL_Box>(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> _ssl_box;
|
||||
};
|
||||
|
||||
} /* namespace toolkit */
|
||||
#endif /* NETWORK_TCPCLIENT_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 <memory>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#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<TcpServer>;
|
||||
|
||||
/**
|
||||
* 创建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<typename SessionType>
|
||||
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<SessionType>(new SessionType(sock), [](SessionType * ptr) {
|
||||
TraceP(static_cast<Session *>(ptr)) << "~" << cls_name;
|
||||
delete ptr;
|
||||
});
|
||||
TraceP(static_cast<Session *>(session.get())) << cls_name;
|
||||
session->setOnCreateSocket(server->_on_create_socket);
|
||||
return std::make_shared<SessionHelper>(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<TcpServer> _parent;
|
||||
Socket::Ptr _socket;
|
||||
std::shared_ptr<Timer> _timer;
|
||||
Socket::onCreateSocket _on_create_socket;
|
||||
std::unordered_map<SessionHelper *, SessionHelper::Ptr> _session_map;
|
||||
std::function<SessionHelper::Ptr(const TcpServer::Ptr &server, const Socket::Ptr &)> _session_alloc;
|
||||
std::unordered_map<const EventPoller *, Ptr> _cloned_server;
|
||||
//对象个数统计
|
||||
ObjectStatistic<TcpServer> _statistic;
|
||||
};
|
||||
|
||||
} /* namespace toolkit */
|
||||
#endif /* TCPSERVER_TCPSERVER_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<UdpServer>;
|
||||
using PeerIdType = std::string;
|
||||
using onCreateSocket = std::function<Socket::Ptr(const EventPoller::Ptr &, const Buffer::Ptr &, struct sockaddr *, int)>;
|
||||
|
||||
explicit UdpServer(const EventPoller::Ptr &poller = nullptr);
|
||||
~UdpServer() override;
|
||||
|
||||
/**
|
||||
* @brief 开始监听服务器
|
||||
*/
|
||||
template<typename SessionType>
|
||||
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<SessionType>(new SessionType(sock), [](SessionType * ptr) {
|
||||
TraceP(static_cast<Session *>(ptr)) << "~" << cls_name;
|
||||
delete ptr;
|
||||
});
|
||||
TraceP(static_cast<Session *>(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<SessionHelper>(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> _timer;
|
||||
onCreateSocket _on_create_socket;
|
||||
//cloned server共享主server的session map,防止数据在不同server间漂移
|
||||
std::shared_ptr<std::recursive_mutex> _session_mutex;
|
||||
std::shared_ptr<std::unordered_map<PeerIdType, SessionHelper::Ptr> > _session_map;
|
||||
//主server持有cloned server的引用
|
||||
std::unordered_map<EventPoller *, Ptr> _cloned_server;
|
||||
std::function<SessionHelper::Ptr(const UdpServer::Ptr &, const Socket::Ptr &)> _session_alloc;
|
||||
// 对象个数统计
|
||||
ObjectStatistic<UdpServer> _statistic;
|
||||
};
|
||||
|
||||
} // namespace toolkit
|
||||
|
||||
#endif // TOOLKIT_NETWORK_UDPSERVER_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 <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#pragma comment (lib, "Ws2_32.lib")
|
||||
#pragma comment(lib,"Iphlpapi.lib")
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
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<map<ip:name> >
|
||||
*/
|
||||
static std::vector<std::map<std::string, std::string>> 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
|
||||
|
|
@ -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 <memory>
|
||||
#include <string>
|
||||
#include "PlayerBase.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class MediaPlayer : public PlayerImp<PlayerBase, PlayerBase> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<MediaPlayer>;
|
||||
|
||||
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_ */
|
||||
|
|
@ -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 <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#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<PlayerBase>;
|
||||
using Event = std::function<void(const toolkit::SockException &ex)>;
|
||||
|
||||
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<Track::Ptr> getTracks(bool ready = true) const override { return std::vector<Track::Ptr>(); };
|
||||
|
||||
/**
|
||||
* 设置一个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<void()> &cb) = 0;
|
||||
|
||||
protected:
|
||||
virtual void onResume() = 0;
|
||||
virtual void onShutdown(const toolkit::SockException &ex) = 0;
|
||||
virtual void onPlayResult(const toolkit::SockException &ex) = 0;
|
||||
};
|
||||
|
||||
template<typename Parent, typename Delegate>
|
||||
class PlayerImp : public Parent {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<PlayerImp>;
|
||||
|
||||
template<typename ...ArgsType>
|
||||
PlayerImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(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<Track::Ptr> getTracks(bool ready = true) const override {
|
||||
return _delegate ? _delegate->getTracks(ready) : Parent::getTracks(ready);
|
||||
}
|
||||
|
||||
std::shared_ptr<toolkit::SockInfo> getSockInfo() const {
|
||||
return std::dynamic_pointer_cast<toolkit::SockInfo>(_delegate);
|
||||
}
|
||||
|
||||
void setMediaSource(const MediaSource::Ptr &src) override {
|
||||
if (_delegate) {
|
||||
_delegate->setMediaSource(src);
|
||||
}
|
||||
_media_src = src;
|
||||
}
|
||||
|
||||
void setOnShutdown(const std::function<void(const toolkit::SockException &)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnShutdown(cb);
|
||||
}
|
||||
_on_shutdown = cb;
|
||||
}
|
||||
|
||||
void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
|
||||
if (_delegate) {
|
||||
_delegate->setOnPlayResult(cb);
|
||||
}
|
||||
_on_play_result = cb;
|
||||
}
|
||||
|
||||
void setOnResume(const std::function<void()> &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<void()> _on_resume;
|
||||
PlayerBase::Event _on_shutdown;
|
||||
PlayerBase::Event _on_play_result;
|
||||
MediaSource::Ptr _media_src;
|
||||
std::shared_ptr<Delegate> _delegate;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif /* SRC_PLAYER_PLAYERBASE_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 <memory>
|
||||
|
||||
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<StreamInfo> 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<PlayerProxy> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<PlayerProxy>;
|
||||
|
||||
// 如果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<void(const toolkit::SockException &ex)> cb);
|
||||
|
||||
/**
|
||||
* 设置主动关闭回调
|
||||
* @param cb 回调对象
|
||||
*/
|
||||
void setOnClose(std::function<void(const toolkit::SockException &ex)> cb);
|
||||
|
||||
/**
|
||||
* Set a callback for failed server connection
|
||||
* @param cb 回调对象
|
||||
*/
|
||||
void setOnDisconnect(std::function<void()> cb);
|
||||
|
||||
/**
|
||||
* Set a callback for a successful connection to the server
|
||||
* @param cb 回调对象
|
||||
*/
|
||||
void setOnConnect(std::function<void(const TranslationInfo&)> 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<toolkit::SockInfo> 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<void()> _on_disconnect;
|
||||
std::function<void(const TranslationInfo &info)> _on_connect;
|
||||
std::function<void(const toolkit::SockException &ex)> _on_close;
|
||||
std::function<void(const toolkit::SockException &ex)> _on_play;
|
||||
TranslationInfo _transtalion_info;
|
||||
MultiMediaSourceMuxer::Ptr _muxer;
|
||||
|
||||
toolkit::Ticker _live_ticker;
|
||||
// 0 表示正常 1 表示正在尝试拉流
|
||||
std::atomic<int> _live_status;
|
||||
std::atomic<uint64_t> _live_secs;
|
||||
|
||||
std::atomic<uint64_t> _repull_count;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
#endif /* SRC_DEVICE_PLAYERPROXY_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 <mutex>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#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<EventPoller> {
|
||||
public:
|
||||
friend class TaskExecutorGetterImp;
|
||||
|
||||
using Ptr = std::shared_ptr<EventPoller>;
|
||||
using PollEventCB = std::function<void(int event)>;
|
||||
using PollCompleteCB = std::function<void(bool success)>;
|
||||
using DelayTask = TaskCancelableImp<uint64_t(void)>;
|
||||
|
||||
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<uint64_t()> 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<BufferRaw> _shared_buffer;
|
||||
//执行事件循环的线程
|
||||
std::thread *_loop_thread = nullptr;
|
||||
//通知事件循环的线程已启动
|
||||
semaphore _sem_run_started;
|
||||
|
||||
//内部事件管道
|
||||
PipeWrap _pipe;
|
||||
//从其他线程切换过来的任务
|
||||
std::mutex _mtx_task;
|
||||
List<Task::Ptr> _list_task;
|
||||
|
||||
//保持日志可用
|
||||
Logger::Ptr _logger;
|
||||
|
||||
#if defined(HAS_EPOLL)
|
||||
//epoll相关
|
||||
int _epoll_fd = -1;
|
||||
unordered_map<int, std::shared_ptr<PollEventCB> > _event_map;
|
||||
#else
|
||||
//select相关
|
||||
struct Poll_Record {
|
||||
using Ptr = std::shared_ptr<Poll_Record>;
|
||||
int fd;
|
||||
int event;
|
||||
int attach;
|
||||
PollEventCB call_back;
|
||||
};
|
||||
unordered_map<int, Poll_Record::Ptr> _event_map;
|
||||
#endif //HAS_EPOLL
|
||||
unordered_map<int, bool> _event_cache_expired_map;
|
||||
|
||||
//定时器相关
|
||||
std::multimap<uint64_t, DelayTask::Ptr> _delay_task_map;
|
||||
};
|
||||
|
||||
class EventPollerPool : public std::enable_shared_from_this<EventPollerPool>, public TaskExecutorGetterImp {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<EventPollerPool>;
|
||||
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 */
|
||||
|
|
@ -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 <functional>
|
||||
#include "PipeWrap.h"
|
||||
#include "EventPoller.h"
|
||||
|
||||
namespace toolkit {
|
||||
|
||||
class Pipe {
|
||||
public:
|
||||
using onRead = std::function<void(int size, const char *buf)>;
|
||||
|
||||
Pipe(const onRead &cb = nullptr, const EventPoller::Ptr &poller = nullptr);
|
||||
~Pipe();
|
||||
|
||||
void send(const char *send, int size = 0);
|
||||
|
||||
private:
|
||||
std::shared_ptr<PipeWrap> _pipe;
|
||||
EventPoller::Ptr _poller;
|
||||
};
|
||||
|
||||
} // namespace toolkit
|
||||
#endif /* 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 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
|
||||
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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 <functional>
|
||||
#include "EventPoller.h"
|
||||
|
||||
namespace toolkit {
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<Timer>;
|
||||
|
||||
/**
|
||||
* 构造定时器
|
||||
* @param second 定时器重复秒数
|
||||
* @param cb 定时器任务,返回true表示重复下次任务,否则不重复,如果任务中抛异常,则默认重复下次任务
|
||||
* @param poller EventPoller对象,可以为nullptr
|
||||
*/
|
||||
Timer(float second, const std::function<bool()> &cb, const EventPoller::Ptr &poller);
|
||||
~Timer();
|
||||
|
||||
private:
|
||||
std::weak_ptr<EventPoller::DelayTask> _tag;
|
||||
//定时器保持EventPoller的强引用
|
||||
EventPoller::Ptr _poller;
|
||||
};
|
||||
|
||||
} // namespace toolkit
|
||||
#endif /* Timer_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 <memory>
|
||||
#include <string>
|
||||
#include "PusherBase.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class MediaPusher : public PusherImp<PusherBase,PusherBase> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<MediaPusher>;
|
||||
|
||||
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<MediaSource> _src;
|
||||
toolkit::EventPoller::Ptr _poller;
|
||||
toolkit::Socket::onCreateSocket _on_create_socket;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif /* SRC_PUSHER_MEDIAPUSHER_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 <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#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<PusherBase>;
|
||||
using Event = std::function<void(const toolkit::SockException &ex)>;
|
||||
|
||||
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<typename Parent, typename Delegate>
|
||||
class PusherImp : public Parent {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<PusherImp>;
|
||||
|
||||
template<typename ...ArgsType>
|
||||
PusherImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(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<toolkit::SockInfo> getSockInfo() const {
|
||||
return std::dynamic_pointer_cast<toolkit::SockInfo>(_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> _delegate;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
#endif /* SRC_PUSHER_PUSHERBASE_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<PusherProxy> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<PusherProxy>;
|
||||
|
||||
// 如果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<void(const toolkit::SockException &ex)> &cb);
|
||||
|
||||
/**
|
||||
* 设置主动关闭回调
|
||||
* @param cb 回调对象
|
||||
*/
|
||||
void setOnClose(const std::function<void(const toolkit::SockException &ex)> &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<int> _live_status;
|
||||
std::atomic<uint64_t> _live_secs;
|
||||
std::atomic<uint64_t> _republish_count;
|
||||
std::weak_ptr<MediaSource> _weak_src;
|
||||
std::function<void(const toolkit::SockException &ex)> _on_close;
|
||||
std::function<void(const toolkit::SockException &ex)> _on_publish;
|
||||
};
|
||||
|
||||
} /* namespace mediakit */
|
||||
|
||||
#endif // SRC_DEVICE_PUSHERPROXY_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue