android rtc播放、推流基本ok

This commit is contained in:
li 2024-06-04 14:22:35 +08:00
parent 31264e4cf4
commit 67341dd29b
585 changed files with 532 additions and 102287 deletions

View File

@ -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

View File

@ -13,4 +13,3 @@
.externalNativeBuild
.cxx
local.properties
/.idea/

View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1 @@
RTCPlayer

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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"/>&nbsp;&nbsp;&nbsp;
<img src="doc/player.jpg" width="240"/>&nbsp;&nbsp;&nbsp;
<img src="doc/pusher.jpg" width="240"/>
</center>
## 延迟情况
- **网页端推流Android端播放**
<center class="half">
<img src="doc/delay_play1.jpg" width="240"/>&nbsp;&nbsp;&nbsp;
<img src="doc/delay_play2.jpg" width="240"/>&nbsp;&nbsp;&nbsp;
<img src="doc/delay_play3.jpg" width="240"/>
</center>
三次延迟分别为490ms、526ms、560ms
- **Android端推屏幕流网页端播放**
<center class="half">
<img src="doc/delay_push1.jpg" width="240"/>&nbsp;&nbsp;&nbsp;
<img src="doc/delay_push2.jpg" width="240"/>&nbsp;&nbsp;&nbsp;
<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)

Binary file not shown.

View File

@ -1,2 +1 @@
/build
.cxx
/build

View File

@ -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'
}

View 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")
}

View File

@ -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());
}
}

View File

@ -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)
}
}

View File

@ -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>

View File

@ -1,4 +1,4 @@
package com.zlmediakit.webrtc
package com.top.player
import android.app.Application

View File

@ -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) {
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,3 +1,3 @@
<resources>
<string name="app_name">ZLMediakit WebRTC</string>
<string name="app_name">RTCPlayer</string>
</resources>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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
}

View 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
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 MiB

After

Width:  |  Height:  |  Size: 9.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 MiB

After

Width:  |  Height:  |  Size: 9.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

View File

@ -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

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 KiB

View File

@ -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

View File

@ -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'

View 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")

View File

@ -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

View File

@ -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

View File

@ -1 +0,0 @@
/build

View File

@ -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"
}

View File

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bc218b4b68589b49cb72278af6759deea0146c47079eea708bba5669545e9005
size 183748898

View File

@ -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

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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)

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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

View File

@ -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 0dts
*/
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 0dts
*/
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_ */

View File

@ -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

View File

@ -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;
/**
* trackTrack的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;
/**
* trackTrack的clone方法
* sps pps这些信息 Delegate相关关系
* @param track
*/
bool addTrack(const Track::Ptr & track) override;
/**
* Track完毕Track3onAllTrackReady
* Track
*
*/
void addTrackCompleted() override;
/**
* track数>=1addTrackCompleted类型
* 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输出frameonAllTrackReady触发后才会调用此方法
* @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

View File

@ -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

View File

@ -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输出frameonAllTrackReady触发后才会调用此方法
* @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

View File

@ -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_

View File

@ -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 &params() 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

View File

@ -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 dts0
* @param pts pts0dts
* @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

View File

@ -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 {
// 每个流媒体服务器的IDGUID
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 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏
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-2016c.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 */

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More