android rtc播放、推流基本ok
2
webrtc_player/android/.gitattributes
vendored
@ -1,2 +0,0 @@
|
||||
zlm/libs/arm64-v8a/libZLToolKit.a filter=lfs diff=lfs merge=lfs -text
|
||||
zlm/libs/armeabi-v7a/libZLToolKit.a filter=lfs diff=lfs merge=lfs -text
|
1
webrtc_player/android/.gitignore
vendored
@ -13,4 +13,3 @@
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/.idea/
|
||||
|
3
webrtc_player/android/.idea/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
1
webrtc_player/android/.idea/.name
Normal file
@ -0,0 +1 @@
|
||||
RTCPlayer
|
6
webrtc_player/android/.idea/compiler.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
10
webrtc_player/android/.idea/deploymentTargetDropDown.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="app">
|
||||
<State />
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
18
webrtc_player/android/.idea/gradle.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
6
webrtc_player/android/.idea/kotlinc.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.22" />
|
||||
</component>
|
||||
</project>
|
10
webrtc_player/android/.idea/migrations.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
10
webrtc_player/android/.idea/misc.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
6
webrtc_player/android/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
104
webrtc_player/android/README.md
Normal file
@ -0,0 +1,104 @@
|
||||
# RTCPlayer 播放器
|
||||
|
||||
一个基于Android客户端的的RTC播放器
|
||||
|
||||
## 项目特点
|
||||
|
||||
- 低延迟
|
||||
- [X]支持[ZLMediakit](https://github.com/ZLMediaKit/ZLMediaKit)流媒体
|
||||
- [ ]支持[SRS](https://github.com/ossrs/srs)
|
||||
- [ ]支持[Janus](https://github.com/meetecho/janus-gateway)
|
||||
|
||||
<center class="half">
|
||||
<img src="doc/home.jpg" width="240"/>
|
||||
<img src="doc/player.jpg" width="240"/>
|
||||
<img src="doc/pusher.jpg" width="240"/>
|
||||
</center>
|
||||
|
||||
## 延迟情况
|
||||
|
||||
- **网页端推流,Android端播放**
|
||||
|
||||
<center class="half">
|
||||
<img src="doc/delay_play1.jpg" width="240"/>
|
||||
<img src="doc/delay_play2.jpg" width="240"/>
|
||||
<img src="doc/delay_play3.jpg" width="240"/>
|
||||
</center>
|
||||
|
||||
三次延迟分别为:490ms、526ms、560ms
|
||||
|
||||
- **Android端推屏幕流,网页端播放**
|
||||
|
||||
<center class="half">
|
||||
<img src="doc/delay_push1.jpg" width="240"/>
|
||||
<img src="doc/delay_push2.jpg" width="240"/>
|
||||
<img src="doc/delay_push3.jpg" width="240"/>
|
||||
</center>
|
||||
|
||||
三次延迟分别为:440ms、430ms、387ms
|
||||
|
||||
## 接口说明
|
||||
|
||||
### Player
|
||||
|
||||
- bind(surface: RTCSurfaceView)
|
||||
|
||||
绑定视图
|
||||
|
||||
- play(app: String, streamId: String)
|
||||
|
||||
播放
|
||||
- setVolume(volume:Float)
|
||||
|
||||
设置音量,范围: 0.0~1.0f
|
||||
- stop()
|
||||
|
||||
停止播放
|
||||
- pause()
|
||||
|
||||
暂停播放
|
||||
- resume()
|
||||
|
||||
恢复播放
|
||||
- capture(listener: (bitmap: Bitmap) -> Unit)
|
||||
|
||||
截图
|
||||
- record(duration: Long, result: (path: String) -> Unit)
|
||||
|
||||
录制
|
||||
- setOnErrorListener(listener: (code: Int, msg: String) -> Unit)
|
||||
|
||||
设置播放器错误监听回调
|
||||
- setOnStatusListener(listener: (status: Status) -> Unit)
|
||||
|
||||
设置播放器状态回调
|
||||
|
||||
### Pusher
|
||||
|
||||
- bind(surface: RTCSurfaceView, localPreview: Boolean)
|
||||
|
||||
绑定视图
|
||||
|
||||
- push(app: String, streamId: String, mode: PushMode = PushMode.CAMERA, inputFile: String = "")
|
||||
|
||||
推流,支持Camera、Screen、File
|
||||
|
||||
- stop()
|
||||
|
||||
停止推流
|
||||
|
||||
- setOnErrorListener(listener: (code: Int, msg: String) -> Unit)
|
||||
|
||||
设置播放器错误监听回调
|
||||
|
||||
## 联系作者
|
||||
如果您需要深度二次开发,并支持其他流媒体服务,可以找我哦!
|
||||
[李之阳](https://github.com/leo94666)
|
||||
|
||||
## 特别感谢
|
||||
|
||||
感谢[ZLMediakit](https://github.com/ZLMediaKit/ZLMediaKit)开源项目
|
||||
|
||||
同时感谢JetBrains对开源项目的支持,本项目使用Android Studio开发与调试:
|
||||
|
||||
[![Android Studio](https://th.bing.com/th?id=ODLS.d2ea10a5-5792-4f82-bd13-1595fd9d969c&w=32&h=32&qlt=90&pcl=fffffa&o=6&pid=1.2)](https://developer.android.com/studio?hl=zh-cn)
|
3
webrtc_player/android/app/.gitignore
vendored
@ -1,2 +1 @@
|
||||
/build
|
||||
.cxx
|
||||
/build
|
@ -1,56 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'kotlin-android-extensions'
|
||||
id 'kotlin-kapt'
|
||||
|
||||
}
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdk 32
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.zlmediakit.webrtc"
|
||||
minSdk 24
|
||||
targetSdk 32
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
implementation project(':zlm')
|
||||
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
|
||||
|
||||
|
||||
}
|
58
webrtc_player/android/app/build.gradle.kts
Normal file
@ -0,0 +1,58 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.top.player"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.top.player"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
viewBinding {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.appcompat:appcompat:1.5.1")
|
||||
implementation("com.google.android.material:material:1.6.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22")
|
||||
implementation("com.guolindev.permissionx:permissionx:1.7.1")
|
||||
|
||||
//implementation(project(":RTCPlayer"))
|
||||
implementation("com.rtc.core:RTCPlayer:1.0.0.beta")
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.top.player;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
assertEquals("com.top.player", appContext.getPackageName());
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package com.zlmediakit.webrtc
|
||||
|
||||
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.zlmediakit.webrtc", appContext.packageName)
|
||||
}
|
||||
}
|
@ -1,24 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.zlmediakit.webrtc">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature android:name="android.hardware.camera"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true"/>
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@ -27,15 +30,14 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AndroidWebRTC"
|
||||
tools:overrideLibrary="com.zlm.rtc"
|
||||
android:theme="@style/Theme.RTCPlayer"
|
||||
tools:targetApi="31"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:name=".App"
|
||||
tools:targetApi="31">
|
||||
tools:overrideLibrary="com.rtc.core"
|
||||
android:name=".App">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -43,12 +45,13 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".PlayDemoActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity android:name=".PlayerDemoActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity android:name=".PusherDemoActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
|
||||
<activity
|
||||
android:name=".PlayerDemoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".PusherDemoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,4 +1,4 @@
|
||||
package com.zlmediakit.webrtc
|
||||
package com.top.player
|
||||
|
||||
import android.app.Application
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.zlmediakit.webrtc
|
||||
package com.top.player
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
@ -11,7 +11,6 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun toPlayActivity(view: View) {
|
||||
@ -23,7 +22,4 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
|
||||
fun toDataChannelActivity(view: View) {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package com.top.player
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.rtc.core.ZLMRTCPlayer
|
||||
import com.rtc.core.play.Status
|
||||
import com.rtc.core.play.ZLMRTCPlayerImpl
|
||||
import com.top.player.databinding.ActivityPlayerBinding
|
||||
|
||||
|
||||
class PlayerDemoActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
private val player: ZLMRTCPlayer by lazy {
|
||||
ZLMRTCPlayerImpl(this)
|
||||
}
|
||||
|
||||
|
||||
private val binding by lazy {
|
||||
ActivityPlayerBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(binding.root)
|
||||
|
||||
//ffmpeg -re -stream_loop -1 -i "D:\li\hot\data\data\baseline.mp4" -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp -bf 0 rtsp://zlmediakit.com/live/li
|
||||
//ffmpeg -re -stream_loop -1 -i "D:\li\hot\data\data\test.mp4" -vcodec h264 -acodec aac -f flv -bf 0 rtmp://zlmediakit.com/live/li
|
||||
|
||||
setTitle("Player Demo")
|
||||
player.bind(binding.surfaceViewRender)
|
||||
|
||||
player.setOnErrorListener { code, msg ->
|
||||
Toast.makeText(this, "code:$code,msg:${msg}", Toast.LENGTH_SHORT).show()
|
||||
|
||||
}
|
||||
|
||||
player.setOnStatusListener {
|
||||
when (it) {
|
||||
Status.PREPARING -> {
|
||||
binding.tvStatus.text = "准备播放"
|
||||
}
|
||||
|
||||
Status.PLAYING -> {
|
||||
binding.tvStatus.text = "播放中.."
|
||||
}
|
||||
|
||||
Status.PAUSE -> {
|
||||
binding.tvStatus.text = "暂停中.."
|
||||
}
|
||||
|
||||
Status.RESUME -> {
|
||||
binding.tvStatus.text = "播放中.."
|
||||
}
|
||||
|
||||
Status.STOP -> {
|
||||
binding.tvStatus.text = ""
|
||||
}
|
||||
|
||||
Status.ERROR -> {
|
||||
binding.tvStatus.text = "播放异常"
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
player.stop()
|
||||
}
|
||||
|
||||
fun onPlayClick(view: View) {
|
||||
|
||||
player.play(binding.tvApp.text.toString(), binding.tvStreamId.text.toString())
|
||||
}
|
||||
|
||||
fun onPauseClick(view: View) {
|
||||
player.pause()
|
||||
//Toast.makeText(this, "ok", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun onStopClick(view: View) {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
fun onResumeClick(view: View) {
|
||||
player.resume()
|
||||
}
|
||||
|
||||
fun onCapture(view: View) {
|
||||
player.capture {
|
||||
Toast.makeText(this, "capture ok", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onRecord(view: View) {
|
||||
player.record(10 * 1000) {
|
||||
Toast.makeText(this, "" + it, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onVolume(view: View) {
|
||||
player.setVolume(0.0f)
|
||||
}
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
package com.zlmediakit.webrtc
|
||||
package com.top.player
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.permissionx.guolindev.PermissionX
|
||||
import com.permissionx.guolindev.callback.RequestCallback
|
||||
import com.zlm.rtc.ZLMRTCPusher
|
||||
import com.zlm.rtc.push.PushMode
|
||||
import com.zlm.rtc.push.ZLMRTCPusherImpl
|
||||
import kotlinx.android.synthetic.main.activity_player.surface_view_renderer
|
||||
import kotlinx.android.synthetic.main.activity_player.tv_app
|
||||
import kotlinx.android.synthetic.main.activity_player.tv_stream_id
|
||||
import com.rtc.core.RTCSurfaceView
|
||||
import com.rtc.core.ZLMRTCPusher
|
||||
import com.rtc.core.push.PushMode
|
||||
import com.rtc.core.push.ZLMRTCPusherImpl
|
||||
import com.top.player.databinding.ActivityPlayerBinding
|
||||
import com.top.player.databinding.ActivityPusherBinding
|
||||
|
||||
|
||||
class PusherDemoActivity : AppCompatActivity() {
|
||||
|
||||
@ -25,15 +23,25 @@ class PusherDemoActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
private val binding by lazy {
|
||||
ActivityPusherBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_pusher)
|
||||
|
||||
pusher.bind(surface_view_renderer, true)
|
||||
val rtcSurfaceView = findViewById<RTCSurfaceView>(R.id.surface_view_render)
|
||||
|
||||
pusher.bind(rtcSurfaceView, true)
|
||||
setTitle("Pusher Demo")
|
||||
|
||||
|
||||
pusher.setOnErrorListener { code, msg ->
|
||||
Toast.makeText(this, "code:${code},msg:${msg}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,7 +50,7 @@ class PusherDemoActivity : AppCompatActivity() {
|
||||
.permissions(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||||
.request { allGranted, grantedList, deniedList ->
|
||||
if (allGranted) {
|
||||
pusher.push(tv_app.text.toString(), tv_stream_id.text.toString())
|
||||
pusher.push(binding.tvApp.text.toString(), binding.tvStreamId.text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,8 +61,8 @@ class PusherDemoActivity : AppCompatActivity() {
|
||||
.request { allGranted, grantedList, deniedList ->
|
||||
if (allGranted) {
|
||||
pusher.push(
|
||||
tv_app.text.toString(),
|
||||
tv_stream_id.text.toString(),
|
||||
binding.tvApp.text.toString(),
|
||||
binding.tvStreamId.text.toString(),
|
||||
PushMode.SCREEN
|
||||
)
|
||||
}
|
||||
@ -67,8 +75,8 @@ class PusherDemoActivity : AppCompatActivity() {
|
||||
.request { allGranted, grantedList, deniedList ->
|
||||
if (allGranted) {
|
||||
pusher.push(
|
||||
tv_app.text.toString(),
|
||||
tv_stream_id.text.toString(),
|
||||
binding.tvApp.text.toString(),
|
||||
binding.tvStreamId.text.toString(),
|
||||
PushMode.FILE,
|
||||
""
|
||||
)
|
||||
@ -81,5 +89,9 @@ class PusherDemoActivity : AppCompatActivity() {
|
||||
pusher.stop()
|
||||
}
|
||||
|
||||
fun onStopPush(view: View) {
|
||||
pusher.stop()
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package com.zlmediakit.webrtc
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.zlm.rtc.ZLMRTCPlayer
|
||||
import com.zlm.rtc.play.ZLMRTCPlayerImpl
|
||||
import kotlinx.android.synthetic.main.activity_player.surface_view_renderer
|
||||
import kotlinx.android.synthetic.main.activity_player.tv_app
|
||||
import kotlinx.android.synthetic.main.activity_player.tv_stream_id
|
||||
|
||||
class PlayerDemoActivity : AppCompatActivity() {
|
||||
|
||||
|
||||
private val player: ZLMRTCPlayer by lazy {
|
||||
ZLMRTCPlayerImpl(this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_player)
|
||||
|
||||
//ffmpeg -re -stream_loop -1 -i "D:\li\hot\data\data\baseline.mp4" -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp -bf 0 rtsp://zlmediakit.com/live/li
|
||||
//ffmpeg -re -stream_loop -1 -i "D:\li\hot\data\data\test.mp4" -vcodec h264 -acodec aac -f flv -bf 0 rtmp://zlmediakit.com/live/li
|
||||
|
||||
player.bind(surface_view_renderer)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
player.stop()
|
||||
}
|
||||
|
||||
fun onPlayClick(view: View) {
|
||||
|
||||
player.play(tv_app.text.toString(), tv_stream_id.text.toString())
|
||||
}
|
||||
|
||||
fun onPauseClick(view: View) {
|
||||
player.pause()
|
||||
}
|
||||
|
||||
fun onStopClick(view: View) {
|
||||
player.stop()
|
||||
}
|
||||
|
||||
fun onResumeClick(view: View) {
|
||||
player.resume()
|
||||
}
|
||||
|
||||
fun onCapture(view: View) {
|
||||
player.capture {
|
||||
Toast.makeText(this, "capture ok", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onRecord(view: View) {
|
||||
player.record(10 * 1000) {
|
||||
Toast.makeText(this, "" + it, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun onVolume(view: View) {
|
||||
player.setVolume()
|
||||
}
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
package com.zlmediakit.webrtc
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.media.AudioManager
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.gson.Gson
|
||||
import okhttp3.*
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import org.webrtc.*
|
||||
import org.webrtc.RendererCommon.ScalingType
|
||||
import org.webrtc.audio.AudioDeviceModule
|
||||
import org.webrtc.audio.JavaAudioDeviceModule
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
public class WebRTCSurfaceView(context: Context, attrs: AttributeSet?) :
|
||||
RelativeLayout(context, attrs), DefaultLifecycleObserver, RendererCommon.RendererEvents {
|
||||
|
||||
|
||||
private data class sdp(var sdp: String, var username: String, var password: String)
|
||||
|
||||
private data class SdpResponse(var code: Int, var id: String, var sdp: String, var type: String)
|
||||
|
||||
private enum class ErrorCode(val errorCode: Int) {
|
||||
SUCCESS(0x00),
|
||||
GET_REMOTE_SDP_ERROR(0x01);
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val TAG = "WebRTCSurfaceView"
|
||||
|
||||
}
|
||||
|
||||
private var mContext: Context = context
|
||||
|
||||
private val eglBase: EglBase = EglBase.create()
|
||||
private var mEGLBaseContext: EglBase.Context = eglBase.eglBaseContext
|
||||
|
||||
private lateinit var videoUrl: String;
|
||||
|
||||
private var mPeerConnectionFactory: PeerConnectionFactory? = null
|
||||
|
||||
private var mLocalMediaStream: MediaStream? = null
|
||||
private var mLocalAudioTrack: AudioTrack? = null
|
||||
private var mAudioSource: AudioSource? = null
|
||||
|
||||
private var mLocalSessionDescription: SessionDescription? = null
|
||||
private var mRemoteSessionDescription: SessionDescription? = null
|
||||
|
||||
private var mLocalPeer: Peer? = null
|
||||
|
||||
private var mSurfaceViewRenderer: SurfaceViewRenderer
|
||||
|
||||
private lateinit var OnErrorListener: (errorCode: Int, errorMsg: String) -> Unit?
|
||||
|
||||
fun setOnErrorListener(listener: (errorCode: Int, errorMsg: String) -> Unit) {
|
||||
this.OnErrorListener = listener
|
||||
}
|
||||
|
||||
private lateinit var OnPreparedListener: () -> Unit?
|
||||
|
||||
fun setOnPreparedListener(listener: () -> Unit) {
|
||||
this.OnPreparedListener = listener
|
||||
}
|
||||
|
||||
private val audioManager: AudioManager
|
||||
|
||||
|
||||
init {
|
||||
|
||||
val view = LayoutInflater.from(mContext).inflate(R.layout.layout_videoview, this)
|
||||
|
||||
mPeerConnectionFactory = createConnectionFactory()
|
||||
|
||||
mSurfaceViewRenderer = view.findViewById(R.id.surface_view_renderer)
|
||||
|
||||
mSurfaceViewRenderer.init(mEGLBaseContext, this)
|
||||
mSurfaceViewRenderer.setScalingType(ScalingType.SCALE_ASPECT_FILL)
|
||||
mSurfaceViewRenderer.setEnableHardwareScaler(true)
|
||||
|
||||
|
||||
//创建媒体流
|
||||
mLocalMediaStream = mPeerConnectionFactory?.createLocalMediaStream("ARDAMS")
|
||||
//采集音频
|
||||
mAudioSource = mPeerConnectionFactory?.createAudioSource(createAudioConstraints())
|
||||
mLocalAudioTrack = mPeerConnectionFactory?.createAudioTrack("ARDAMSa0", mAudioSource)
|
||||
|
||||
//添加Tracks
|
||||
mLocalMediaStream?.addTrack(mLocalAudioTrack)
|
||||
|
||||
audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
audioManager.isSpeakerphoneOn = false
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun set(width: Int, height: Int) {
|
||||
layoutParams.width = width
|
||||
layoutParams.height = height
|
||||
}
|
||||
|
||||
private fun createConnectionFactory(): PeerConnectionFactory? {
|
||||
|
||||
val options = PeerConnectionFactory.InitializationOptions.builder(mContext)
|
||||
.setEnableInternalTracer(false)
|
||||
.createInitializationOptions()
|
||||
|
||||
PeerConnectionFactory.initialize(options)
|
||||
|
||||
val videoEncoderFactory = DefaultVideoEncoderFactory(
|
||||
mEGLBaseContext,
|
||||
true,
|
||||
true
|
||||
)
|
||||
|
||||
val videoDecoderFactory = DefaultVideoDecoderFactory(mEGLBaseContext)
|
||||
|
||||
|
||||
val audioDevice = createJavaAudioDevice()
|
||||
val peerConnectionFactory = PeerConnectionFactory.builder()
|
||||
.setAudioDeviceModule(audioDevice)
|
||||
.setVideoEncoderFactory(videoEncoderFactory)
|
||||
.setVideoDecoderFactory(videoDecoderFactory)
|
||||
.createPeerConnectionFactory()
|
||||
audioDevice.release()
|
||||
|
||||
return peerConnectionFactory
|
||||
|
||||
}
|
||||
|
||||
private fun createAudioConstraints(): MediaConstraints {
|
||||
val audioConstraints = MediaConstraints()
|
||||
audioConstraints.mandatory.add(
|
||||
MediaConstraints.KeyValuePair(
|
||||
"googEchoCancellation",
|
||||
"true"
|
||||
)
|
||||
)
|
||||
audioConstraints.mandatory.add(
|
||||
MediaConstraints.KeyValuePair(
|
||||
"googAutoGainControl",
|
||||
"false"
|
||||
)
|
||||
)
|
||||
audioConstraints.mandatory.add(
|
||||
MediaConstraints.KeyValuePair(
|
||||
"googHighpassFilter",
|
||||
"true"
|
||||
)
|
||||
)
|
||||
audioConstraints.mandatory.add(
|
||||
MediaConstraints.KeyValuePair(
|
||||
"googNoiseSuppression",
|
||||
"true"
|
||||
)
|
||||
)
|
||||
return audioConstraints
|
||||
}
|
||||
|
||||
private fun offerOrAnswerConstraint(): MediaConstraints {
|
||||
val mediaConstraints = MediaConstraints()
|
||||
val keyValuePairs = java.util.ArrayList<MediaConstraints.KeyValuePair>()
|
||||
keyValuePairs.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
||||
keyValuePairs.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
|
||||
mediaConstraints.mandatory.addAll(keyValuePairs)
|
||||
return mediaConstraints
|
||||
}
|
||||
|
||||
private fun createJavaAudioDevice(): AudioDeviceModule {
|
||||
val audioTrackErrorCallback: JavaAudioDeviceModule.AudioTrackErrorCallback = object :
|
||||
JavaAudioDeviceModule.AudioTrackErrorCallback {
|
||||
override fun onWebRtcAudioTrackInitError(errorMessage: String) {
|
||||
Log.i(TAG, "onWebRtcAudioTrackInitError ============> $errorMessage")
|
||||
|
||||
}
|
||||
|
||||
override fun onWebRtcAudioTrackStartError(
|
||||
errorCode: JavaAudioDeviceModule.AudioTrackStartErrorCode, errorMessage: String
|
||||
) {
|
||||
Log.i(TAG, "onWebRtcAudioTrackStartError ============> $errorCode:$errorMessage")
|
||||
|
||||
}
|
||||
|
||||
override fun onWebRtcAudioTrackError(errorMessage: String) {
|
||||
Log.i(TAG, "onWebRtcAudioTrackError ============> $errorMessage")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set audio track state callbacks.
|
||||
val audioTrackStateCallback: JavaAudioDeviceModule.AudioTrackStateCallback = object :
|
||||
JavaAudioDeviceModule.AudioTrackStateCallback {
|
||||
override fun onWebRtcAudioTrackStart() {
|
||||
Log.i(TAG, "onWebRtcAudioTrackStart ============>")
|
||||
|
||||
}
|
||||
|
||||
override fun onWebRtcAudioTrackStop() {
|
||||
Log.i(TAG, "onWebRtcAudioTrackStop ============>")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return JavaAudioDeviceModule.builder(mContext)
|
||||
.setUseHardwareAcousticEchoCanceler(true)
|
||||
.setUseHardwareNoiseSuppressor(true)
|
||||
.setAudioTrackErrorCallback(audioTrackErrorCallback)
|
||||
.setAudioTrackStateCallback(audioTrackStateCallback)
|
||||
.setUseStereoOutput(true) //立体声
|
||||
.createAudioDeviceModule()
|
||||
}
|
||||
|
||||
fun setVideoPath(url: String) {
|
||||
videoUrl = url
|
||||
}
|
||||
|
||||
fun start() {
|
||||
|
||||
mLocalPeer = Peer {
|
||||
val okHttpClient = OkHttpClient.Builder().build()
|
||||
|
||||
|
||||
val body = RequestBody.create("text/plain; charset=utf-8".toMediaType(), it!!)
|
||||
|
||||
|
||||
val request: Request = Request.Builder()
|
||||
.url(videoUrl)
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
val call: Call = okHttpClient.newCall(request)
|
||||
|
||||
call.enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
Log.i(TAG, "onFailure")
|
||||
OnErrorListener?.invoke(
|
||||
ErrorCode.GET_REMOTE_SDP_ERROR.errorCode,
|
||||
e.message.toString()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
val body = response.body?.string()
|
||||
val sdpResponse = Gson().fromJson(body, SdpResponse::class.java)
|
||||
|
||||
try {
|
||||
mRemoteSessionDescription = SessionDescription(
|
||||
SessionDescription.Type.fromCanonicalForm("answer"),
|
||||
sdpResponse.sdp
|
||||
)
|
||||
Log.i(
|
||||
TAG,
|
||||
"RemoteSdpObserver onCreateSuccess:[SessionDescription[type=${mRemoteSessionDescription?.type?.name},description=${mRemoteSessionDescription?.description}]]"
|
||||
)
|
||||
mLocalPeer?.setRemoteDescription(mRemoteSessionDescription!!)
|
||||
} catch (e: Exception) {
|
||||
Log.i(TAG, e.toString())
|
||||
OnErrorListener.invoke(
|
||||
ErrorCode.GET_REMOTE_SDP_ERROR.errorCode,
|
||||
e.localizedMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
mSurfaceViewRenderer.pauseVideo()
|
||||
//mSurfaceViewRenderer.disableFpsReduction()
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
mSurfaceViewRenderer.setFpsReduction(15f)
|
||||
}
|
||||
|
||||
fun screenshot(listener: (bitmap: Bitmap) -> Unit) {
|
||||
mSurfaceViewRenderer.addFrameListener({
|
||||
listener.invoke(it)
|
||||
}, 1f)
|
||||
}
|
||||
|
||||
fun setSpeakerphoneOn(on: Boolean) {
|
||||
audioManager.isSpeakerphoneOn = on
|
||||
}
|
||||
|
||||
fun mute(on:Boolean) {
|
||||
audioManager.isMicrophoneMute=on
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
mSurfaceViewRenderer.release()
|
||||
mLocalPeer?.mPeerConnection?.dispose()
|
||||
mAudioSource?.dispose()
|
||||
mPeerConnectionFactory?.dispose()
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
}
|
||||
|
||||
inner class Peer(var sdp: (String?) -> Unit = {}) : PeerConnection.Observer, SdpObserver {
|
||||
|
||||
var mPeerConnection: PeerConnection? = null
|
||||
|
||||
init {
|
||||
mPeerConnection = createPeerConnection()
|
||||
mPeerConnection?.createOffer(this, offerOrAnswerConstraint())
|
||||
}
|
||||
|
||||
//初始化 RTCPeerConnection 连接管道
|
||||
private fun createPeerConnection(): PeerConnection? {
|
||||
if (mPeerConnectionFactory == null) {
|
||||
mPeerConnectionFactory = createConnectionFactory()
|
||||
}
|
||||
// 管道连接抽象类实现方法
|
||||
val ICEServers = LinkedList<PeerConnection.IceServer>()
|
||||
val rtcConfig = PeerConnection.RTCConfiguration(ICEServers)
|
||||
//修改模式 PlanB无法使用仅接收音视频的配置
|
||||
//rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.PLAN_B
|
||||
return mPeerConnectionFactory?.createPeerConnection(rtcConfig, this)
|
||||
}
|
||||
|
||||
fun setRemoteDescription(sdp: SessionDescription) {
|
||||
mPeerConnection?.setRemoteDescription(this, sdp)
|
||||
}
|
||||
|
||||
override fun onCreateSuccess(sessionDescription: SessionDescription?) {
|
||||
mPeerConnection?.setLocalDescription(this, sessionDescription)
|
||||
mPeerConnection?.addStream(mLocalMediaStream)
|
||||
sdp.invoke(sessionDescription?.description)
|
||||
}
|
||||
|
||||
override fun onSetSuccess() {
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateFailure(p0: String?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSetFailure(p0: String?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSignalingChange(signalingState: PeerConnection.SignalingState?) {
|
||||
Log.i(TAG, "onSignalingChange ============> " + signalingState.toString())
|
||||
}
|
||||
|
||||
override fun onIceConnectionChange(iceConnectionState: PeerConnection.IceConnectionState?) {
|
||||
Log.i(TAG, "onIceConnectionChange ============> " + iceConnectionState.toString())
|
||||
|
||||
}
|
||||
|
||||
override fun onIceConnectionReceivingChange(p0: Boolean) {
|
||||
Log.i(TAG, "onIceConnectionReceivingChange ============> $p0")
|
||||
|
||||
}
|
||||
|
||||
override fun onIceGatheringChange(iceGatheringState: PeerConnection.IceGatheringState?) {
|
||||
Log.i(TAG, "onIceGatheringChange ============> ${iceGatheringState.toString()}")
|
||||
}
|
||||
|
||||
override fun onIceCandidate(iceCandidate: IceCandidate?) {
|
||||
Log.i(TAG, "onIceCandidate ============> ${iceCandidate.toString()}")
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
|
||||
Log.i(TAG, "onIceCandidatesRemoved ============> ${p0.toString()}")
|
||||
}
|
||||
|
||||
override fun onAddStream(mediaStream: MediaStream?) {
|
||||
Log.i(TAG, "onAddStream ============> ${mediaStream?.toString()}")
|
||||
|
||||
if (mediaStream?.videoTracks?.isEmpty() != true) {
|
||||
val remoteVideoTrack = mediaStream?.videoTracks?.get(0)
|
||||
remoteVideoTrack?.setEnabled(true)
|
||||
remoteVideoTrack?.addSink(mSurfaceViewRenderer)
|
||||
}
|
||||
|
||||
if (mediaStream?.audioTracks?.isEmpty() != true) {
|
||||
val remoteAudioTrack = mediaStream?.audioTracks?.get(0)
|
||||
remoteAudioTrack?.setEnabled(true)
|
||||
remoteAudioTrack?.setVolume(1.0)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onRemoveStream(mediaStream: MediaStream?) {
|
||||
Log.i(TAG, "onRemoveStream ============> ${mediaStream.toString()}")
|
||||
|
||||
}
|
||||
|
||||
override fun onDataChannel(dataChannel: DataChannel?) {
|
||||
Log.i(TAG, "onDataChannel ============> ${dataChannel.toString()}")
|
||||
|
||||
}
|
||||
|
||||
override fun onRenegotiationNeeded() {
|
||||
Log.i(TAG, "onRenegotiationNeeded ============>")
|
||||
|
||||
}
|
||||
|
||||
override fun onAddTrack(rtpReceiver: RtpReceiver?, p1: Array<out MediaStream>?) {
|
||||
Log.i(TAG, "onAddTrack ============>" + rtpReceiver?.track())
|
||||
Log.i(TAG, "onAddTrack ============>" + p1?.size)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFirstFrameRendered() {
|
||||
Log.i(TAG, "onFirstFrameRendered ============>")
|
||||
|
||||
}
|
||||
|
||||
override fun onFrameResolutionChanged(frameWidth: Int, frameHeight: Int, rotation: Int) {
|
||||
Log.i(TAG, "onFrameResolutionChanged ============> $frameWidth:$frameHeight:$rotation")
|
||||
//set(frameWidth,frameHeight)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="toPlayActivity"
|
||||
|
||||
android:text="播放" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
@ -16,12 +17,6 @@
|
||||
android:onClick="toPushActivity"
|
||||
android:text="推流" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="toDataChannelActivity"
|
||||
android:text="DataChannel"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -1,93 +0,0 @@
|
||||
<?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>
|
@ -5,16 +5,28 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.webrtc.SurfaceViewRenderer
|
||||
android:id="@+id/surface_view_renderer"
|
||||
android:layout_width="wrap_content"
|
||||
<com.rtc.core.RTCSurfaceView
|
||||
android:id="@+id/surface_view_render"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="240dp" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_margin="6dp"
|
||||
android:background="@color/black"
|
||||
/>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/surface_view_renderer"
|
||||
android:layout_below="@id/surface_view_render"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
@ -41,7 +53,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/surface_view_renderer">
|
||||
android:layout_below="@id/surface_view_render">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="0dp"
|
||||
@ -56,7 +68,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:text="li"
|
||||
android:text="test"
|
||||
android:gravity="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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"
|
||||
<com.rtc.core.RTCSurfaceView
|
||||
android:id="@+id/surface_view_render"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="240dp" />
|
||||
|
||||
@ -13,8 +13,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/surface_view_renderer"
|
||||
>
|
||||
android:layout_below="@id/surface_view_render"
|
||||
app:layout_constraintTop_toBottomOf="@id/surface_view_render">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -40,7 +40,7 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/surface_view_renderer">
|
||||
android:layout_below="@id/surface_view_render">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="0dp"
|
||||
@ -55,7 +55,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:text="li"
|
||||
android:text="test"
|
||||
android:gravity="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
@ -82,5 +82,14 @@
|
||||
android:textAllCaps="false"
|
||||
android:onClick="onPushFile"/>
|
||||
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Stop Push"
|
||||
android:textAllCaps="false"
|
||||
android:onClick="onStopPush"/>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</RelativeLayout>
|
@ -1,13 +0,0 @@
|
||||
<?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="wrap_content"/>
|
||||
|
||||
</RelativeLayout>
|
@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@ -1,16 +1,7 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.AndroidWebRTC" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<style name="Base.Theme.RTCPlayer" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Customize your dark theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||
</style>
|
||||
</resources>
|
@ -1,10 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
@ -1,3 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">ZLMediakit WebRTC</string>
|
||||
<string name="app_name">RTCPlayer</string>
|
||||
</resources>
|
@ -1,16 +1,9 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.AndroidWebRTC" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<style name="Base.Theme.RTCPlayer" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Customize your light theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.RTCPlayer" parent="Base.Theme.RTCPlayer" />
|
||||
</resources>
|
@ -0,0 +1,17 @@
|
||||
package com.top.player;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package com.zlmediakit.webrtc
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id 'com.android.application' version '7.2.1' apply false
|
||||
id 'com.android.library' version '7.2.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
6
webrtc_player/android/build.gradle.kts
Normal file
@ -0,0 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("com.android.library") version "8.2.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
v=0
|
||||
o=- 6282534540641525368 2 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1 2
|
||||
a=extmap-allow-mixed
|
||||
a=msid-semantic: WMS
|
||||
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:H2Ms
|
||||
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
|
||||
a=setup:actpass
|
||||
a=mid:0
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendrecv
|
||||
a=msid:- ef1bcbd2-7e69-40d3-9532-0f4a698022a7
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:102 ILBC/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:2860285610 cname:2EW1hNfQMKxwbRrt
|
||||
a=ssrc:2860285610 msid:- ef1bcbd2-7e69-40d3-9532-0f4a698022a7
|
||||
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:H2Ms
|
||||
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
|
||||
a=setup:actpass
|
||||
a=mid:1
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:13 urn:3gpp:video-orientation
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=sendonly
|
||||
a=msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
|
||||
a=rtcp-mux
|
||||
a=rtcp-rsize
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtcp-fb:96 goog-remb
|
||||
a=rtcp-fb:96 transport-cc
|
||||
a=rtcp-fb:96 ccm fir
|
||||
a=rtcp-fb:96 nack
|
||||
a=rtcp-fb:96 nack pli
|
||||
a=rtpmap:97 rtx/90000
|
||||
a=fmtp:97 apt=96
|
||||
a=rtpmap:39 AV1/90000
|
||||
a=rtcp-fb:39 goog-remb
|
||||
a=rtcp-fb:39 transport-cc
|
||||
a=rtcp-fb:39 ccm fir
|
||||
a=rtcp-fb:39 nack
|
||||
a=rtcp-fb:39 nack pli
|
||||
a=rtpmap:40 rtx/90000
|
||||
a=fmtp:40 apt=39
|
||||
a=rtpmap:98 VP9/90000
|
||||
a=rtcp-fb:98 goog-remb
|
||||
a=rtcp-fb:98 transport-cc
|
||||
a=rtcp-fb:98 ccm fir
|
||||
a=rtcp-fb:98 nack
|
||||
a=rtcp-fb:98 nack pli
|
||||
a=fmtp:98 profile-id=0
|
||||
a=rtpmap:99 rtx/90000
|
||||
a=fmtp:99 apt=98
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=rtpmap:103 rtx/90000
|
||||
a=fmtp:103 apt=127
|
||||
a=rtpmap:104 H265/90000
|
||||
a=rtcp-fb:104 goog-remb
|
||||
a=rtcp-fb:104 transport-cc
|
||||
a=rtcp-fb:104 ccm fir
|
||||
a=rtcp-fb:104 nack
|
||||
a=rtcp-fb:104 nack pli
|
||||
a=rtpmap:105 rtx/90000
|
||||
a=fmtp:105 apt=104
|
||||
a=rtpmap:106 red/90000
|
||||
a=rtpmap:107 rtx/90000
|
||||
a=fmtp:107 apt=106
|
||||
a=rtpmap:108 ulpfec/90000
|
||||
a=ssrc-group:FID 100166069 2038394461
|
||||
a=ssrc:100166069 cname:2EW1hNfQMKxwbRrt
|
||||
a=ssrc:100166069 msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
|
||||
a=ssrc:2038394461 cname:2EW1hNfQMKxwbRrt
|
||||
a=ssrc:2038394461 msid:- 8b57ef63-835d-4acf-bce9-18e26cebf9b1
|
||||
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:H2Ms
|
||||
a=ice-pwd:14ODtTVh5+lT7W0+g9EAlE4j
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 47:81:90:07:43:95:D7:F2:DA:60:DA:79:E5:88:26:65:29:4A:26:28:7A:B7:AB:D3:DB:CE:C9:09:39:EB:31:91
|
||||
a=setup:actpass
|
||||
a=mid:2
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendonly
|
||||
a=msid:- 49d08cb1-a325-4a5f-805a-72a215eb67af
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:102 ILBC/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:2666757999 cname:2EW1hNfQMKxwbRrt
|
||||
a=ssrc:2666757999 msid:- 49d08cb1-a325-4a5f-805a-72a215eb67af
|
BIN
webrtc_player/android/doc/delay_play1.jpg
Normal file
After Width: | Height: | Size: 2.8 MiB |
BIN
webrtc_player/android/doc/delay_play2.jpg
Normal file
After Width: | Height: | Size: 2.5 MiB |
BIN
webrtc_player/android/doc/delay_play3.jpg
Normal file
After Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 9.8 MiB After Width: | Height: | Size: 9.3 MiB |
Before Width: | Height: | Size: 9.6 MiB After Width: | Height: | Size: 9.6 MiB |
BIN
webrtc_player/android/doc/delay_push3.jpg
Normal file
After Width: | Height: | Size: 8.7 MiB |
BIN
webrtc_player/android/doc/home.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
webrtc_player/android/doc/player.jpg
Normal file
After Width: | Height: | Size: 140 KiB |
BIN
webrtc_player/android/doc/pusher.jpg
Normal file
After Width: | Height: | Size: 154 KiB |
@ -12,11 +12,9 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app"s APK
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
|
@ -1,6 +1,6 @@
|
||||
#Mon Sep 19 22:08:36 CST 2022
|
||||
#Fri May 31 15:05:44 CST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
Before Width: | Height: | Size: 635 KiB |
@ -1,76 +0,0 @@
|
||||
v=0
|
||||
o=- 5986549880880292178 2 IN IP4 108.181.24.73
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1
|
||||
a=msid-semantic: WMS ARDAMS
|
||||
a=ice-lite
|
||||
m=audio 8000 UDP/TLS/RTP/SAVPF 111
|
||||
c=IN IP4 108.181.24.73
|
||||
a=rtcp:8000 IN IP4 108.181.24.73
|
||||
a=ice-ufrag:bLUYSR9AH0A=_2105
|
||||
a=ice-pwd:wstnRIopRDwBZyv56AZlGuOm
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 F4:C9:0C:ED:05:93:C0:F9:AC:34:71:60:2D:3E:D0:47:91:CC:FD:38:C7:63:4A:93:C3:48:0C:B4:6F:32:76:FE
|
||||
a=setup:passive
|
||||
a=mid:0
|
||||
a=ice-lite
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=inactive
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=msid:zlmediakit-mslabel zlmediakit-label-0
|
||||
a=ssrc:2 cname:zlmediakit-rtp
|
||||
a=ssrc:2 msid:zlmediakit-mslabel zlmediakit-label-0
|
||||
a=ssrc:2 mslabel:zlmediakit-mslabel
|
||||
a=ssrc:2 label:zlmediakit-label-0
|
||||
a=candidate:udpcandidate 1 udp 110 108.181.24.73 8000 typ host
|
||||
a=candidate:tcpcandidate 1 tcp 105 108.181.24.73 8000 typ host tcptype passive
|
||||
|
||||
|
||||
m=video 8000 UDP/TLS/RTP/SAVPF 127 103
|
||||
c=IN IP4 108.181.24.73
|
||||
a=rtcp:8000 IN IP4 108.181.24.73
|
||||
a=ice-ufrag:bLUYSR9AH0A=_2105
|
||||
a=ice-pwd:wstnRIopRDwBZyv56AZlGuOm
|
||||
a=ice-options:trickle
|
||||
a=fingerprint:sha-256 F4:C9:0C:ED:05:93:C0:F9:AC:34:71:60:2D:3E:D0:47:91:CC:FD:38:C7:63:4A:93:C3:48:0C:B4:6F:32:76:FE
|
||||
a=setup:passive
|
||||
a=mid:1
|
||||
a=ice-lite
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=sendonly
|
||||
a=rtcp-mux
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=rtpmap:103 rtx/90000
|
||||
a=fmtp:103 apt=127
|
||||
a=msid:zlmediakit-mslabel zlmediakit-label-1
|
||||
a=ssrc:1 cname:zlmediakit-rtp
|
||||
a=ssrc:1 msid:zlmediakit-mslabel zlmediakit-label-1
|
||||
a=ssrc:1 mslabel:zlmediakit-mslabel
|
||||
a=ssrc:1 label:zlmediakit-label-1
|
||||
a=ssrc:3 cname:zlmediakit-rtp
|
||||
a=ssrc:3 msid:zlmediakit-mslabel zlmediakit-label-1
|
||||
a=ssrc:3 mslabel:zlmediakit-mslabel
|
||||
a=ssrc:3 label:zlmediakit-label-1
|
||||
a=ssrc-group:FID 1 3
|
||||
a=candidate:udpcandidate 1 udp 110 108.181.24.73 8000 typ host
|
||||
a=candidate:tcpcandidate 1 tcp 105 108.181.24.73 8000 typ host tcptype passive
|
@ -1,25 +0,0 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter() // Warning: this repository is going to shut down soon
|
||||
|
||||
maven { url 'https://jitpack.io' }
|
||||
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter() // Warning: this repository is going to shut down soon
|
||||
|
||||
maven { url 'https://jitpack.io' }
|
||||
|
||||
}
|
||||
}
|
||||
rootProject.name = "android"
|
||||
include ':app'
|
||||
include ':zlm'
|
25
webrtc_player/android/settings.gradle.kts
Normal file
@ -0,0 +1,25 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
maven {
|
||||
credentials {
|
||||
username = ("6256cd6c7e8dbc28d896a661")
|
||||
password = ("KRuEgA3WYUVy")
|
||||
}
|
||||
url = uri("https://packages.aliyun.com/maven/repository/2302596-release-mpvXBR/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "RTCPlayer"
|
||||
include(":app")
|
||||
//include(":RTCPlayer")
|
@ -1,112 +0,0 @@
|
||||
v=0
|
||||
o=- 6005790964057401669 2 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1
|
||||
a=extmap-allow-mixed
|
||||
a=msid-semantic: WMS ARDAMS
|
||||
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:UITA
|
||||
a=ice-pwd:obg6A6ZHKhr5CGzmmgRfTk/y
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 5E:23:E2:B2:0F:ED:FC:A2:20:06:24:0F:6A:4B:99:33:91:39:A7:40:A5:20:88:77:D6:99:04:FB:C7:CD:7F:33
|
||||
a=setup:actpass
|
||||
a=mid:0
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 urn:3gpp:video-orientation
|
||||
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=sendrecv
|
||||
a=msid:ARDAMS ARDAMSv0
|
||||
a=rtcp-mux
|
||||
a=rtcp-rsize
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtcp-fb:96 goog-remb
|
||||
a=rtcp-fb:96 transport-cc
|
||||
a=rtcp-fb:96 ccm fir
|
||||
a=rtcp-fb:96 nack
|
||||
a=rtcp-fb:96 nack pli
|
||||
a=rtpmap:97 rtx/90000
|
||||
a=fmtp:97 apt=96
|
||||
a=rtpmap:39 AV1/90000
|
||||
a=rtcp-fb:39 goog-remb
|
||||
a=rtcp-fb:39 transport-cc
|
||||
a=rtcp-fb:39 ccm fir
|
||||
a=rtcp-fb:39 nack
|
||||
a=rtcp-fb:39 nack pli
|
||||
a=rtpmap:40 rtx/90000
|
||||
a=fmtp:40 apt=39
|
||||
a=rtpmap:98 VP9/90000
|
||||
a=rtcp-fb:98 goog-remb
|
||||
a=rtcp-fb:98 transport-cc
|
||||
a=rtcp-fb:98 ccm fir
|
||||
a=rtcp-fb:98 nack
|
||||
a=rtcp-fb:98 nack pli
|
||||
a=fmtp:98 profile-id=0
|
||||
a=rtpmap:99 rtx/90000
|
||||
a=fmtp:99 apt=98
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=rtpmap:103 rtx/90000
|
||||
a=fmtp:103 apt=127
|
||||
a=rtpmap:104 H265/90000
|
||||
a=rtcp-fb:104 goog-remb
|
||||
a=rtcp-fb:104 transport-cc
|
||||
a=rtcp-fb:104 ccm fir
|
||||
a=rtcp-fb:104 nack
|
||||
a=rtcp-fb:104 nack pli
|
||||
a=rtpmap:105 rtx/90000
|
||||
a=fmtp:105 apt=104
|
||||
a=rtpmap:106 red/90000
|
||||
a=rtpmap:107 rtx/90000
|
||||
a=fmtp:107 apt=106
|
||||
a=rtpmap:108 ulpfec/90000
|
||||
a=ssrc-group:FID 1882091013 3163545249
|
||||
a=ssrc:1882091013 cname:L6diPFsW7sR8uU/a
|
||||
a=ssrc:1882091013 msid:ARDAMS ARDAMSv0
|
||||
a=ssrc:3163545249 cname:L6diPFsW7sR8uU/a
|
||||
a=ssrc:3163545249 msid:ARDAMS ARDAMSv0
|
||||
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:UITA
|
||||
a=ice-pwd:obg6A6ZHKhr5CGzmmgRfTk/y
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 5E:23:E2:B2:0F:ED:FC:A2:20:06:24:0F:6A:4B:99:33:91:39:A7:40:A5:20:88:77:D6:99:04:FB:C7:CD:7F:33
|
||||
a=setup:actpass
|
||||
a=mid:1
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendrecv
|
||||
a=msid:ARDAMS ARDAMSa0
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:102 ILBC/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:1776947399 cname:L6diPFsW7sR8uU/a
|
||||
a=ssrc:1776947399 msid:ARDAMS ARDAMSa0
|
@ -1,119 +0,0 @@
|
||||
v=0
|
||||
o=- 2629706501878304631 2 IN IP4 127.0.0.1
|
||||
s=-
|
||||
t=0 0
|
||||
a=group:BUNDLE 0 1
|
||||
a=extmap-allow-mixed
|
||||
a=msid-semantic: WMS ARDAMS
|
||||
m=video 9 UDP/TLS/RTP/SAVPF 96 97 39 40 98 99 127 103 104 105 106 107 108 43
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:+zOA
|
||||
a=ice-pwd:q88Il7adjqdu6SeSqAdsv8lv
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 8D:0B:86:0B:8C:DF:22:99:85:DD:60:41:AA:FE:F1:98:FE:FB:29:32:5C:11:6E:47:54:82:21:6C:A0:4F:60:B2
|
||||
a=setup:actpass
|
||||
a=mid:0
|
||||
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:3 urn:3gpp:video-orientation
|
||||
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
|
||||
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
|
||||
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
||||
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
|
||||
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
|
||||
a=sendrecv
|
||||
a=msid:ARDAMS ARDAMSv0
|
||||
a=rtcp-mux
|
||||
a=rtcp-rsize
|
||||
a=rtpmap:96 VP8/90000
|
||||
a=rtcp-fb:96 goog-remb
|
||||
a=rtcp-fb:96 transport-cc
|
||||
a=rtcp-fb:96 ccm fir
|
||||
a=rtcp-fb:96 nack
|
||||
a=rtcp-fb:96 nack pli
|
||||
a=rtpmap:97 rtx/90000
|
||||
a=fmtp:97 apt=96
|
||||
a=rtpmap:39 AV1/90000
|
||||
a=rtcp-fb:39 goog-remb
|
||||
a=rtcp-fb:39 transport-cc
|
||||
a=rtcp-fb:39 ccm fir
|
||||
a=rtcp-fb:39 nack
|
||||
a=rtcp-fb:39 nack pli
|
||||
a=rtpmap:40 rtx/90000
|
||||
a=fmtp:40 apt=39
|
||||
a=rtpmap:98 VP9/90000
|
||||
a=rtcp-fb:98 goog-remb
|
||||
a=rtcp-fb:98 transport-cc
|
||||
a=rtcp-fb:98 ccm fir
|
||||
a=rtcp-fb:98 nack
|
||||
a=rtcp-fb:98 nack pli
|
||||
a=fmtp:98 profile-id=0
|
||||
a=rtpmap:99 rtx/90000
|
||||
a=fmtp:99 apt=98
|
||||
a=rtpmap:127 H264/90000
|
||||
a=rtcp-fb:127 goog-remb
|
||||
a=rtcp-fb:127 transport-cc
|
||||
a=rtcp-fb:127 ccm fir
|
||||
a=rtcp-fb:127 nack
|
||||
a=rtcp-fb:127 nack pli
|
||||
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
|
||||
a=rtpmap:103 rtx/90000
|
||||
a=fmtp:103 apt=127
|
||||
a=rtpmap:104 H265/90000
|
||||
a=rtcp-fb:104 goog-remb
|
||||
a=rtcp-fb:104 transport-cc
|
||||
a=rtcp-fb:104 ccm fir
|
||||
a=rtcp-fb:104 nack
|
||||
a=rtcp-fb:104 nack pli
|
||||
a=rtpmap:105 rtx/90000
|
||||
a=fmtp:105 apt=104
|
||||
a=rtpmap:106 red/90000
|
||||
a=rtpmap:107 rtx/90000
|
||||
a=fmtp:107 apt=106
|
||||
a=rtpmap:108 ulpfec/90000
|
||||
a=rtpmap:43 flexfec-03/90000
|
||||
a=rtcp-fb:43 goog-remb
|
||||
a=rtcp-fb:43 transport-cc
|
||||
a=fmtp:43 repair-window=10000000
|
||||
a=ssrc-group:FID 129065984 1082217256
|
||||
a=ssrc-group:FEC-FR 129065984 4071693473
|
||||
a=ssrc:129065984 cname:T1fpJkmBNuVUtXM8
|
||||
a=ssrc:129065984 msid:ARDAMS ARDAMSv0
|
||||
a=ssrc:1082217256 cname:T1fpJkmBNuVUtXM8
|
||||
a=ssrc:1082217256 msid:ARDAMS ARDAMSv0
|
||||
a=ssrc:4071693473 cname:T1fpJkmBNuVUtXM8
|
||||
a=ssrc:4071693473 msid:ARDAMS ARDAMSv0
|
||||
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 102 0 8 13 110 126
|
||||
c=IN IP4 0.0.0.0
|
||||
a=rtcp:9 IN IP4 0.0.0.0
|
||||
a=ice-ufrag:+zOA
|
||||
a=ice-pwd:q88Il7adjqdu6SeSqAdsv8lv
|
||||
a=ice-options:trickle renomination
|
||||
a=fingerprint:sha-256 8D:0B:86:0B:8C:DF:22:99:85:DD:60:41:AA:FE:F1:98:FE:FB:29:32:5C:11:6E:47:54:82:21:6C:A0:4F:60:B2
|
||||
a=setup:actpass
|
||||
a=mid:1
|
||||
a=extmap:14 urn:ietf:params:rtp-hdrext:ssrc-audio-level
|
||||
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
|
||||
a=sendrecv
|
||||
a=msid:ARDAMS ARDAMSa0
|
||||
a=rtcp-mux
|
||||
a=rtpmap:111 opus/48000/2
|
||||
a=rtcp-fb:111 transport-cc
|
||||
a=fmtp:111 minptime=10;useinbandfec=1
|
||||
a=rtpmap:63 red/48000/2
|
||||
a=fmtp:63 111/111
|
||||
a=rtpmap:9 G722/8000
|
||||
a=rtpmap:102 ILBC/8000
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:13 CN/8000
|
||||
a=rtpmap:110 telephone-event/48000
|
||||
a=rtpmap:126 telephone-event/8000
|
||||
a=ssrc:2019904101 cname:T1fpJkmBNuVUtXM8
|
||||
a=ssrc:2019904101 msid:ARDAMS ARDAMSa0
|
1
webrtc_player/android/zlm/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/build
|
@ -1,61 +0,0 @@
|
||||
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'
|
||||
}
|
||||
sourceSets {
|
||||
main{
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bc218b4b68589b49cb72278af6759deea0146c47079eea708bba5669545e9005
|
||||
size 183748898
|
21
webrtc_player/android/zlm/proguard-rules.pro
vendored
@ -1,21 +0,0 @@
|
||||
# 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
|
@ -1,24 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
|
||||
<application>
|
||||
<service android:name="com.zlm.rtc.push.ScreenShareService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaProjection"/>
|
||||
</application>
|
||||
</manifest>
|
@ -1,46 +0,0 @@
|
||||
# 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(ZLToolKit 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)
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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_ */
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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_ */
|
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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_ */
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,218 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,443 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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_
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,444 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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_ */
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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
|