This commit is contained in:
li 2024-05-09 16:58:15 +08:00
parent 58d273e04e
commit 0dcf083b4f
351 changed files with 72908 additions and 143 deletions

View File

@ -41,6 +41,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation project(':zlm')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View File

@ -41,6 +41,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PlayDemoActivity"
android:screenOrientation="portrait"/>
<activity android:name=".PlayerDemoActivity" />
</application>
</manifest>

View File

@ -1,79 +1,26 @@
package com.zlmediakit.webrtc
import android.annotation.SuppressLint
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*
class MainActivity : AppCompatActivity() {
private var isSpeaker = true
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
lifecycle.addObserver(web_rtc_sv)
fun toPlayActivity(view: View) {
startActivity(Intent(this, PlayDemoActivity::class.java))
}
//http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play
url.setText("http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play")
//http://192.168.1.17/index/api/webrtc?app=live&stream=test&type=play
btn_play.setOnClickListener {
web_rtc_sv?.setVideoPath(url.text.toString())
web_rtc_sv.start()
}
web_rtc_sv.setOnErrorListener { errorCode, errorMsg ->
runOnUiThread {
Toast.makeText(this, "errorCode:$errorCode,errorMsg:$errorMsg", Toast.LENGTH_SHORT)
.show()
}
}
btn_pause.setOnClickListener {
web_rtc_sv?.pause()
}
btn_resume.setOnClickListener {
web_rtc_sv?.resume()
}
btn_screenshot.setOnClickListener {
web_rtc_sv?.screenshot {
runOnUiThread {
iv_screen.setImageDrawable(BitmapDrawable(it))
}
}
}
btn_mute.setOnClickListener {
web_rtc_sv.mute(true)
}
selectAudio()
btn_speaker.setOnClickListener {
selectAudio()
}
fun toPushActivity(view: View) {
}
fun selectAudio(){
if (isSpeaker){
btn_speaker.setText("扬声器")
web_rtc_sv.setSpeakerphoneOn(isSpeaker)
}else{
btn_speaker.setText("话筒")
web_rtc_sv.setSpeakerphoneOn(isSpeaker)
}
isSpeaker=!isSpeaker
fun toDataChannelActivity(view: View) {
}
}

View File

@ -0,0 +1,84 @@
package com.zlmediakit.webrtc
import android.annotation.SuppressLint
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.zlm.rtc.ZLMRTCPlayer
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*
import kotlinx.android.synthetic.main.activity_play.*
class PlayDemoActivity : AppCompatActivity() {
private var isSpeaker = true
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play)
lifecycle.addObserver(web_rtc_sv)
//http://124.223.98.45/index/api/webrtc?app=live&stream=test&type=play
url.setText("https://zlmediakit.com/index/api/webrtc?app=live&stream=test&type=play")
//http://192.168.1.17/index/api/webrtc?app=live&stream=test&type=play
btn_play.setOnClickListener {
web_rtc_sv?.setVideoPath(url.text.toString())
web_rtc_sv.start()
}
web_rtc_sv.setOnErrorListener { errorCode, errorMsg ->
runOnUiThread {
Toast.makeText(this, "errorCode:$errorCode,errorMsg:$errorMsg", Toast.LENGTH_SHORT)
.show()
}
}
btn_pause.setOnClickListener {
web_rtc_sv?.pause()
}
btn_resume.setOnClickListener {
web_rtc_sv?.resume()
}
btn_screenshot.setOnClickListener {
web_rtc_sv?.screenshot {
runOnUiThread {
iv_screen.setImageDrawable(BitmapDrawable(it))
}
}
}
btn_mute.setOnClickListener {
web_rtc_sv.mute(true)
}
selectAudio()
btn_speaker.setOnClickListener {
selectAudio()
}
}
fun selectAudio(){
if (isSpeaker){
btn_speaker.setText("扬声器")
web_rtc_sv.setSpeakerphoneOn(isSpeaker)
}else{
btn_speaker.setText("话筒")
web_rtc_sv.setSpeakerphoneOn(isSpeaker)
}
isSpeaker=!isSpeaker
}
}

View File

@ -0,0 +1,19 @@
package com.zlmediakit.webrtc
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.zlm.rtc.ZLMRTCPlayer
import kotlinx.android.synthetic.main.activity_player.surface_view_renderer
class PlayerDemoActivity:AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play)
ZLMRTCPlayer.shareInstance().bind(this,surface_view_renderer,true)
}
}

View File

@ -0,0 +1,12 @@
package com.zlmediakit.webrtc
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class PushDemoActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}

View File

@ -1,93 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
android:orientation="vertical">
<com.zlmediakit.webrtc.WebRTCSurfaceView
android:id="@+id/web_rtc_sv"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/url"
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/web_rtc_sv"
android:text=""/>
<LinearLayout
android:id="@+id/ll"
android:onClick="toPlayActivity"
android:text="播放" />
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@+id/url">
android:onClick="toPushActivity"
android:text="推流" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_resume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="恢复" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_speaker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扬声器" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_mute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="静音" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll2"
<androidx.appcompat.widget.AppCompatButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@+id/ll">
android:onClick="toDataChannelActivity"
android:text="DataChannel"
android:textAllCaps="false" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_screenshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="截图" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_screen_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="录制" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_screen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PlayDemoActivity">
<com.zlmediakit.webrtc.WebRTCSurfaceView
android:id="@+id/web_rtc_sv"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/web_rtc_sv"
android:text=""/>
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="30dp"
app:layout_constraintTop_toBottomOf="@+id/url">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_resume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="恢复" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_speaker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扬声器" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_mute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="静音" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@+id/ll">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_screenshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="截图" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_screen_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="录制" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_screen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<org.webrtc.SurfaceViewRenderer
android:id="@+id/surface_view_renderer"
android:layout_width="wrap_content"
android:layout_height="240dp"/>
</RelativeLayout>

View File

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

View File

@ -22,3 +22,4 @@ dependencyResolutionManagement {
}
rootProject.name = "android"
include ':app'
include ':zlm'

1
webrtc_player/android/zlm/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,54 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.zlm.rtc'
compileSdk 34
defaultConfig {
minSdk 24
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.13.1'
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.12.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'org.webrtc:google-webrtc:1.0.32006'
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.zlm.rtc
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.zlm.rtc.test", appContext.packageName)
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,46 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("rtc")
add_library(ZLToolKit IMPORTED STATIC)
set_target_properties(tools PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI}/libZLToolKit.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
rtc.cpp)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
log
ZLToolKit)

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef CODEC_AACENCODER_H_
#define CODEC_AACENCODER_H_
namespace mediakit {
class AACEncoder {
public:
AACEncoder(void);
virtual ~AACEncoder(void);
bool init(int iSampleRate, int iAudioChannel, int iAudioSampleBit);
int inputData(char *pcData, int iLen, unsigned char **ppucOutBuffer);
private:
unsigned char *_pucPcmBuf = nullptr;
unsigned int _uiPcmLen = 0;
unsigned char *_pucAacBuf = nullptr;
void *_hEncoder = nullptr;
unsigned long _ulInputSamples = 0;
unsigned long _ulMaxInputBytes = 0;
unsigned long _ulMaxOutputBytes = 0;
};
} /* namespace mediakit */
#endif /* CODEC_AACENCODER_H_ */

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef CODEC_H264ENCODER_H_
#define CODEC_H264ENCODER_H_
#include <cstdint>
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
#include <x264.h>
#ifdef __cplusplus
}
#endif //__cplusplus
namespace mediakit {
class H264Encoder {
public:
typedef struct {
int iType;
int iLength;
uint8_t *pucData;
} H264Frame;
H264Encoder();
~H264Encoder();
bool init(int iWidth, int iHeight, int iFps, int iBitRate);
int inputData(char *yuv[3], int linesize[3], int64_t cts, H264Frame **out_frame);
private:
x264_t *_pX264Handle = nullptr;
x264_picture_t *_pPicIn = nullptr;
x264_picture_t *_pPicOut = nullptr;
H264Frame _aFrames[10];
};
} /* namespace mediakit */
#endif /* CODEC_H264ENCODER_H_ */

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_TRANSCODE_H
#define ZLMEDIAKIT_TRANSCODE_H
#if defined(ENABLE_FFMPEG)
#include "Util/TimeTicker.h"
#include "Common/MediaSink.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/pixdesc.h"
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
#ifdef __cplusplus
}
#endif
namespace mediakit {
class FFmpegFrame {
public:
using Ptr = std::shared_ptr<FFmpegFrame>;
FFmpegFrame(std::shared_ptr<AVFrame> frame = nullptr);
~FFmpegFrame();
AVFrame *get() const;
void fillPicture(AVPixelFormat target_format, int target_width, int target_height);
private:
char *_data = nullptr;
std::shared_ptr<AVFrame> _frame;
};
class FFmpegSwr {
public:
using Ptr = std::shared_ptr<FFmpegSwr>;
FFmpegSwr(AVSampleFormat output, int channel, int channel_layout, int samplerate);
~FFmpegSwr();
FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame);
private:
int _target_channels;
int _target_channel_layout;
int _target_samplerate;
AVSampleFormat _target_format;
SwrContext *_ctx = nullptr;
};
class TaskManager {
public:
virtual ~TaskManager();
void setMaxTaskSize(size_t size);
void stopThread(bool drop_task);
protected:
void startThread(const std::string &name);
bool addEncodeTask(std::function<void()> task);
bool addDecodeTask(bool key_frame, std::function<void()> task);
bool isEnabled() const;
private:
void onThreadRun(const std::string &name);
private:
class ThreadExitException : public std::runtime_error {
public:
ThreadExitException() : std::runtime_error("exit") {}
};
private:
bool _decode_drop_start = false;
bool _exit = false;
size_t _max_task = 30;
std::mutex _task_mtx;
toolkit::semaphore _sem;
toolkit::List<std::function<void()> > _task;
std::shared_ptr<std::thread> _thread;
};
class FFmpegDecoder : public TaskManager {
public:
using Ptr = std::shared_ptr<FFmpegDecoder>;
using onDec = std::function<void(const FFmpegFrame::Ptr &)>;
FFmpegDecoder(const Track::Ptr &track, int thread_num = 2, const std::vector<std::string> &codec_name = {});
~FFmpegDecoder() override;
bool inputFrame(const Frame::Ptr &frame, bool live, bool async, bool enable_merge = true);
void setOnDecode(onDec cb);
void flush();
const AVCodecContext *getContext() const;
private:
void onDecode(const FFmpegFrame::Ptr &frame);
bool inputFrame_l(const Frame::Ptr &frame, bool live, bool enable_merge);
bool decodeFrame(const char *data, size_t size, uint64_t dts, uint64_t pts, bool live, bool key_frame);
private:
bool _do_merger = false;
toolkit::Ticker _ticker;
onDec _cb;
std::shared_ptr<AVCodecContext> _context;
FrameMerger _merger{FrameMerger::h264_prefix};
};
class FFmpegSws {
public:
using Ptr = std::shared_ptr<FFmpegSws>;
FFmpegSws(AVPixelFormat output, int width, int height);
~FFmpegSws();
FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame);
int inputFrame(const FFmpegFrame::Ptr &frame, uint8_t *data);
private:
FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame, int &ret, uint8_t *data);
private:
int _target_width = 0;
int _target_height = 0;
int _src_width = 0;
int _src_height = 0;
SwsContext *_ctx = nullptr;
AVPixelFormat _src_format = AV_PIX_FMT_NONE;
AVPixelFormat _target_format = AV_PIX_FMT_NONE;
};
}//namespace mediakit
#endif// ENABLE_FFMPEG
#endif //ZLMEDIAKIT_TRANSCODE_H

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef DEVICE_DEVICE_H_
#define DEVICE_DEVICE_H_
#include <memory>
#include <string>
#include <functional>
#include "Util/TimeTicker.h"
#include "Common/MultiMediaSourceMuxer.h"
namespace mediakit {
class H264Encoder;
class AACEncoder;
class VideoInfo {
public:
CodecId codecId = CodecH264;
int iWidth;
int iHeight;
float iFrameRate;
int iBitRate = 2 * 1024 * 1024;
};
class AudioInfo {
public:
CodecId codecId = CodecAAC;
int iChannel;
int iSampleBit;
int iSampleRate;
};
/**
* MultiMediaSourceMuxer类的包装便使
*/
class DevChannel : public MultiMediaSourceMuxer{
public:
using Ptr = std::shared_ptr<DevChannel>;
//fDuration<=0为直播否则为点播
DevChannel(const MediaTuple& tuple, float duration = 0, const ProtocolOption &option = ProtocolOption())
: MultiMediaSourceMuxer(tuple, duration, option) {}
/**
* Track
* MultiMediaSourceMuxer::addTrack(VideoTrack::Ptr );
* @param info
*/
bool initVideo(const VideoInfo &info);
/**
* Track
* MultiMediaSourceMuxer::addTrack(AudioTrack::Ptr );
* @param info
*/
bool initAudio(const AudioInfo &info);
/**
* 264
* @param data 264
* @param len
* @param dts 0
* @param pts 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

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_JEMALLOCUTIL_H
#define ZLMEDIAKIT_JEMALLOCUTIL_H
#include <functional>
#include <string>
#include <cstdint>
namespace mediakit {
class JemallocUtil {
public:
static void enable_profiling();
static void disable_profiling();
static void dump(const std::string &file_name);
static std::string get_malloc_stats();
static void some_malloc_stats(const std::function<void(const char *, uint64_t)> &fn);
};
} // namespace mediakit
#endif // ZLMEDIAKIT_JEMALLOCUTIL_H

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MEDIASINK_H
#define ZLMEDIAKIT_MEDIASINK_H
#include <mutex>
#include <memory>
#include "Util/TimeTicker.h"
#include "Extension/Frame.h"
#include "Extension/Track.h"
namespace mediakit{
class TrackListener {
public:
virtual ~TrackListener() = default;
/**
* 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

@ -0,0 +1,443 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MEDIASOURCE_H
#define ZLMEDIAKIT_MEDIASOURCE_H
#include <string>
#include <atomic>
#include <memory>
#include <functional>
#include "Network/Socket.h"
#include "Extension/Track.h"
#include "Record/Recorder.h"
namespace toolkit {
class Session;
} // namespace toolkit
namespace mediakit {
enum class MediaOriginType : uint8_t {
unknown = 0,
rtmp_push ,
rtsp_push,
rtp_push,
pull,
ffmpeg_pull,
mp4_vod,
device_chn,
rtc_push,
srt_push
};
std::string getOriginTypeString(MediaOriginType type);
class MediaSource;
class MultiMediaSourceMuxer;
class MediaSourceEvent {
public:
friend class MediaSource;
class NotImplemented : public std::runtime_error {
public:
template<typename ...T>
NotImplemented(T && ...args) : std::runtime_error(std::forward<T>(args)...) {}
};
virtual ~MediaSourceEvent() = default;
// 获取媒体源类型
virtual MediaOriginType getOriginType(MediaSource &sender) const { return MediaOriginType::unknown; }
// 获取媒体源url或者文件路径
virtual std::string getOriginUrl(MediaSource &sender) const;
// 获取媒体源客户端相关信息
virtual std::shared_ptr<toolkit::SockInfo> getOriginSock(MediaSource &sender) const { return nullptr; }
// 通知拖动进度条
virtual bool seekTo(MediaSource &sender, uint32_t stamp) { return false; }
// 通知暂停或恢复
virtual bool pause(MediaSource &sender, bool pause) { return false; }
// 通知倍数
virtual bool speed(MediaSource &sender, float speed) { return false; }
// 通知其停止产生流
virtual bool close(MediaSource &sender) { return false; }
// 获取观看总人数,此函数一般强制重载
virtual int totalReaderCount(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::totalReaderCount not implemented"); }
// 通知观看人数变化
virtual void onReaderChanged(MediaSource &sender, int size);
//流注册或注销事件
virtual void onRegist(MediaSource &sender, bool regist) {}
// 获取丢包率
virtual float getLossRate(MediaSource &sender, TrackType type) { return -1; }
// 获取所在线程, 此函数一般强制重载
virtual toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) { throw NotImplemented(toolkit::demangle(typeid(*this).name()) + "::getOwnerPoller not implemented"); }
////////////////////////仅供MultiMediaSourceMuxer对象继承////////////////////////
// 开启或关闭录制
virtual bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) { return false; };
// 获取录制状态
virtual bool isRecording(MediaSource &sender, Recorder::type type) { return false; }
// 获取所有track相关信息
virtual std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const { return std::vector<Track::Ptr>(); };
// 获取MultiMediaSourceMuxer对象
virtual std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) { return nullptr; }
class SendRtpArgs {
public:
enum Type { kRtpRAW = 0, kRtpPS = 1, kRtpTS = 2 };
// 是否采用udp方式发送rtp
bool is_udp = true;
// rtp类型
Type type = kRtpPS;
//发送es流时指定是否只发送纯音频流
bool only_audio = false;
//tcp被动方式
bool passive = false;
// rtp payload type
uint8_t pt = 96;
//是否支持同ssrc多服务器发送
bool ssrc_multi_send = false;
// 指定rtp ssrc
std::string ssrc;
// 指定本地发送端口
uint16_t src_port = 0;
// 发送目标端口
uint16_t dst_port;
// 发送目标主机地址可以是ip或域名
std::string dst_url;
//udp发送时是否开启rr rtcp接收超时判断
bool udp_rtcp_timeout = false;
//tcp被动发送服务器延时关闭事件单位毫秒设置为0时则使用默认值5000ms
uint32_t tcp_passive_close_delay_ms = 0;
//udp 发送时rr rtcp包接收超时时间单位毫秒
uint32_t rtcp_timeout_ms = 30 * 1000;
//udp 发送时发送sr rtcp包间隔单位毫秒
uint32_t rtcp_send_interval_ms = 5 * 1000;
//发送rtp同时接收一般用于双向语言对讲, 如果不为空,说明开启接收
std::string recv_stream_id;
};
// 开始发送ps-rtp
virtual void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) { cb(0, toolkit::SockException(toolkit::Err_other, "not implemented"));};
// 停止发送ps-rtp
virtual bool stopSendRtp(MediaSource &sender, const std::string &ssrc) {return false; }
private:
toolkit::Timer::Ptr _async_close_timer;
};
template <typename MAP, typename KEY, typename TYPE>
static void getArgsValue(const MAP &allArgs, const KEY &key, TYPE &value) {
auto val = ((MAP &)allArgs)[key];
if (!val.empty()) {
value = (TYPE)val;
}
}
class ProtocolOption {
public:
ProtocolOption();
enum {
kModifyStampOff = 0, // 采用源视频流绝对时间戳,不做任何改变
kModifyStampSystem = 1, // 采用zlmediakit接收数据时的系统时间戳(有平滑处理)
kModifyStampRelative = 2 // 采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正
};
// 时间戳类型
int modify_stamp;
//转协议是否开启音频
bool enable_audio;
//添加静音音频,在关闭音频时,此开关无效
bool add_mute_audio;
// 无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
// 此配置置1时此流如果无人观看将不触发on_none_reader hook回调
// 而是将直接关闭流
bool auto_close;
//断连续推延时,单位毫秒,默认采用配置文件
uint32_t continue_push_ms;
// 平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存
// 该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题
uint32_t paced_sender_ms;
//是否开启转换为hls(mpegts)
bool enable_hls;
//是否开启转换为hls(fmp4)
bool enable_hls_fmp4;
//是否开启MP4录制
bool enable_mp4;
//是否开启转换为rtsp/webrtc
bool enable_rtsp;
//是否开启转换为rtmp/flv
bool enable_rtmp;
//是否开启转换为http-ts/ws-ts
bool enable_ts;
//是否开启转换为http-fmp4/ws-fmp4
bool enable_fmp4;
// hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
bool hls_demand;
// rtsp[s]协议是否按需生成
bool rtsp_demand;
// rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
bool rtmp_demand;
// http[s]-ts协议是否按需生成
bool ts_demand;
// http[s]-fmp4、ws[s]-fmp4协议是否按需生成
bool fmp4_demand;
//是否将mp4录制当做观看者
bool mp4_as_player;
//mp4切片大小单位秒
size_t mp4_max_second;
//mp4录制保存路径
std::string mp4_save_path;
//hls录制保存路径
std::string hls_save_path;
// 支持通过on_publish返回值替换stream_id
std::string stream_replace;
// 最大track数
size_t max_track = 2;
template <typename MAP>
ProtocolOption(const MAP &allArgs) : ProtocolOption() {
load(allArgs);
}
template <typename MAP>
void load(const MAP &allArgs) {
#define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key)
GET_OPT_VALUE(modify_stamp);
GET_OPT_VALUE(enable_audio);
GET_OPT_VALUE(add_mute_audio);
GET_OPT_VALUE(auto_close);
GET_OPT_VALUE(continue_push_ms);
GET_OPT_VALUE(paced_sender_ms);
GET_OPT_VALUE(enable_hls);
GET_OPT_VALUE(enable_hls_fmp4);
GET_OPT_VALUE(enable_mp4);
GET_OPT_VALUE(enable_rtsp);
GET_OPT_VALUE(enable_rtmp);
GET_OPT_VALUE(enable_ts);
GET_OPT_VALUE(enable_fmp4);
GET_OPT_VALUE(hls_demand);
GET_OPT_VALUE(rtsp_demand);
GET_OPT_VALUE(rtmp_demand);
GET_OPT_VALUE(ts_demand);
GET_OPT_VALUE(fmp4_demand);
GET_OPT_VALUE(mp4_max_second);
GET_OPT_VALUE(mp4_as_player);
GET_OPT_VALUE(mp4_save_path);
GET_OPT_VALUE(hls_save_path);
GET_OPT_VALUE(stream_replace);
GET_OPT_VALUE(max_track);
}
};
//该对象用于拦截感兴趣的MediaSourceEvent事件
class MediaSourceEventInterceptor : public MediaSourceEvent {
public:
void setDelegate(const std::weak_ptr<MediaSourceEvent> &listener);
std::shared_ptr<MediaSourceEvent> getDelegate() const;
MediaOriginType getOriginType(MediaSource &sender) const override;
std::string getOriginUrl(MediaSource &sender) const override;
std::shared_ptr<toolkit::SockInfo> getOriginSock(MediaSource &sender) const override;
bool seekTo(MediaSource &sender, uint32_t stamp) override;
bool pause(MediaSource &sender, bool pause) override;
bool speed(MediaSource &sender, float speed) override;
bool close(MediaSource &sender) override;
int totalReaderCount(MediaSource &sender) override;
void onReaderChanged(MediaSource &sender, int size) override;
void onRegist(MediaSource &sender, bool regist) override;
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override;
bool isRecording(MediaSource &sender, Recorder::type type) override;
std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
void startSendRtp(MediaSource &sender, const SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) override;
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
float getLossRate(MediaSource &sender, TrackType type) override;
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) override;
private:
std::weak_ptr<MediaSourceEvent> _listener;
};
/**
* url获取媒体相关信息
*/
class MediaInfo: public MediaTuple {
public:
MediaInfo() = default;
MediaInfo(const std::string &url) { parse(url); }
void parse(const std::string &url);
std::string getUrl() const { return schema + "://" + shortUrl(); }
public:
uint16_t port = 0;
std::string full_url;
std::string schema;
std::string host;
};
bool equalMediaTuple(const MediaTuple& a, const MediaTuple& b);
/**
* rtsp/rtmp的直播流都源自该对象
*/
class MediaSource: public TrackSource, public std::enable_shared_from_this<MediaSource> {
public:
static MediaSource& NullMediaSource();
using Ptr = std::shared_ptr<MediaSource>;
MediaSource(const std::string &schema, const MediaTuple& tuple);
virtual ~MediaSource();
////////////////获取MediaSource相关信息////////////////
// 获取协议类型
const std::string& getSchema() const {
return _schema;
}
const MediaTuple& getMediaTuple() const {
return _tuple;
}
std::string getUrl() const { return _schema + "://" + _tuple.shortUrl(); }
//获取对象所有权
std::shared_ptr<void> getOwnership();
// 获取所有Track
std::vector<Track::Ptr> getTracks(bool ready = true) const override;
// 获取流当前时间戳
virtual uint32_t getTimeStamp(TrackType type) { return 0; };
// 设置时间戳
virtual void setTimeStamp(uint32_t stamp) {};
// 获取数据速率单位bytes/s
int getBytesSpeed(TrackType type = TrackInvalid);
// 获取流创建GMT unix时间戳单位秒
uint64_t getCreateStamp() const { return _create_stamp; }
// 获取流上线时间,单位秒
uint64_t getAliveSecond() const;
////////////////MediaSourceEvent相关接口实现////////////////
// 设置监听者
virtual void setListener(const std::weak_ptr<MediaSourceEvent> &listener);
// 获取监听者
std::weak_ptr<MediaSourceEvent> getListener() const;
// 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数
virtual int readerCount() = 0;
// 观看者个数,包括(hls/rtsp/rtmp)
virtual int totalReaderCount();
// 获取播放器列表
virtual void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) {
assert(cb);
cb(std::list<toolkit::Any>());
}
virtual bool broadcastMessage(const toolkit::Any &data) { return false; }
// 获取媒体源类型
MediaOriginType getOriginType() const;
// 获取媒体源url或者文件路径
std::string getOriginUrl() const;
// 获取媒体源客户端相关信息
std::shared_ptr<toolkit::SockInfo> getOriginSock() const;
// 拖动进度条
bool seekTo(uint32_t stamp);
// 暂停
bool pause(bool pause);
// 倍数播放
bool speed(float speed);
// 关闭该流
bool close(bool force);
// 该流观看人数变化
void onReaderChanged(int size);
// 开启或关闭录制
bool setupRecord(Recorder::type type, bool start, const std::string &custom_path, size_t max_second);
// 获取录制状态
bool isRecording(Recorder::type type);
// 开始发送ps-rtp
void startSendRtp(const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb);
// 停止发送ps-rtp
bool stopSendRtp(const std::string &ssrc);
// 获取丢包率
float getLossRate(mediakit::TrackType type);
// 获取所在线程
toolkit::EventPoller::Ptr getOwnerPoller();
// 获取MultiMediaSourceMuxer对象
std::shared_ptr<MultiMediaSourceMuxer> getMuxer();
////////////////static方法查找或生成MediaSource////////////////
// 同步查找流
static Ptr find(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &id, bool from_mp4 = false);
static Ptr find(const MediaInfo &info, bool from_mp4 = false) {
return find(info.schema, info.vhost, info.app, info.stream, from_mp4);
}
// 忽略schema同步查找流可能返回rtmp/rtsp/hls类型
static Ptr find(const std::string &vhost, const std::string &app, const std::string &stream_id, bool from_mp4 = false);
// 异步查找流
static void findAsync(const MediaInfo &info, const std::shared_ptr<toolkit::Session> &session, const std::function<void(const Ptr &src)> &cb);
// 遍历所有流
static void for_each_media(const std::function<void(const Ptr &src)> &cb, const std::string &schema = "", const std::string &vhost = "", const std::string &app = "", const std::string &stream = "");
// 从mp4文件生成MediaSource
static MediaSource::Ptr createFromMP4(const std::string &schema, const std::string &vhost, const std::string &app, const std::string &stream, const std::string &file_path = "", bool check_app = true);
protected:
//媒体注册
void regist();
private:
// 媒体注销
bool unregist();
// 触发媒体事件
void emitEvent(bool regist);
protected:
toolkit::BytesSpeed _speed[TrackMax];
MediaTuple _tuple;
private:
std::atomic_flag _owned { false };
time_t _create_stamp;
toolkit::Ticker _ticker;
std::string _schema;
std::weak_ptr<MediaSourceEvent> _listener;
// 对象个数统计
toolkit::ObjectStatistic<MediaSource> _statistic;
};
} /* namespace mediakit */
#endif //ZLMEDIAKIT_MEDIASOURCE_H

View File

@ -0,0 +1,188 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#define ZLMEDIAKIT_MULTIMEDIASOURCEMUXER_H
#include "Common/Stamp.h"
#include "Common/MediaSource.h"
#include "Common/MediaSink.h"
#include "Record/Recorder.h"
#include "Rtp/RtpSender.h"
#include "Record/HlsRecorder.h"
#include "Record/HlsMediaSource.h"
#include "Rtsp/RtspMediaSourceMuxer.h"
#include "Rtmp/RtmpMediaSourceMuxer.h"
#include "TS/TSMediaSourceMuxer.h"
#include "FMP4/FMP4MediaSourceMuxer.h"
namespace mediakit {
class MultiMediaSourceMuxer : public MediaSourceEventInterceptor, public MediaSink, public std::enable_shared_from_this<MultiMediaSourceMuxer>{
public:
using Ptr = std::shared_ptr<MultiMediaSourceMuxer>;
using RingType = toolkit::RingBuffer<Frame::Ptr>;
class Listener {
public:
virtual ~Listener() = default;
virtual void onAllTrackReady() = 0;
};
MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec = 0.0,const ProtocolOption &option = ProtocolOption());
/**
*
* @param listener
*/
void setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener);
/**
* Track就绪事件监听器
* @param listener
*/
void setTrackListener(const std::weak_ptr<Listener> &listener);
/**
*
*/
int totalReaderCount() const;
/**
* ()
*/
bool isEnabled();
/**
* MediaSource时间戳
* @param stamp
*/
void setTimeStamp(uint32_t stamp);
/**
* track
*/
void resetTracks() override;
/////////////////////////////////MediaSourceEvent override/////////////////////////////////
/**
*
* @param sender
* @return
*/
int totalReaderCount(MediaSource &sender) override;
/**
*
* @param type
* @param start
* @param custom_path
* @return
*/
bool setupRecord(MediaSource &sender, Recorder::type type, bool start, const std::string &custom_path, size_t max_second) override;
/**
*
* @param type
* @return
*/
bool isRecording(MediaSource &sender, Recorder::type type) override;
/**
* ps-rtp流
* @param dst_url ip或域名
* @param dst_port
* @param ssrc rtp的ssrc
* @param is_udp udp
* @param cb
*/
void startSendRtp(MediaSource &sender, const MediaSourceEvent::SendRtpArgs &args, const std::function<void(uint16_t, const toolkit::SockException &)> cb) override;
/**
* ps-rtp发送
* @return
*/
bool stopSendRtp(MediaSource &sender, const std::string &ssrc) override;
/**
* Track
* @param trackReady track
* @return Track
*/
std::vector<Track::Ptr> getMediaTracks(MediaSource &sender, bool trackReady = true) const override;
/**
* 线
*/
toolkit::EventPoller::Ptr getOwnerPoller(MediaSource &sender) override;
/**
*
*/
std::shared_ptr<MultiMediaSourceMuxer> getMuxer(MediaSource &sender) override;
const ProtocolOption &getOption() const;
const MediaTuple &getMediaTuple() const;
std::string shortUrl() const;
protected:
/////////////////////////////////MediaSink override/////////////////////////////////
/**
* track已经准备好ready()true
* sps pps等相关信息了
* @param track
*/
bool onTrackReady(const Track::Ptr & track) override;
/**
* Track已经准备好
*/
void onAllTrackReady() override;
/**
* Track输出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

@ -0,0 +1,90 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_PACKET_CACHE_H_
#define ZLMEDIAKIT_PACKET_CACHE_H_
#include "Common/config.h"
#include "Util/List.h"
namespace mediakit {
/// 缓存刷新策略类
class FlushPolicy {
public:
bool isFlushAble(bool is_video, bool is_key, uint64_t new_stamp, size_t cache_size);
private:
// 音视频的最后时间戳
uint64_t _last_stamp[2] = { 0, 0 };
};
/// 合并写缓存模板
/// \tparam packet 包类型
/// \tparam policy 刷新缓存策略
/// \tparam packet_list 包缓存类型
template<typename packet, typename policy = FlushPolicy, typename packet_list = toolkit::List<std::shared_ptr<packet> > >
class PacketCache {
public:
PacketCache() { _cache = std::make_shared<packet_list>(); }
virtual ~PacketCache() = default;
void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr<packet> pkt, bool key_pos) {
bool flag = flushImmediatelyWhenCloseMerge();
if (!flag && _policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) {
flush();
}
//追加数据到最后
_cache->emplace_back(std::move(pkt));
if (key_pos) {
_key_pos = key_pos;
}
if (flag) {
flush();
}
}
void flush() {
if (_cache->empty()) {
return;
}
onFlush(std::move(_cache), _key_pos);
_cache = std::make_shared<packet_list>();
_key_pos = false;
}
virtual void clearCache() {
_cache->clear();
}
virtual void onFlush(std::shared_ptr<packet_list>, bool key_pos) = 0;
private:
bool flushImmediatelyWhenCloseMerge() {
// 一般的协议关闭合并写时立即刷新缓存这样可以减少一帧的延时但是rtp例外
// 因为rtp的包很小一个RtpPacket包中也不是完整的一帧图像所以在关闭合并写时
// 还是有必要缓冲一帧的rtp(也就是时间戳相同的rtp)再输出,这样虽然会增加一帧的延时
// 但是却对性能提升很大,这样做还是比较划算的
GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS);
GET_CONFIG(int, rtspLowLatency, Rtsp::kLowLatency);
return std::is_same<packet, RtpPacket>::value ? rtspLowLatency : (mergeWriteMS <= 0);
}
private:
bool _key_pos = false;
policy _policy;
std::shared_ptr<packet_list> _cache;
};
}
#endif //ZLMEDIAKIT_PACKET_CACHE_H_

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_PARSER_H
#define ZLMEDIAKIT_PARSER_H
#include <map>
#include <string>
#include "Util/util.h"
namespace mediakit {
// 从字符串中提取子字符串
std::string findSubString(const char *buf, const char *start, const char *end, size_t buf_size = 0);
// 把url解析为主机地址和端口号,兼容ipv4/ipv6/dns
void splitUrl(const std::string &url, std::string &host, uint16_t &port);
// 解析proxy url,仅支持http
void parseProxyUrl(const std::string &proxy_url, std::string &proxy_host, uint16_t &proxy_port, std::string &proxy_auth);
struct StrCaseCompare {
bool operator()(const std::string &__x, const std::string &__y) const { return strcasecmp(__x.data(), __y.data()) < 0; }
};
class StrCaseMap : public std::multimap<std::string, std::string, StrCaseCompare> {
public:
using Super = std::multimap<std::string, std::string, StrCaseCompare>;
std::string &operator[](const std::string &k) {
auto it = find(k);
if (it == end()) {
it = Super::emplace(k, "");
}
return it->second;
}
template <typename K, typename V>
void emplace(K &&k, V &&v) {
auto it = find(k);
if (it != end()) {
return;
}
Super::emplace(std::forward<K>(k), std::forward<V>(v));
}
template <typename K, typename V>
void emplace_force(K &&k, V &&v) {
Super::emplace(std::forward<K>(k), std::forward<V>(v));
}
};
// rtsp/http/sip解析类
class Parser {
public:
// 解析http/rtsp/sip请求需要确保buf以\0结尾
void parse(const char *buf, size_t size);
// 获取命令字如GET/POST
const std::string &method() const;
// 请求时获取中间url不包含?后面的参数
const std::string &url() const;
// 回复时获取状态码如200/404
const std::string &status() const;
// 获取中间url包含?后面的参数
std::string fullUrl() const;
// 请求时获取协议名如HTTP/1.1
const std::string &protocol() const;
// 回复时,获取状态字符串,如 OK/Not Found
const std::string &statusStr() const;
// 根据header key名获取请求header value值
const std::string &operator[](const char *name) const;
// 获取http body或sdp
const std::string &content() const;
// 清空,为了重用
void clear();
// 获取?后面的参数
const std::string &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

@ -0,0 +1,148 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_STAMP_H
#define ZLMEDIAKIT_STAMP_H
#include <set>
#include <cstdint>
#include "Util/TimeTicker.h"
namespace mediakit {
class DeltaStamp {
public:
DeltaStamp();
virtual ~DeltaStamp() = default;
/**
*
* @param stamp
* @param enable_rollback 退
* @return
*/
int64_t deltaStamp(int64_t stamp, bool enable_rollback = true);
int64_t relativeStamp(int64_t stamp, bool enable_rollback = true);
int64_t relativeStamp();
// 设置最大允许回退或跳跃幅度
void setMaxDelta(size_t max_delta);
protected:
virtual void needSync() {}
protected:
int _max_delta;
int64_t _last_stamp = 0;
int64_t _relative_stamp = 0;
};
//该类解决时间戳回环、回退问题
//计算相对时间戳或者产生平滑时间戳
class Stamp : public DeltaStamp{
public:
/**
* ,dts回退等功能
* @param dts 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

@ -0,0 +1,444 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef COMMON_CONFIG_H
#define COMMON_CONFIG_H
#include "Util/NoticeCenter.h"
#include "Util/mini.h"
#include "Util/onceToken.h"
#include "macros.h"
#include <functional>
namespace mediakit {
class ProtocolOption;
// 加载配置文件,如果配置文件不存在,那么会导出默认配置并生成配置文件
// 加载配置文件成功后会触发kBroadcastUpdateConfig广播
// 如果指定的文件名(ini_path)为空,那么会加载默认配置文件
// 默认配置文件名为 /path/to/your/exe.ini
// 加载配置文件成功后返回true否则返回false
bool loadIniConfig(const char *ini_path = nullptr);
////////////广播名称///////////
namespace Broadcast {
// 注册或反注册MediaSource事件广播
extern const std::string kBroadcastMediaChanged;
#define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender
// 录制mp4文件成功后广播
extern const std::string kBroadcastRecordMP4;
#define BroadcastRecordMP4Args const RecordInfo &info
// 录制 ts 文件后广播
extern const std::string kBroadcastRecordTs;
#define BroadcastRecordTsArgs const RecordInfo &info
// 收到http api请求广播
extern const std::string kBroadcastHttpRequest;
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限
extern const std::string kBroadcastHttpAccess;
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射
// 在该事件中通过自行覆盖path参数可以做到譬如根据虚拟主机或者app选择不同http根目录的目的
extern const std::string kBroadcastHttpBeforeAccess;
#define BroadcastHttpBeforeAccessArgs const Parser &parser, std::string &path, SockInfo &sender
// 该流是否需要认证是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证
extern const std::string kBroadcastOnGetRtspRealm;
#define BroadcastOnGetRtspRealmArgs const MediaInfo &args, const RtspSession::onGetRealm &invoker, SockInfo &sender
// 请求认证用户密码事件user_name为用户名must_no_encrypt如果为true则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败
// 获取到密码后请调用invoker并输入对应类型的密码和密码类型invoker执行时会匹配密码
extern const std::string kBroadcastOnRtspAuth;
#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, SockInfo &sender
// 推流鉴权结果回调对象
// 如果err为空则代表鉴权成功
using PublishAuthInvoker = std::function<void(const std::string &err, const ProtocolOption &option)>;
// 收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权
extern const std::string kBroadcastMediaPublish;
#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender
// 播放鉴权结果回调对象
// 如果err为空则代表鉴权成功
using AuthInvoker = std::function<void(const std::string &err)>;
// 播放rtsp/rtmp/http-flv事件广播通过该事件控制播放鉴权
extern const std::string kBroadcastMediaPlayed;
#define BroadcastMediaPlayedArgs const MediaInfo &args, const Broadcast::AuthInvoker &invoker, SockInfo &sender
// shell登录鉴权
extern const std::string kBroadcastShellLogin;
#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender
// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播
extern const std::string kBroadcastFlowReport;
#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, SockInfo &sender
// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了
extern const std::string kBroadcastNotFoundStream;
#define BroadcastNotFoundStreamArgs const MediaInfo &args, SockInfo &sender, const std::function<void()> &closePlayer
// 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑
extern const std::string kBroadcastStreamNoneReader;
#define BroadcastStreamNoneReaderArgs MediaSource &sender
// rtp推流被动停止时触发
extern const std::string kBroadcastSendRtpStopped;
#define BroadcastSendRtpStoppedArgs MultiMediaSourceMuxer &sender, const std::string &ssrc, const SockException &ex
// 更新配置文件事件广播,执行loadIniConfig函数加载配置文件成功后会触发该广播
extern const std::string kBroadcastReloadConfig;
#define BroadcastReloadConfigArgs void
// rtp server 超时
extern const std::string kBroadcastRtpServerTimeout;
#define BroadcastRtpServerTimeoutArgs uint16_t &local_port, const string &stream_id,int &tcp_mode, bool &re_use_port, uint32_t &ssrc
// rtc transport sctp 连接状态
extern const std::string kBroadcastRtcSctpConnecting;
extern const std::string kBroadcastRtcSctpConnected;
extern const std::string kBroadcastRtcSctpFailed;
extern const std::string kBroadcastRtcSctpClosed;
#define BroadcastRtcSctpConnectArgs WebRtcTransport& sender
// rtc transport sctp 发送数据
extern const std::string kBroadcastRtcSctpSend;
#define BroadcastRtcSctpSendArgs WebRtcTransport& sender, const uint8_t *&data, size_t& len
// rtc transport sctp 接收数据
extern const std::string kBroadcastRtcSctpReceived;
#define BroadcastRtcSctpReceivedArgs WebRtcTransport& sender, uint16_t &streamId, uint32_t &ppid, const uint8_t *&msg, size_t &len
#define ReloadConfigTag ((void *)(0xFF))
#define RELOAD_KEY(arg, key) \
do { \
decltype(arg) arg##_tmp = ::toolkit::mINI::Instance()[key]; \
if (arg == arg##_tmp) { \
return; \
} \
arg = arg##_tmp; \
InfoL << "reload config:" << key << "=" << arg; \
} while (0)
// 监听某个配置发送变更
#define LISTEN_RELOAD_KEY(arg, key, ...) \
do { \
static ::toolkit::onceToken s_token_listen([]() { \
::toolkit::NoticeCenter::Instance().addListener( \
ReloadConfigTag, Broadcast::kBroadcastReloadConfig, [](BroadcastReloadConfigArgs) { __VA_ARGS__; }); \
}); \
} while (0)
#define GET_CONFIG(type, arg, key) \
static type arg = ::toolkit::mINI::Instance()[key]; \
LISTEN_RELOAD_KEY(arg, key, { RELOAD_KEY(arg, key); });
#define GET_CONFIG_FUNC(type, arg, key, ...) \
static type arg; \
do { \
static ::toolkit::onceToken s_token_set([]() { \
static auto lam = __VA_ARGS__; \
static auto arg##_str = ::toolkit::mINI::Instance()[key]; \
arg = lam(arg##_str); \
LISTEN_RELOAD_KEY(arg, key, { \
RELOAD_KEY(arg##_str, key); \
arg = lam(arg##_str); \
}); \
}); \
} while (0)
} // namespace Broadcast
////////////通用配置///////////
namespace General {
// 每个流媒体服务器的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

@ -0,0 +1,102 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MACROS_H
#define ZLMEDIAKIT_MACROS_H
#include "Util/logger.h"
#include <iostream>
#include <sstream>
#if defined(__MACH__)
#include <arpa/inet.h>
#include <machine/endian.h>
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#elif defined(__linux__)
#include <arpa/inet.h>
#include <endian.h>
#elif defined(_WIN32)
#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BYTE_ORDER LITTLE_ENDIAN
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#endif
#ifndef CHECK
#define CHECK(exp, ...) ::mediakit::Assert_ThrowCpp(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
#endif // CHECK
#ifndef CHECK_RET
#define CHECK_RET(...) \
try { \
CHECK(__VA_ARGS__); \
} catch (AssertFailedException & ex) { \
WarnL << ex.what(); \
return; \
}
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif // MAX
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif // MIN
#ifndef CLEAR_ARR
#define CLEAR_ARR(arr) \
for (auto &item : arr) { \
item = 0; \
}
#endif // CLEAR_ARR
#define RTSP_SCHEMA "rtsp"
#define RTMP_SCHEMA "rtmp"
#define TS_SCHEMA "ts"
#define FMP4_SCHEMA "fmp4"
#define HLS_SCHEMA "hls"
#define HLS_FMP4_SCHEMA "hls.fmp4"
#define VHOST_KEY "vhost"
#define DEFAULT_VHOST "__defaultVhost__"
#ifdef __cplusplus
extern "C" {
#endif
extern void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str);
#ifdef __cplusplus
}
#endif
namespace mediakit {
class AssertFailedException : public std::runtime_error {
public:
template<typename ...T>
AssertFailedException(T && ...args) : std::runtime_error(std::forward<T>(args)...) {}
};
extern const char kServerName[];
template <typename... ARGS>
void Assert_ThrowCpp(int failed, const char *exp, const char *func, const char *file, int line, ARGS &&...args) {
if (failed) {
std::stringstream ss;
toolkit::LoggerWrapper::appendLog(ss, std::forward<ARGS>(args)...);
Assert_Throw(failed, exp, func, file, line, ss.str().data());
}
}
} // namespace mediakit
#endif // ZLMEDIAKIT_MACROS_H

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_HTTP_STRCODING_H_
#define SRC_HTTP_STRCODING_H_
#include <iostream>
#include <string>
#include <cstdint>
namespace mediakit {
class strCoding {
public:
static std::string UrlEncodePath(const std::string &str); //url路径 utf8编码
static std::string UrlEncodeComponent(const std::string &str); // url参数 utf8编码
static std::string UrlDecodePath(const std::string &str); //url路径 utf8解码
static std::string UrlDecodeComponent(const std::string &str); // url参数 utf8解码
#if defined(_WIN32)
static std::string UTF8ToGB2312(const std::string &str);//utf_8转为gb2312
static std::string GB2312ToUTF8(const std::string &str); //gb2312 转utf_8
#endif//defined(_WIN32)
private:
strCoding(void);
virtual ~strCoding(void);
};
} /* namespace mediakit */
#endif /* SRC_HTTP_STRCODING_H_ */

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_COMMONRTMP_H
#define ZLMEDIAKIT_COMMONRTMP_H
#include "Frame.h"
#include "Rtmp/RtmpCodec.h"
namespace mediakit{
/**
* rtmp解码类
*/
class CommonRtmpDecoder : public RtmpCodec {
public:
using Ptr = std::shared_ptr<CommonRtmpDecoder>;
/**
*
*/
CommonRtmpDecoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
* Rtmp并解码
* @param rtmp Rtmp数据包
*/
void inputRtmp(const RtmpPacket::Ptr &rtmp) override;
};
/**
* rtmp编码类
*/
class CommonRtmpEncoder : public RtmpCodec {
public:
using Ptr = std::shared_ptr<CommonRtmpEncoder>;
CommonRtmpEncoder(const Track::Ptr &track) : RtmpCodec(track) {}
/**
*
*/
bool inputFrame(const Frame::Ptr &frame) override;
private:
uint8_t _audio_flv_flags { 0 };
};
}//namespace mediakit
#endif //ZLMEDIAKIT_COMMONRTMP_H

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_COMMONRTP_H
#define ZLMEDIAKIT_COMMONRTP_H
#include "Frame.h"
#include "Rtsp/RtpCodec.h"
namespace mediakit{
/**
* rtp解码类
*/
class CommonRtpDecoder : public RtpCodec {
public:
using Ptr = std::shared_ptr <CommonRtpDecoder>;
/**
*
* @param codec id
* @param max_frame_size
*/
CommonRtpDecoder(CodecId codec, size_t max_frame_size = 2 * 1024);
/**
* rtp并解码
* @param rtp rtp数据包
* @param key_pos false,
*/
bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override;
private:
void obtainFrame();
private:
bool _drop_flag = false;
uint16_t _last_seq = 0;
uint64_t _last_stamp = 0;
size_t _max_frame_size;
CodecId _codec;
FrameImp::Ptr _frame;
};
/**
* rtp编码类
*/
class CommonRtpEncoder : public RtpCodec {
public:
using Ptr = std::shared_ptr <CommonRtpEncoder>;
/**
* rtp
*/
bool inputFrame(const Frame::Ptr &frame) override;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_COMMONRTP_H

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_FACTORY_H
#define ZLMEDIAKIT_FACTORY_H
#include <string>
#include "Rtmp/amf.h"
#include "Extension/Track.h"
#include "Extension/Frame.h"
#include "Rtsp/RtpCodec.h"
#include "Rtmp/RtmpCodec.h"
#include "Util/onceToken.h"
#define REGISTER_STATIC_VAR_INNER(var_name, line) var_name##_##line##__
#define REGISTER_STATIC_VAR(var_name, line) REGISTER_STATIC_VAR_INNER(var_name, line)
#define REGISTER_CODEC(plugin) \
static toolkit::onceToken REGISTER_STATIC_VAR(s_token, __LINE__) ([]() { \
Factory::registerPlugin(plugin); \
});
namespace mediakit {
struct CodecPlugin {
CodecId (*getCodec)();
Track::Ptr (*getTrackByCodecId)(int sample_rate, int channels, int sample_bit);
Track::Ptr (*getTrackBySdp)(const SdpTrack::Ptr &track);
RtpCodec::Ptr (*getRtpEncoderByCodecId)(uint8_t pt);
RtpCodec::Ptr (*getRtpDecoderByCodecId)();
RtmpCodec::Ptr (*getRtmpEncoderByTrack)(const Track::Ptr &track);
RtmpCodec::Ptr (*getRtmpDecoderByTrack)(const Track::Ptr &track);
Frame::Ptr (*getFrameFromPtr)(const char *data, size_t bytes, uint64_t dts, uint64_t pts);
};
class Factory {
public:
/**
* 线
*/
static void registerPlugin(const CodecPlugin &plugin);
/**
* codec_id track
* @param codecId id
* @param sample_rate 90000
* @param channels
* @param sample_bit
*/
static Track::Ptr getTrackByCodecId(CodecId codecId, int sample_rate = 0, int channels = 0, int sample_bit = 0);
////////////////////////////////rtsp相关//////////////////////////////////
/**
* sdp生成Track对象
*/
static Track::Ptr getTrackBySdp(const SdpTrack::Ptr &track);
/**
* c api Track生成具体Track对象
*/
static Track::Ptr getTrackByAbstractTrack(const Track::Ptr& track);
/**
* codec id生成rtp编码器
* @param codec_id id
* @param pt rtp payload type
*/
static RtpCodec::Ptr getRtpEncoderByCodecId(CodecId codec_id, uint8_t pt);
/**
* Track生成Rtp解包器
*/
static RtpCodec::Ptr getRtpDecoderByCodecId(CodecId codec);
////////////////////////////////rtmp相关//////////////////////////////////
/**
* amf对象获取视频相应的Track
* @param amf rtmp metadata中的videocodecid的值
*/
static Track::Ptr getVideoTrackByAmf(const AMFValue &amf);
/**
* amf对象获取音频相应的Track
* @param amf rtmp metadata中的audiocodecid的值
*/
static Track::Ptr getAudioTrackByAmf(const AMFValue& amf, int sample_rate, int channels, int sample_bit);
/**
* Track获取Rtmp的编码器
* @param track
*/
static RtmpCodec::Ptr getRtmpEncoderByTrack(const Track::Ptr &track);
/**
* Track获取Rtmp的解码器
* @param track
*/
static RtmpCodec::Ptr getRtmpDecoderByTrack(const Track::Ptr &track);
/**
* codecId获取rtmp的codec描述
*/
static AMFValue getAmfByCodecId(CodecId codecId);
static Frame::Ptr getFrameFromPtr(CodecId codec, const char *data, size_t size, uint64_t dts, uint64_t pts);
static Frame::Ptr getFrameFromBuffer(CodecId codec, toolkit::Buffer::Ptr data, uint64_t dts, uint64_t pts);
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FACTORY_H

View File

@ -0,0 +1,650 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_FRAME_H
#define ZLMEDIAKIT_FRAME_H
#include <map>
#include <mutex>
#include <functional>
#include "Util/List.h"
#include "Util/TimeTicker.h"
#include "Common/Stamp.h"
#include "Network/Buffer.h"
namespace mediakit {
class Stamp;
typedef enum {
TrackInvalid = -1,
TrackVideo = 0,
TrackAudio,
TrackTitle,
TrackApplication,
TrackMax
} TrackType;
#define CODEC_MAP(XX) \
XX(CodecH264, TrackVideo, 0, "H264", PSI_STREAM_H264, MOV_OBJECT_H264) \
XX(CodecH265, TrackVideo, 1, "H265", PSI_STREAM_H265, MOV_OBJECT_HEVC) \
XX(CodecAAC, TrackAudio, 2, "mpeg4-generic", PSI_STREAM_AAC, MOV_OBJECT_AAC) \
XX(CodecG711A, TrackAudio, 3, "PCMA", PSI_STREAM_AUDIO_G711A, MOV_OBJECT_G711a) \
XX(CodecG711U, TrackAudio, 4, "PCMU", PSI_STREAM_AUDIO_G711U, MOV_OBJECT_G711u) \
XX(CodecOpus, TrackAudio, 5, "opus", PSI_STREAM_AUDIO_OPUS, MOV_OBJECT_OPUS) \
XX(CodecL16, TrackAudio, 6, "L16", PSI_STREAM_RESERVED, MOV_OBJECT_NONE) \
XX(CodecVP8, TrackVideo, 7, "VP8", PSI_STREAM_VP8, MOV_OBJECT_VP8) \
XX(CodecVP9, TrackVideo, 8, "VP9", PSI_STREAM_VP9, MOV_OBJECT_VP9) \
XX(CodecAV1, TrackVideo, 9, "AV1", PSI_STREAM_AV1, MOV_OBJECT_AV1) \
XX(CodecJPEG, TrackVideo, 10, "JPEG", PSI_STREAM_JPEG_2000, MOV_OBJECT_JPEG)
typedef enum {
CodecInvalid = -1,
#define XX(name, type, value, str, mpeg_id, mp4_id) name = value,
CODEC_MAP(XX)
#undef XX
CodecMax
} CodecId;
/**
*
*/
TrackType getTrackType(const std::string &str);
/**
*
*/
const char* getTrackString(TrackType type);
/**
* SDP中描述获取codec_id
* @param str
* @return
*/
CodecId getCodecId(const std::string &str);
/**
*
*/
const char *getCodecName(CodecId codecId);
/**
*
*/
TrackType getTrackType(CodecId codecId);
/**
* codecid获取mov object id
*/
int getMovIdByCodec(CodecId codecId);
/**
* mov object id获取CodecId
*/
CodecId getCodecByMovId(int object_id);
/**
* codecid获取mpeg id
*/
int getMpegIdByCodec(CodecId codec);
/**
* mpeg id获取CodecId
*/
CodecId getCodecByMpegId(int mpeg_id);
/**
*
*/
class CodecInfo {
public:
using Ptr = std::shared_ptr<CodecInfo>;
virtual ~CodecInfo() = default;
/**
*
*/
virtual CodecId getCodecId() const = 0;
/**
*
*/
const char *getCodecName() const;
/**
*
*/
TrackType getTrackType() const;
/**
*
*/
std::string getTrackTypeStr() const;
/**
* track index, track
*/
void setIndex(int index) { _index = index; }
/**
* track index, track
*/
int getIndex() const { return _index < 0 ? (int)getTrackType() : _index; }
private:
int _index = -1;
};
/**
*
*/
class Frame : public toolkit::Buffer, public CodecInfo {
public:
using Ptr = std::shared_ptr<Frame>;
/**
*
*/
virtual uint64_t dts() const = 0;
/**
*
*/
virtual uint64_t pts() const { return dts(); }
/**
* 2640x00 00 00 01,4
* aac前缀则为7个字节
*/
virtual size_t prefixSize() const = 0;
/**
*
*/
virtual bool keyFrame() const = 0;
/**
* sps pps vps
*/
virtual bool configFrame() const = 0;
/**
*
*/
virtual bool cacheAble() const { return true; }
/**
*
* SEI/AUD帧可以丢弃
*
*/
virtual bool dropAble() const { return false; }
/**
*
* sps pps等帧不能解码
*/
virtual bool decodeAble() const {
if (getTrackType() != TrackVideo) {
//非视频帧都可以解码
return true;
}
//默认非sps pps帧都可以解码
return !configFrame();
}
/**
* frame
*/
static Ptr getCacheAbleFrame(const Ptr &frame);
private:
//对象个数统计
toolkit::ObjectStatistic<Frame> _statistic;
};
class FrameImp : public Frame {
public:
using Ptr = std::shared_ptr<FrameImp>;
template <typename C = FrameImp>
static std::shared_ptr<C> create() {
#if 0
static ResourcePool<C> packet_pool;
static onceToken token([]() {
packet_pool.setSize(1024);
});
auto ret = packet_pool.obtain2();
ret->_buffer.clear();
ret->_prefix_size = 0;
ret->_dts = 0;
ret->_pts = 0;
return ret;
#else
return std::shared_ptr<C>(new C());
#endif
}
char *data() const override { return (char *)_buffer.data(); }
size_t size() const override { return _buffer.size(); }
uint64_t dts() const override { return _dts; }
uint64_t pts() const override { return _pts ? _pts : _dts; }
size_t prefixSize() const override { return _prefix_size; }
CodecId getCodecId() const override { return _codec_id; }
bool keyFrame() const override { return false; }
bool configFrame() const override { return false; }
public:
CodecId _codec_id = CodecInvalid;
uint64_t _dts = 0;
uint64_t _pts = 0;
size_t _prefix_size = 0;
toolkit::BufferLikeString _buffer;
private:
//对象个数统计
toolkit::ObjectStatistic<FrameImp> _statistic;
protected:
friend class toolkit::ResourcePool_l<FrameImp>;
FrameImp() = default;
};
// 包装一个指针成不可缓存的frame
class FrameFromPtr : public Frame {
public:
using Ptr = std::shared_ptr<FrameFromPtr>;
FrameFromPtr(CodecId codec_id, char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0, bool is_key = false)
: FrameFromPtr(ptr, size, dts, pts, prefix_size, is_key) {
_codec_id = codec_id;
}
char *data() const override { return _ptr; }
size_t size() const override { return _size; }
uint64_t dts() const override { return _dts; }
uint64_t pts() const override { return _pts ? _pts : dts(); }
size_t prefixSize() const override { return _prefix_size; }
bool cacheAble() const override { return false; }
bool keyFrame() const override { return _is_key; }
bool configFrame() const override { return false; }
CodecId getCodecId() const override {
if (_codec_id == CodecInvalid) {
throw std::invalid_argument("Invalid codec type of FrameFromPtr");
}
return _codec_id;
}
protected:
FrameFromPtr() = default;
FrameFromPtr(char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0, bool is_key = false) {
_ptr = ptr;
_size = size;
_dts = dts;
_pts = pts;
_prefix_size = prefix_size;
_is_key = is_key;
}
protected:
bool _is_key;
char *_ptr;
uint64_t _dts;
uint64_t _pts = 0;
size_t _size;
size_t _prefix_size;
CodecId _codec_id = CodecInvalid;
};
/**
* Frame类中可以有多个帧(AAC)
* ZLMediaKit会先把这种复合帧split成单个帧然后再处理
* Frame
*
*/
template <typename Parent>
class FrameInternalBase : public Parent {
public:
using Ptr = std::shared_ptr<FrameInternalBase>;
FrameInternalBase(Frame::Ptr parent_frame, char *ptr, size_t size, uint64_t dts, uint64_t pts = 0, size_t prefix_size = 0)
: Parent(parent_frame->getCodecId(), ptr, size, dts, pts, prefix_size) {
_parent_frame = std::move(parent_frame);
this->setIndex(_parent_frame->getIndex());
}
bool cacheAble() const override { return _parent_frame->cacheAble(); }
private:
Frame::Ptr _parent_frame;
};
/**
* Frame类中可以有多个帧 0x 00 00 01
* ZLMediaKit会先把这种复合帧split成单个帧然后再处理
* Frame
*
*/
template <typename Parent>
class FrameInternal : public FrameInternalBase<Parent> {
public:
using Ptr = std::shared_ptr<FrameInternal>;
FrameInternal(const Frame::Ptr &parent_frame, char *ptr, size_t size, size_t prefix_size)
: FrameInternalBase<Parent>(parent_frame, ptr, size, parent_frame->dts(), parent_frame->pts(), prefix_size) {}
};
// 管理一个指针生命周期并生产一个frame
class FrameAutoDelete : public FrameFromPtr {
public:
template <typename... ARGS>
FrameAutoDelete(ARGS &&...args) : FrameFromPtr(std::forward<ARGS>(args)...) {}
~FrameAutoDelete() override { delete[] _ptr; };
bool cacheAble() const override { return true; }
};
// 把一个不可缓存的frame声明为可缓存的
template <typename Parent>
class FrameToCache : public Parent {
public:
template<typename ... ARGS>
FrameToCache(ARGS &&...args) : Parent(std::forward<ARGS>(args)...) {};
bool cacheAble() const override {
return true;
}
};
// 该对象的功能是把一个不可缓存的帧转换成可缓存的帧
class FrameCacheAble : public FrameFromPtr {
public:
using Ptr = std::shared_ptr<FrameCacheAble>;
FrameCacheAble(const Frame::Ptr &frame, bool force_key_frame = false, toolkit::Buffer::Ptr buf = nullptr) {
setIndex(frame->getIndex());
if (frame->cacheAble()) {
_ptr = frame->data();
_buffer = frame;
} else if (buf) {
_ptr = frame->data();
_buffer = std::move(buf);
} else {
auto buffer = std::make_shared<toolkit::BufferLikeString>();
buffer->assign(frame->data(), frame->size());
_ptr = buffer->data();
_buffer = std::move(buffer);
}
_size = frame->size();
_dts = frame->dts();
_pts = frame->pts();
_prefix_size = frame->prefixSize();
_codec_id = frame->getCodecId();
_key = force_key_frame ? true : frame->keyFrame();
_config = frame->configFrame();
_drop_able = frame->dropAble();
_decode_able = frame->decodeAble();
}
/**
*
*/
bool cacheAble() const override { return true; }
bool keyFrame() const override { return _key; }
bool configFrame() const override { return _config; }
bool dropAble() const override { return _drop_able; }
bool decodeAble() const override { return _decode_able; }
private:
bool _key;
bool _config;
bool _drop_able;
bool _decode_able;
toolkit::Buffer::Ptr _buffer;
};
//该类实现frame级别的时间戳覆盖
class FrameStamp : public Frame {
public:
using Ptr = std::shared_ptr<FrameStamp>;
FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp);
~FrameStamp() override {}
uint64_t dts() const override { return (uint64_t)_dts; }
uint64_t pts() const override { return (uint64_t)_pts; }
size_t prefixSize() const override { return _frame->prefixSize(); }
bool keyFrame() const override { return _frame->keyFrame(); }
bool configFrame() const override { return _frame->configFrame(); }
bool cacheAble() const override { return _frame->cacheAble(); }
bool dropAble() const override { return _frame->dropAble(); }
bool decodeAble() const override { return _frame->decodeAble(); }
char *data() const override { return _frame->data(); }
size_t size() const override { return _frame->size(); }
CodecId getCodecId() const override { return _frame->getCodecId(); }
private:
int64_t _dts;
int64_t _pts;
Frame::Ptr _frame;
};
/**
* Buffer对象转换成可缓存的Frame对象
*/
template <typename Parent>
class FrameFromBuffer : public Parent {
public:
/**
* frame
* @param buf
* @param dts
* @param pts
* @param prefix
* @param offset buffer有效数据偏移量
*/
FrameFromBuffer(toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0)
: Parent(buf->data() + offset, buf->size() - offset, dts, pts, prefix) {
_buf = std::move(buf);
}
/**
* frame
* @param buf
* @param dts
* @param pts
* @param prefix
* @param offset buffer有效数据偏移量
* @param codec
*/
FrameFromBuffer(CodecId codec, toolkit::Buffer::Ptr buf, uint64_t dts, uint64_t pts, size_t prefix = 0, size_t offset = 0)
: Parent(codec, buf->data() + offset, buf->size() - offset, dts, pts, prefix) {
_buf = std::move(buf);
}
/**
*
*/
bool cacheAble() const override { return true; }
private:
toolkit::Buffer::Ptr _buf;
};
/**
* frame
*/
class FrameMerger {
public:
using onOutput = std::function<void(uint64_t dts, uint64_t pts, const toolkit::Buffer::Ptr &buffer, bool have_key_frame)>;
using Ptr = std::shared_ptr<FrameMerger>;
enum {
none = 0,
h264_prefix,
mp4_nal_size,
};
FrameMerger(int type);
/**
* FrameMerger::inputFrame传入的onOutput回调
*
*/
void flush();
void clear();
bool inputFrame(const Frame::Ptr &frame, onOutput cb, toolkit::BufferLikeString *buffer = nullptr);
private:
bool willFlush(const Frame::Ptr &frame) const;
void doMerge(toolkit::BufferLikeString &buffer, const Frame::Ptr &frame) const;
private:
int _type;
bool _have_decode_able_frame = false;
onOutput _cb;
toolkit::List<Frame::Ptr> _frame_cache;
};
/**
*
*/
class FrameWriterInterface {
public:
using Ptr = std::shared_ptr<FrameWriterInterface>;
virtual ~FrameWriterInterface() = default;
/**
*
*/
virtual bool inputFrame(const Frame::Ptr &frame) = 0;
/**
* frame缓存
*/
virtual void flush() {};
};
/**
*
*/
class FrameDispatcher : public FrameWriterInterface {
public:
using Ptr = std::shared_ptr<FrameDispatcher>;
/**
*
*/
FrameWriterInterface* addDelegate(FrameWriterInterface::Ptr delegate) {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _delegates.emplace(delegate.get(), std::move(delegate)).first->second.get();
}
FrameWriterInterface* addDelegate(std::function<bool(const Frame::Ptr &frame)> cb);
/**
*
*/
void delDelegate(FrameWriterInterface *ptr) {
std::lock_guard<std::recursive_mutex> lck(_mtx);
_delegates.erase(ptr);
}
/**
*
*/
bool inputFrame(const Frame::Ptr &frame) override {
std::lock_guard<std::recursive_mutex> lck(_mtx);
doStatistics(frame);
bool ret = false;
for (auto &pr : _delegates) {
if (pr.second->inputFrame(frame)) {
ret = true;
}
}
return ret;
}
/**
*
*/
size_t size() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _delegates.size();
}
void clear() {
std::lock_guard<std::recursive_mutex> lck(_mtx);
_delegates.clear();
}
/**
*
*/
uint64_t getVideoKeyFrames() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _video_key_frames;
}
/**
*
*/
uint64_t getFrames() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _frames;
}
size_t getVideoGopSize() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _gop_size;
}
size_t getVideoGopInterval() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _gop_interval_ms;
}
int64_t getDuration() const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _stamp.getRelativeStamp();
}
private:
void doStatistics(const Frame::Ptr &frame) {
if (!frame->configFrame() && !frame->dropAble()) {
// 忽略配置帧与可丢弃的帧
++_frames;
int64_t out;
_stamp.revise(frame->dts(), frame->pts(), out, out);
if (frame->keyFrame() && frame->getTrackType() == TrackVideo) {
// 遇视频关键帧时统计
++_video_key_frames;
_gop_size = _frames - _last_frames;
_gop_interval_ms = _ticker.elapsedTime();
_last_frames = _frames;
_ticker.resetTime();
}
}
}
private:
toolkit::Ticker _ticker;
size_t _gop_interval_ms = 0;
size_t _gop_size = 0;
uint64_t _last_frames = 0;
uint64_t _frames = 0;
uint64_t _video_key_frames = 0;
Stamp _stamp;
mutable std::recursive_mutex _mtx;
std::map<void *, FrameWriterInterface::Ptr> _delegates;
};
} // namespace mediakit
#endif // ZLMEDIAKIT_FRAME_H

View File

@ -0,0 +1,261 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_TRACK_H
#define ZLMEDIAKIT_TRACK_H
#include <memory>
#include <string>
#include "Frame.h"
#include "Rtsp/Rtsp.h"
namespace mediakit{
/**
*
*/
class Track : public FrameDispatcher, public CodecInfo {
public:
using Ptr = std::shared_ptr<Track>;
/**
*
*/
Track() = default;
/**
*
*
*/
Track(const Track &that) {
_bit_rate = that._bit_rate;
setIndex(that.getIndex());
}
/**
* sps pps等信息
*/
virtual bool ready() const = 0;
/**
*
*
*
*/
virtual Track::Ptr clone() const = 0;
/**
* track信息sps/pps解析
*/
virtual bool update() { return false; }
/**
* sdp
* @return sdp对象
*/
virtual Sdp::Ptr getSdp(uint8_t payload_type) const = 0;
/**
* extra data, rtmp/mp4生成
*/
virtual toolkit::Buffer::Ptr getExtraData() const { return nullptr; }
/**
* extra data
*/
virtual void setExtraData(const uint8_t *data, size_t size) {}
/**
*
* @return
*/
virtual int getBitRate() const { return _bit_rate; }
/**
*
* @param bit_rate
*/
virtual void setBitRate(int bit_rate) { _bit_rate = bit_rate; }
private:
int _bit_rate = 0;
};
/**
* Track类fps信息
*/
class VideoTrack : public Track {
public:
using Ptr = std::shared_ptr<VideoTrack>;
/**
*
*/
virtual int getVideoHeight() const { return 0; }
/**
*
*/
virtual int getVideoWidth() const { return 0; }
/**
* fps
*/
virtual float getVideoFps() const { return 0; }
};
class VideoTrackImp : public VideoTrack {
public:
using Ptr = std::shared_ptr<VideoTrackImp>;
/**
*
* @param codec_id
* @param width
* @param height
* @param fps
*/
VideoTrackImp(CodecId codec_id, int width, int height, int fps) {
_codec_id = codec_id;
_width = width;
_height = height;
_fps = fps;
}
int getVideoWidth() const override { return _width; }
int getVideoHeight() const override { return _height; }
float getVideoFps() const override { return _fps; }
bool ready() const override { return true; }
Track::Ptr clone() const override { return std::make_shared<VideoTrackImp>(*this); }
Sdp::Ptr getSdp(uint8_t payload_type) const override { return nullptr; }
CodecId getCodecId() const override { return _codec_id; }
private:
CodecId _codec_id;
int _width = 0;
int _height = 0;
float _fps = 0;
};
/**
* Track派生类
*/
class AudioTrack : public Track {
public:
using Ptr = std::shared_ptr<AudioTrack>;
/**
*
*/
virtual int getAudioSampleRate() const {return 0;};
/**
* 168
*/
virtual int getAudioSampleBit() const {return 0;};
/**
*
*/
virtual int getAudioChannel() const {return 0;};
};
class AudioTrackImp : public AudioTrack{
public:
using Ptr = std::shared_ptr<AudioTrackImp>;
/**
*
* @param codecId
* @param sample_rate (HZ)
* @param channels
* @param sample_bit 16
*/
AudioTrackImp(CodecId codecId, int sample_rate, int channels, int sample_bit){
_codecid = codecId;
_sample_rate = sample_rate;
_channels = channels;
_sample_bit = sample_bit;
}
/**
*
*/
CodecId getCodecId() const override{
return _codecid;
}
/**
*
*/
bool ready() const override {
return true;
}
/**
*
*/
int getAudioSampleRate() const override{
return _sample_rate;
}
/**
* 168
*/
int getAudioSampleBit() const override{
return _sample_bit;
}
/**
*
*/
int getAudioChannel() const override{
return _channels;
}
Track::Ptr clone() const override { return std::make_shared<AudioTrackImp>(*this); }
Sdp::Ptr getSdp(uint8_t payload_type) const override { return nullptr; }
private:
CodecId _codecid;
int _sample_rate;
int _channels;
int _sample_bit;
};
class TrackSource {
public:
virtual ~TrackSource() = default;
/**
* Track
* @param trackReady Track
*/
virtual std::vector<Track::Ptr> getTracks(bool trackReady = true) const = 0;
/**
* Track
* @param type track类型
* @param trackReady Track
*/
Track::Ptr getTrack(TrackType type , bool trackReady = true) const {
auto tracks = getTracks(trackReady);
for(auto &track : tracks){
if(track->getTrackType() == type){
return track;
}
}
return nullptr;
}
};
}//namespace mediakit
#endif //ZLMEDIAKIT_TRACK_H

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCE_H
#define ZLMEDIAKIT_FMP4MEDIASOURCE_H
#include "Common/MediaSource.h"
#include "Common/PacketCache.h"
#include "Util/RingBuffer.h"
#define FMP4_GOP_SIZE 512
namespace mediakit {
//FMP4直播数据包
class FMP4Packet : public toolkit::BufferString{
public:
using Ptr = std::shared_ptr<FMP4Packet>;
template<typename ...ARGS>
FMP4Packet(ARGS && ...args) : toolkit::BufferString(std::forward<ARGS>(args)...) {};
public:
uint64_t time_stamp = 0;
};
//FMP4直播源
class FMP4MediaSource final : public MediaSource, public toolkit::RingDelegate<FMP4Packet::Ptr>, private PacketCache<FMP4Packet>{
public:
using Ptr = std::shared_ptr<FMP4MediaSource>;
using RingDataType = std::shared_ptr<toolkit::List<FMP4Packet::Ptr> >;
using RingType = toolkit::RingBuffer<RingDataType>;
FMP4MediaSource(const MediaTuple& tuple,
int ring_size = FMP4_GOP_SIZE) : MediaSource(FMP4_SCHEMA, tuple), _ring_size(ring_size) {}
~FMP4MediaSource() override { flush(); }
/**
*
*/
const RingType::Ptr &getRing() const {
return _ring;
}
void getPlayerList(const std::function<void(const std::list<toolkit::Any> &info_list)> &cb,
const std::function<toolkit::Any(toolkit::Any &&info)> &on_change) override {
_ring->getInfoList(cb, on_change);
}
/**
* fmp4 init segment
*/
const std::string &getInitSegment() const{
return _init_segment;
}
/**
* fmp4 init segment
* @param str init segment
*/
void setInitSegment(std::string str) {
_init_segment = std::move(str);
createRing();
}
/**
*
*/
int readerCount() override {
return _ring ? _ring->readerCount() : 0;
}
/**
* FMP4包
* @param packet FMP4包
* @param key
*/
void onWrite(FMP4Packet::Ptr packet, bool key) override {
if (!_ring) {
createRing();
}
if (key) {
_have_video = true;
}
_speed[TrackVideo] += packet->size();
auto stamp = packet->time_stamp;
PacketCache<FMP4Packet>::inputPacket(stamp, true, std::move(packet), key);
}
/**
* GOP缓存
*/
void clearCache() override {
PacketCache<FMP4Packet>::clearCache();
_ring->clearCache();
}
private:
void createRing(){
std::weak_ptr<FMP4MediaSource> weak_self = std::static_pointer_cast<FMP4MediaSource>(shared_from_this());
_ring = std::make_shared<RingType>(_ring_size, [weak_self](int size) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->onReaderChanged(size);
});
if (!_init_segment.empty()) {
regist();
}
}
/**
*
* @param packet_list
* @param key_pos
*/
void onFlush(std::shared_ptr<toolkit::List<FMP4Packet::Ptr> > packet_list, bool key_pos) override {
//如果不存在视频那么就没有存在GOP缓存的意义所以确保一直清空GOP缓存
_ring->write(std::move(packet_list), _have_video ? key_pos : true);
}
private:
bool _have_video = false;
int _ring_size;
std::string _init_segment;
RingType::Ptr _ring;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCE_H

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#define ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H
#include "FMP4MediaSource.h"
#include "Record/MP4Muxer.h"
namespace mediakit {
class FMP4MediaSourceMuxer final : public MP4MuxerMemory, public MediaSourceEventInterceptor,
public std::enable_shared_from_this<FMP4MediaSourceMuxer> {
public:
using Ptr = std::shared_ptr<FMP4MediaSourceMuxer>;
FMP4MediaSourceMuxer(const MediaTuple& tuple, const ProtocolOption &option) {
_option = option;
_media_src = std::make_shared<FMP4MediaSource>(tuple);
}
~FMP4MediaSourceMuxer() override { MP4MuxerMemory::flush(); };
void setListener(const std::weak_ptr<MediaSourceEvent> &listener){
setDelegate(listener);
_media_src->setListener(shared_from_this());
}
int readerCount() const{
return _media_src->readerCount();
}
void onReaderChanged(MediaSource &sender, int size) override {
_enabled = _option.fmp4_demand ? size : true;
if (!size && _option.fmp4_demand) {
_clear_cache = true;
}
MediaSourceEventInterceptor::onReaderChanged(sender, size);
}
bool inputFrame(const Frame::Ptr &frame) override {
if (_clear_cache && _option.fmp4_demand) {
_clear_cache = false;
_media_src->clearCache();
}
if (_enabled || !_option.fmp4_demand) {
return MP4MuxerMemory::inputFrame(frame);
}
return false;
}
bool isEnabled() {
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _option.fmp4_demand ? (_clear_cache ? true : _enabled) : true;
}
void addTrackCompleted() override {
MP4MuxerMemory::addTrackCompleted();
_media_src->setInitSegment(getInitSegment());
}
protected:
void onSegmentData(std::string string, uint64_t stamp, bool key_frame) override {
if (string.empty()) {
return;
}
FMP4Packet::Ptr packet = std::make_shared<FMP4Packet>(std::move(string));
packet->time_stamp = stamp;
_media_src->onWrite(std::move(packet), key_frame);
}
private:
bool _enabled = true;
bool _clear_cache = false;
ProtocolOption _option;
FMP4MediaSource::Ptr _media_src;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FMP4MEDIASOURCEMUXER_H

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HTTP_HLSPARSER_H
#define HTTP_HLSPARSER_H
#include <string>
#include <list>
#include <map>
namespace mediakit {
typedef struct{
//url地址
std::string url;
//ts切片长度
float duration;
//////内嵌m3u8//////
//节目id
int program_id;
//带宽
int bandwidth;
//宽度
int width;
//高度
int height;
} ts_segment;
class HlsParser {
public:
bool parse(const std::string &http_url,const std::string &m3u8);
/**
* #EXTM3U字段m3u8文件
*/
bool isM3u8() const;
/**
* #EXT-X-ALLOW-CACHE值cache
*/
bool allowCache() const;
/**
* #EXT-X-ENDLIST字段
*/
bool isLive() const ;
/**
* #EXT-X-VERSION值
*/
int getVersion() const;
/**
* #EXT-X-TARGETDURATION字段值
*/
int getTargetDur() const;
/**
* #EXT-X-MEDIA-SEQUENCE字段值m3u8序号
*/
int64_t getSequence() const;
/**
* m3u8
*/
bool isM3u8Inner() const;
/**
*
*/
float getTotalDuration() const;
protected:
/**
* m3u8文件回调
* @param is_m3u8_inner m3u8文件中是否包含多个hls地址
* @param sequence ts序号
* @param ts_list ts地址列表
* @return false时HlsParser::parse返回false
*/
virtual bool onParsed(bool is_m3u8_inner, int64_t sequence, const std::map<int, ts_segment> &ts_list) = 0;
private:
bool _is_m3u8 = false;
bool _allow_cache = false;
bool _is_live = true;
int _version = 0;
int _target_dur = 0;
float _total_dur = 0;
int64_t _sequence = 0;
//每部是否有m3u8
bool _is_m3u8_inner = false;
};
}//namespace mediakit
#endif //HTTP_HLSPARSER_H

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HTTP_HLSPLAYER_H
#define HTTP_HLSPLAYER_H
#include "Player/PlayerBase.h"
#include "HttpTSPlayer.h"
#include "HlsParser.h"
#include "Rtp/TSDecoder.h"
#define MIN_TIMEOUT_MULTIPLE 2
#define MAX_TIMEOUT_MULTIPLE 5
#define MAX_TRY_FETCH_INDEX_TIMES 5
#define MAX_TS_DOWNLOAD_FAILED_COUNT 10
namespace mediakit {
class HlsDemuxer : public MediaSinkInterface , public TrackSource, public std::enable_shared_from_this<HlsDemuxer> {
public:
~HlsDemuxer() override { _timer = nullptr; }
void start(const toolkit::EventPoller::Ptr &poller, TrackListener *listener);
bool inputFrame(const Frame::Ptr &frame) override;
bool addTrack(const Track::Ptr &track) override { return _delegate.addTrack(track); }
void addTrackCompleted() override { _delegate.addTrackCompleted(); }
void resetTracks() override { ((MediaSink &)_delegate).resetTracks(); }
std::vector<Track::Ptr> getTracks(bool ready = true) const override { return _delegate.getTracks(ready); }
void pushTask(std::function<void()> task);
private:
void onTick();
int64_t getBufferMS();
int64_t getPlayPosition();
void setPlayPosition(int64_t pos);
private:
int64_t _ticker_offset = 0;
toolkit::Ticker _ticker;
toolkit::Timer::Ptr _timer;
MediaSinkDelegate _delegate;
std::deque<std::pair<int64_t, std::function<void()> > > _frame_cache;
};
class HlsPlayer : public HttpClientImp , public PlayerBase , public HlsParser{
public:
HlsPlayer(const toolkit::EventPoller::Ptr &poller);
/**
*
* start play
*/
void play(const std::string &url) override;
/**
*
* stop play
*/
void teardown() override;
protected:
/**
* ts包
* Received ts package
* @param data ts数据负载 ts data payload
* @param len ts包长度 ts package length
*/
virtual void onPacket(const char *data, size_t len) = 0;
private:
bool onParsed(bool is_m3u8_inner, int64_t sequence, const map<int, ts_segment> &ts_map) override;
void onResponseHeader(const std::string &status, const HttpHeader &headers) override;
void onResponseBody(const char *buf, size_t size) override;
void onResponseCompleted(const toolkit::SockException &e) override;
bool onRedirectUrl(const std::string &url, bool temporary) override;
private:
void playDelay(float delay_sec = 0);
float delaySecond();
void fetchSegment();
void teardown_l(const toolkit::SockException &ex);
void fetchIndexFile();
private:
struct UrlComp {
// url忽略后面的参数
// Ignore the parameters after the url?
bool operator()(const std::string& __x, const std::string& __y) const {
return toolkit::split(__x,"?")[0] < toolkit::split(__y,"?")[0];
}
};
private:
bool _play_result = false;
int64_t _last_sequence = -1;
std::string _m3u8;
std::string _play_url;
toolkit::Timer::Ptr _timer;
toolkit::Timer::Ptr _timer_ts;
toolkit::Ticker _wait_index_update_ticker;
std::list<ts_segment> _ts_list;
std::list<std::string> _ts_url_sort;
std::set<std::string, UrlComp> _ts_url_cache;
HttpTSPlayer::Ptr _http_ts_player;
int _timeout_multiple = MIN_TIMEOUT_MULTIPLE;
int _try_fetch_index_times = 0;
int _ts_download_failed_count = 0;
};
class HlsPlayerImp : public PlayerImp<HlsPlayer, PlayerBase>, private TrackListener {
public:
using Ptr = std::shared_ptr<HlsPlayerImp>;
HlsPlayerImp(const toolkit::EventPoller::Ptr &poller = nullptr);
private:
//// HlsPlayer override////
void onPacket(const char *data, size_t len) override;
private:
//// PlayerBase override////
void onPlayResult(const toolkit::SockException &ex) override;
std::vector<Track::Ptr> getTracks(bool ready = true) const override;
void onShutdown(const toolkit::SockException &ex) override;
private:
//// TrackListener override////
bool addTrack(const Track::Ptr &track) override { return true; };
void addTrackCompleted() override;
private:
DecoderImp::Ptr _decoder;
MediaSinkInterface::Ptr _demuxer;
};
}//namespace mediakit
#endif //HTTP_HLSPLAYER_H

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_FILEREADER_H
#define ZLMEDIAKIT_FILEREADER_H
#include <stdlib.h>
#include <memory>
#include "Network/Buffer.h"
#include "Util/ResourcePool.h"
#include "Util/logger.h"
#include "Thread/WorkThreadPool.h"
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b) )
#endif //MIN
namespace mediakit {
/**
* http content部分基类定义
*/
class HttpBody : public std::enable_shared_from_this<HttpBody>{
public:
using Ptr = std::shared_ptr<HttpBody>;
virtual ~HttpBody() = default;
/**
* -1, content-length
*/
virtual int64_t remainSize() { return 0;};
/**
* size
* @param size
* @return ,nullptr
*/
virtual toolkit::Buffer::Ptr readData(size_t size) { return nullptr;};
/**
* size
* @param size
* @param cb
*/
virtual void readDataAsync(size_t size,const std::function<void(const toolkit::Buffer::Ptr &buf)> &cb){
//由于unix和linux是通过mmap的方式读取文件所以把读文件操作放在后台线程并不能提高性能
//反而会由于频繁的线程切换导致性能降低以及延时增加,所以我们默认同步获取文件内容
//(其实并没有读,拷贝文件数据时在内核态完成文件读)
cb(readData(size));
}
/**
* 使sendfile优化文件发送
* @param fd socket fd
* @return 0
*/
virtual int sendFile(int fd) {
return -1;
}
};
/**
* std::string类型的content
*/
class HttpStringBody : public HttpBody{
public:
using Ptr = std::shared_ptr<HttpStringBody>;
HttpStringBody(std::string str);
int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override ;
private:
size_t _offset = 0;
mutable std::string _str;
};
/**
* Buffer类型的content
*/
class HttpBufferBody : public HttpBody{
public:
using Ptr = std::shared_ptr<HttpBufferBody>;
HttpBufferBody(toolkit::Buffer::Ptr buffer);
int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override;
private:
toolkit::Buffer::Ptr _buffer;
};
/**
* content
*/
class HttpFileBody : public HttpBody {
public:
using Ptr = std::shared_ptr<HttpFileBody>;
/**
*
* @param file_path
* @param use_mmap 使mmap方式访问文件
*/
HttpFileBody(const std::string &file_path, bool use_mmap = true);
/**
*
* @param offset
* @param max_size
*/
void setRange(uint64_t offset, uint64_t max_size);
int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override;
int sendFile(int fd) override;
private:
int64_t _read_to = 0;
uint64_t _file_offset = 0;
std::shared_ptr<FILE> _fp;
std::shared_ptr<char> _map_addr;
toolkit::ResourcePool<toolkit::BufferRaw> _pool;
};
class HttpArgs;
/**
* http MultiForm http content
*/
class HttpMultiFormBody : public HttpBody {
public:
using Ptr = std::shared_ptr<HttpMultiFormBody>;
/**
*
* @param args http提交参数列表
* @param filePath
* @param boundary boundary字符串
*/
HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY");
int64_t remainSize() override ;
toolkit::Buffer::Ptr readData(size_t size) override;
public:
static std::string multiFormBodyPrefix(const HttpArgs &args,const std::string &boundary,const std::string &fileName);
static std::string multiFormBodySuffix(const std::string &boundary);
static std::string multiFormContentType(const std::string &boundary);
private:
uint64_t _offset = 0;
int64_t _totalSize;
std::string _bodyPrefix;
std::string _bodySuffix;
HttpFileBody::Ptr _fileBody;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_FILEREADER_H

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H
#define ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H
#include <functional>
#include "HttpRequestSplitter.h"
namespace mediakit{
class HttpChunkedSplitter : public HttpRequestSplitter {
public:
/**
* len == 0
*/
using onChunkData = std::function<void(const char *data, size_t len)>;
HttpChunkedSplitter(const onChunkData &cb) { _onChunkData = cb; };
~HttpChunkedSplitter() override { _onChunkData = nullptr; };
protected:
ssize_t onRecvHeader(const char *data,size_t len) override;
void onRecvContent(const char *data,size_t len) override;
const char *onSearchPacketTail(const char *data,size_t len) override;
protected:
virtual void onRecvChunk(const char *data,size_t len){
if(_onChunkData){
_onChunkData(data,len);
}
};
private:
onChunkData _onChunkData;
};
}//namespace mediakit
#endif //ZLMEDIAKIT_HTTPCHUNKEDSPLITTER_H

View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef Http_HttpClient_h
#define Http_HttpClient_h
#include <stdio.h>
#include <string.h>
#include <functional>
#include <memory>
#include "Util/util.h"
#include "Util/mini.h"
#include "Network/TcpClient.h"
#include "Common/Parser.h"
#include "HttpRequestSplitter.h"
#include "HttpCookie.h"
#include "HttpChunkedSplitter.h"
#include "Common/strCoding.h"
#include "HttpBody.h"
namespace mediakit {
class HttpArgs : public std::map<std::string, toolkit::variant, StrCaseCompare> {
public:
std::string make() const {
std::string ret;
for (auto &pr : *this) {
ret.append(pr.first);
ret.append("=");
ret.append(strCoding::UrlEncodeComponent(pr.second));
ret.append("&");
}
if (ret.size()) {
ret.pop_back();
}
return ret;
}
};
class HttpClient : public toolkit::TcpClient, public HttpRequestSplitter {
public:
using HttpHeader = StrCaseMap;
using Ptr = std::shared_ptr<HttpClient>;
/**
* http[s]
* @param url url
*/
virtual void sendRequest(const std::string &url);
/**
*
*/
virtual void clear();
/**
* http方法
* @param method GET/POST等
*/
void setMethod(std::string method);
/**
* http头
* @param header
*/
void setHeader(HttpHeader header);
HttpClient &addHeader(std::string key, std::string val, bool force = false);
/**
* http content
* @param body http content
*/
void setBody(std::string body);
/**
* http content
* @param body http content
*/
void setBody(HttpBody::Ptr body);
/**
*
*/
const Parser &response() const;
/**
* header声明的body大小
*/
ssize_t responseBodyTotalSize() const;
/**
* body的大小
*/
size_t responseBodySize() const;
/**
* url
*/
const std::string &getUrl() const;
/**
*
*/
bool waitResponse() const;
/**
* https
*/
bool isHttps() const;
/**
* header完毕的延时10
* 0
*/
void setHeaderTimeout(size_t timeout_ms);
/**
* body数据超时时间, 5
* body回复的超时问题
* 0
*/
void setBodyTimeout(size_t timeout_ms);
/**
* , 0
* 0HeaderTimeout和BodyTimeout无效
*/
void setCompleteTimeout(size_t timeout_ms);
/**
* http代理url
*/
void setProxyUrl(std::string proxy_url);
/**
* ,
* If the reuse connection fails, whether to allow the request to be resent
* @param allow true: / true: allow the request to be resent
*/
void setAllowResendRequest(bool allow);
protected:
/**
* http回复头
* @param status :200 OK
* @param headers http头
*/
virtual void onResponseHeader(const std::string &status, const HttpHeader &headers) = 0;
/**
* http conten数据
* @param buf
* @param size
*/
virtual void onResponseBody(const char *buf, size_t size) = 0;
/**
* http回复完毕,
*/
virtual void onResponseCompleted(const toolkit::SockException &ex) = 0;
/**
*
* @param url url
* @param temporary
* @return
*/
virtual bool onRedirectUrl(const std::string &url, bool temporary) { return true; };
protected:
//// HttpRequestSplitter override ////
ssize_t onRecvHeader(const char *data, size_t len) override;
void onRecvContent(const char *data, size_t len) override;
//// TcpClient override ////
void onConnect(const toolkit::SockException &ex) override;
void onRecv(const toolkit::Buffer::Ptr &pBuf) override;
void onError(const toolkit::SockException &ex) override;
void onFlush() override;
void onManager() override;
void clearResponse();
bool checkProxyConnected(const char *data, size_t len);
bool isUsedProxy() const;
bool isProxyConnected() const;
private:
void onResponseCompleted_l(const toolkit::SockException &ex);
void onConnect_l(const toolkit::SockException &ex);
void checkCookie(HttpHeader &headers);
private:
//for http response
bool _complete = false;
bool _header_recved = false;
bool _http_persistent = true;
bool _allow_resend_request = false;
size_t _recved_body_size;
ssize_t _total_body_size;
Parser _parser;
std::shared_ptr<HttpChunkedSplitter> _chunked_splitter;
//for request args
bool _is_https;
std::string _url;
HttpHeader _user_set_header;
HttpBody::Ptr _body;
std::string _method;
std::string _last_host;
//for this request
std::string _path;
HttpHeader _header;
//for timeout
size_t _wait_header_ms = 10 * 1000;
size_t _wait_body_ms = 10 * 1000;
size_t _wait_complete_ms = 0;
toolkit::Ticker _wait_header;
toolkit::Ticker _wait_body;
toolkit::Ticker _wait_complete;
bool _used_proxy = false;
bool _proxy_connected = false;
uint16_t _proxy_port;
std::string _proxy_url;
std::string _proxy_host;
std::string _proxy_auth;
};
} /* namespace mediakit */
#endif /* Http_HttpClient_h */

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_HTTP_HTTPCLIENTIMP_H_
#define SRC_HTTP_HTTPCLIENTIMP_H_
#include "HttpClient.h"
#include "Util/SSLBox.h"
namespace mediakit {
class HttpClientImp : public toolkit::TcpClientWithSSL<HttpClient> {
public:
using Ptr = std::shared_ptr<HttpClientImp>;
protected:
void onConnect(const toolkit::SockException &ex) override;
ssize_t onRecvHeader(const char *data, size_t len) override;
};
} /* namespace mediakit */
#endif /* SRC_HTTP_HTTPCLIENTIMP_H_ */

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_HTTPCONST_H
#define ZLMEDIAKIT_HTTPCONST_H
#include <string>
namespace mediakit{
class HttpConst {
public:
HttpConst() = delete;
~HttpConst() = delete;
/**
* http错误代码获取字符说明
* @param status 404
* @return Not Found
*/
static const char *getHttpStatusMessage(int status);
/**
* http mime
* @param name html
* @return mime值text/html
*/
static const std::string &getHttpContentType(const char *name);
};
}//mediakit
#endif //ZLMEDIAKIT_HTTPCONST_H

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_HTTPCOOKIE_H
#define ZLMEDIAKIT_HTTPCOOKIE_H
#include <string>
#include <memory>
#include <vector>
#include <map>
#include <unordered_map>
#include <mutex>
namespace mediakit {
/**
* http客户端cookie对象
*/
class HttpCookie {
public:
using Ptr = std::shared_ptr<HttpCookie>;
friend class HttpCookieStorage;
void setPath(const std::string &path);
void setHost(const std::string &host);
void setExpires(const std::string &expires,const std::string &server_date);
void setKeyVal(const std::string &key,const std::string &val);
operator bool ();
const std::string &getKey() const ;
const std::string &getVal() const ;
private:
std::string _host;
std::string _path = "/";
std::string _key;
std::string _val;
time_t _expire = 0;
};
/**
* http客户端cookie全局保存器
*/
class HttpCookieStorage{
public:
static HttpCookieStorage &Instance();
void set(const HttpCookie::Ptr &cookie);
std::vector<HttpCookie::Ptr> get(const std::string &host,const std::string &path);
private:
HttpCookieStorage() = default;
private:
std::unordered_map<std::string/*host*/, std::map<std::string/*cookie path*/,std::map<std::string/*cookie_key*/, HttpCookie::Ptr> > > _all_cookie;
std::mutex _mtx_cookie;
};
} /* namespace mediakit */
#endif //ZLMEDIAKIT_HTTPCOOKIE_H

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_HTTP_COOKIEMANAGER_H
#define SRC_HTTP_COOKIEMANAGER_H
#include "Common/Parser.h"
#include "Network/Socket.h"
#include "Util/TimeTicker.h"
#include "Util/mini.h"
#include "Util/util.h"
#include <memory>
#include <unordered_map>
#define COOKIE_DEFAULT_LIFE (7 * 24 * 60 * 60)
namespace mediakit {
class HttpCookieManager;
/**
* cookie对象cookie的一些相关属性
*/
class HttpServerCookie : public toolkit::noncopyable {
public:
using Ptr = std::shared_ptr<HttpServerCookie>;
/**
* cookie
* @param manager cookie管理者对象
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @param cookie cookie随机字符串
* @param max_elapsed
*/
HttpServerCookie(
const std::shared_ptr<HttpCookieManager> &manager, const std::string &cookie_name, const std::string &uid,
const std::string &cookie, uint64_t max_elapsed);
~HttpServerCookie();
/**
* uid
* @return uid
*/
const std::string &getUid() const;
/**
* http中Set-Cookie字段的值
* @param cookie_name cookie的名称 MY_SESSION
* @param path http访问路径
* @return MY_SESSION=XXXXXX;expires=Wed, Jun 12 2019 06:30:48 GMT;path=/index/files/
*/
std::string getCookie(const std::string &path) const;
/**
* cookie随机字符串
* @return cookie随机字符串
*/
const std::string &getCookie() const;
/**
* cookie名
* @return
*/
const std::string &getCookieName() const;
/**
* cookie的过期时间cookie不失效
*/
void updateTime();
/**
* cookie是否过期
* @return
*/
bool isExpired();
/**
*
*/
void setAttach(toolkit::Any attach);
/*
*
*/
template <class T>
T& getAttach() {
return _attach.get<T>();
}
private:
std::string cookieExpireTime() const;
private:
std::string _uid;
std::string _cookie_name;
std::string _cookie_uuid;
uint64_t _max_elapsed;
toolkit::Ticker _ticker;
toolkit::Any _attach;
std::weak_ptr<HttpCookieManager> _manager;
};
/**
* cookie随机字符串生成器
*/
class RandStrGenerator {
public:
/**
*
* @return
*/
std::string obtain();
/**
*
* @param str
*/
void release(const std::string &str);
private:
std::string obtain_l();
private:
//碰撞库
std::unordered_set<std::string> _obtained;
//增长index防止碰撞用
int _index = 0;
};
/**
* cookie管理器cookie的生成以及过期管理
*
*/
class HttpCookieManager : public std::enable_shared_from_this<HttpCookieManager> {
public:
friend class HttpServerCookie;
using Ptr = std::shared_ptr<HttpCookieManager>;
~HttpCookieManager();
/**
*
*/
static HttpCookieManager &Instance();
/**
* cookie
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @param max_client
* @param max_elapsed cookie过期时间
* @return cookie对象
*/
HttpServerCookie::Ptr addCookie(
const std::string &cookie_name, const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE,
toolkit::Any = toolkit::Any{},
int max_client = 1);
/**
* cookie随机字符串查找cookie对象
* @param cookie_name cookie名MY_SESSION
* @param cookie cookie随机字符串
* @return cookie对象nullptr
*/
HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const std::string &cookie);
/**
* http头中获取cookie对象
* @param cookie_name cookie名MY_SESSION
* @param http_header http头
* @return cookie对象
*/
HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const StrCaseMap &http_header);
/**
* uid获取cookie
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @return cookie对象
*/
HttpServerCookie::Ptr getCookieByUid(const std::string &cookie_name, const std::string &uid);
/**
* cookie使
* @param cookie cookie对象nullptr
* @return
*/
bool delCookie(const HttpServerCookie::Ptr &cookie);
private:
HttpCookieManager();
void onManager();
/**
* cookie对象时触发cookie
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @param cookie cookie随机字符串
*/
void onAddCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie);
/**
* cookie对象时触发
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @param cookie cookie随机字符串
*/
void onDelCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie);
/**
* cookie
* @param cookie_name cookie名MY_SESSION
* @param uid id
* @param max_client
* @return cookie随机字符串
*/
std::string getOldestCookie(const std::string &cookie_name, const std::string &uid, int max_client = 1);
/**
* cookie
* @param cookie_name cookie名MY_SESSION
* @param cookie cookie随机字符串
* @return true
*/
bool delCookie(const std::string &cookie_name, const std::string &cookie);
private:
std::unordered_map<
std::string /*cookie_name*/, std::unordered_map<std::string /*cookie*/, HttpServerCookie::Ptr /*cookie_data*/>>
_map_cookie;
std::unordered_map<
std::string /*cookie_name*/,
std::unordered_map<std::string /*uid*/, std::map<uint64_t /*cookie time stamp*/, std::string /*cookie*/>>>
_map_uid_to_cookie;
std::recursive_mutex _mtx_cookie;
toolkit::Timer::Ptr _timer;
RandStrGenerator _generator;
};
} // namespace mediakit
#endif // SRC_HTTP_COOKIEMANAGER_H

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_HTTP_HTTPDOWNLOADER_H_
#define SRC_HTTP_HTTPDOWNLOADER_H_
#include "HttpClientImp.h"
namespace mediakit {
class HttpDownloader : public HttpClientImp {
public:
using Ptr = std::shared_ptr<HttpDownloader>;
using onDownloadResult = std::function<void(const toolkit::SockException &ex, const std::string &filePath)>;
~HttpDownloader() override;
/**
* ,
* @param url http url
* @param file_path
* @param append
*/
void startDownload(const std::string &url, const std::string &file_path = "", bool append = false);
void startDownload(const std::string &url, const onDownloadResult &cb) {
setOnResult(cb);
startDownload(url, "", false);
}
void setOnResult(const onDownloadResult &cb) { _on_result = cb; }
protected:
void onResponseBody(const char *buf, size_t size) override;
void onResponseHeader(const std::string &status, const HttpHeader &headers) override;
void onResponseCompleted(const toolkit::SockException &ex) override;
private:
void closeFile();
private:
FILE *_save_file = nullptr;
std::string _file_path;
onDownloadResult _on_result;
};
} /* namespace mediakit */
#endif /* SRC_HTTP_HTTPDOWNLOADER_H_ */

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_HTTPFILEMANAGER_H
#define ZLMEDIAKIT_HTTPFILEMANAGER_H
#include "HttpBody.h"
#include "HttpCookie.h"
#include "Common/Parser.h"
#include "Network/Session.h"
#include "Util/function_traits.h"
namespace mediakit {
class HttpResponseInvokerImp{
public:
typedef std::function<void(int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body)> HttpResponseInvokerLambda0;
typedef std::function<void(int code, const StrCaseMap &headerOut, const std::string &body)> HttpResponseInvokerLambda1;
template<typename C>
HttpResponseInvokerImp(const C &c):HttpResponseInvokerImp(typename toolkit::function_traits<C>::stl_function_type(c)) {}
HttpResponseInvokerImp(const HttpResponseInvokerLambda0 &lambda);
HttpResponseInvokerImp(const HttpResponseInvokerLambda1 &lambda);
void operator()(int code, const StrCaseMap &headerOut, const toolkit::Buffer::Ptr &body) const;
void operator()(int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const;
void operator()(int code, const StrCaseMap &headerOut, const std::string &body) const;
void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const std::string &file, bool use_mmap = true, bool is_path = true) const;
operator bool();
private:
HttpResponseInvokerLambda0 _lambad;
};
/**
* http静态文件夹服务器的访问权限
*/
class HttpFileManager {
public:
typedef std::function<void(int code, const std::string &content_type, const StrCaseMap &responseHeader, const HttpBody::Ptr &body)> invoker;
/**
* 访
* @param sender
* @param parser http请求
* @param cb
*/
static void onAccessPath(toolkit::Session &sender, Parser &parser, const invoker &cb);
/**
* mime值
* @param name
* @return mime值
*/
static const std::string &getContentType(const char *name);
/**
* ip是否再白名单中
* @param ip ipv4和ipv6
*/
static bool isIPAllowed(const std::string &ip);
private:
HttpFileManager() = delete;
~HttpFileManager() = delete;
};
}
#endif //ZLMEDIAKIT_HTTPFILEMANAGER_H

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_HTTPREQUESTSPLITTER_H
#define ZLMEDIAKIT_HTTPREQUESTSPLITTER_H
#include <string>
#include "Network/Buffer.h"
namespace mediakit {
class HttpRequestSplitter {
public:
HttpRequestSplitter();
virtual ~HttpRequestSplitter() = default;
/**
*
* @param data
* @param len
* @warning len + 1, 使 strstr , , @p len + 1 '\0' .
*/
virtual void input(const char *data, size_t len);
/**
*
*/
void reset();
/**
*
*/
size_t remainDataSize();
/**
*
*/
const char *remainData() const;
/**
*
*/
void setMaxCacheSize(size_t max_cache_size);
protected:
/**
*
* @param data
* @param len
*
* @return content长度,
* <0 : contentcontent将分段通过onRecvContent函数回调出去
* 0 : ,
* >0 : content,content并等到所有content接收完毕一次性通过onRecvContent函数回调出去
*/
virtual ssize_t onRecvHeader(const char *data,size_t len) = 0;
/**
* content分片或全部数据
* onRecvHeader函数返回>0,
* @param data content分片或全部数据
* @param len
*/
virtual void onRecvContent(const char *data,size_t len) {};
/**
*
* @param data
* @param len
* @return nullptr代表未找到包位
*/
virtual const char *onSearchPacketTail(const char *data, size_t len);
/**
* content len
*/
void setContentLen(ssize_t content_len);
private:
ssize_t _content_len = 0;
size_t _max_cache_size = 0;
size_t _remain_data_size = 0;
toolkit::BufferLikeString _remain_data;
};
} /* namespace mediakit */
#endif //ZLMEDIAKIT_HTTPREQUESTSPLITTER_H

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef Htt_HttpRequester_h
#define Htt_HttpRequester_h
#include "HttpClientImp.h"
namespace mediakit {
class HttpRequester : public HttpClientImp {
public:
using Ptr = std::shared_ptr<HttpRequester>;
using HttpRequesterResult = std::function<void(const toolkit::SockException &ex, const Parser &response)>;
void setOnResult(const HttpRequesterResult &onResult);
void startRequester(const std::string &url, const HttpRequesterResult &on_result, float timeout_sec = 10);
void setRetry(size_t count, size_t delay);
size_t getRetry() const { return _retry; }
size_t getRetryDelay() const { return _retry_delay; }
size_t getMaxRetry() const { return _max_retry; }
void clear() override;
private:
void onResponseHeader(const std::string &status, const HttpHeader &headers) override;
void onResponseBody(const char *buf, size_t size) override;
void onResponseCompleted(const toolkit::SockException &ex) override;
private:
size_t _retry = 0;
size_t _max_retry = 0;
size_t _retry_delay = 2000; // ms
std::string _res_body;
HttpRequesterResult _on_result;
};
}//namespace mediakit
#endif /* Htt_HttpRequester_h */

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_HTTP_HTTPSESSION_H_
#define SRC_HTTP_HTTPSESSION_H_
#include <functional>
#include "Network/Session.h"
#include "Rtmp/FlvMuxer.h"
#include "HttpRequestSplitter.h"
#include "WebSocketSplitter.h"
#include "HttpCookieManager.h"
#include "HttpFileManager.h"
#include "TS/TSMediaSource.h"
#include "FMP4/FMP4MediaSource.h"
namespace mediakit {
class HttpSession: public toolkit::Session,
public FlvMuxer,
public HttpRequestSplitter,
public WebSocketSplitter {
public:
using Ptr = std::shared_ptr<HttpSession>;
using KeyValue = StrCaseMap;
using HttpResponseInvoker = HttpResponseInvokerImp ;
friend class AsyncSender;
/**
* @param errMsg
* @param accessPath 访
* @param cookieLifeSecond cookie有效期
**/
using HttpAccessPathInvoker = std::function<void(const std::string &errMsg,const std::string &accessPath, int cookieLifeSecond)>;
HttpSession(const toolkit::Socket::Ptr &pSock);
void onRecv(const toolkit::Buffer::Ptr &) override;
void onError(const toolkit::SockException &err) override;
void onManager() override;
static std::string urlDecodePath(const std::string &str);
static std::string urlDecodeComponent(const std::string &str);
void setTimeoutSec(size_t second);
void setMaxReqSize(size_t max_req_size);
protected:
//FlvMuxer override
void onWrite(const toolkit::Buffer::Ptr &data, bool flush) override ;
void onDetach() override;
std::shared_ptr<FlvMuxer> getSharedPtr() override;
//HttpRequestSplitter override
ssize_t onRecvHeader(const char *data,size_t len) override;
void onRecvContent(const char *data,size_t len) override;
/**
* content
* http-flv推流
* @param header http请求头
* @param data content分片数据
* @param len content分片数据大小
* @param totalSize content总大小,0content
* @param recvedSize
*/
virtual void onRecvUnlimitedContent(const Parser &header,
const char *data,
size_t len,
size_t totalSize,
size_t recvedSize){
shutdown(toolkit::SockException(toolkit::Err_shutdown,"http post content is too huge,default closed"));
}
/**
* websocket客户端连接上事件
* @param header http头
* @return true代表允许websocket连接
*/
virtual bool onWebSocketConnect(const Parser &header){
WarnP(this) << "http server do not support websocket default";
return false;
}
//WebSocketSplitter override
/**
* websocket协议打包后回调
* @param buffer websocket协议数据
*/
void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override;
/**
* webSocket数据包后回调
* @param header
*/
void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override;
//重载获取客户端ip
std::string get_peer_ip() override;
private:
void onHttpRequest_GET();
void onHttpRequest_POST();
void onHttpRequest_HEAD();
void onHttpRequest_OPTIONS();
bool checkLiveStream(const std::string &schema, const std::string &url_suffix, const std::function<void(const MediaSource::Ptr &src)> &cb);
bool checkLiveStreamFlv(const std::function<void()> &cb = nullptr);
bool checkLiveStreamTS(const std::function<void()> &cb = nullptr);
bool checkLiveStreamFMP4(const std::function<void()> &fmp4_list = nullptr);
bool checkWebSocket();
bool emitHttpEvent(bool doInvoke);
void urlDecode(Parser &parser);
void sendNotFound(bool bClose);
void sendResponse(int code, bool bClose, const char *pcContentType = nullptr,
const HttpSession::KeyValue &header = HttpSession::KeyValue(),
const HttpBody::Ptr &body = nullptr, bool no_content_length = false);
//设置socket标志
void setSocketFlags();
protected:
MediaInfo _media_info;
private:
bool _is_live_stream = false;
bool _live_over_websocket = false;
//超时时间
size_t _keep_alive_sec = 0;
//最大http请求字节大小
size_t _max_req_size = 0;
//消耗的总流量
uint64_t _total_bytes_usage = 0;
// http请求中的 Origin字段
std::string _origin;
Parser _parser;
toolkit::Ticker _ticker;
TSMediaSource::RingType::RingReader::Ptr _ts_reader;
FMP4MediaSource::RingType::RingReader::Ptr _fmp4_reader;
//处理content数据的callback
std::function<bool (const char *data,size_t len) > _on_recv_body;
};
using HttpsSession = toolkit::SessionWithSSL<HttpSession>;
} /* namespace mediakit */
#endif /* SRC_HTTP_HTTPSESSION_H_ */

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HTTP_HTTPTSPLAYER_H
#define HTTP_HTTPTSPLAYER_H
#include "Http/HttpDownloader.h"
#include "Player/MediaPlayer.h"
#include "Rtp/TSDecoder.h"
namespace mediakit {
//http-ts播发器未实现ts解复用
class HttpTSPlayer : public HttpClientImp {
public:
using Ptr = std::shared_ptr<HttpTSPlayer>;
using onComplete = std::function<void(const toolkit::SockException &)>;
HttpTSPlayer(const toolkit::EventPoller::Ptr &poller = nullptr);
/**
*
*/
void setOnComplete(onComplete cb);
/**
* ts包回调
*/
void setOnPacket(TSSegment::onSegment cb);
protected:
///HttpClient override///
void onResponseHeader(const std::string &status, const HttpHeader &header) override;
void onResponseBody(const char *buf, size_t size) override;
void onResponseCompleted(const toolkit::SockException &ex) override;
private:
void emitOnComplete(const toolkit::SockException &ex);
private:
onComplete _on_complete;
TSSegment::onSegment _on_segment;
};
}//namespace mediakit
#endif //HTTP_HTTPTSPLAYER_H

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
* Created by alex on 2021/4/6.
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HTTP_TSPLAYER_H
#define HTTP_TSPLAYER_H
#include "HttpTSPlayer.h"
#include "Player/PlayerBase.h"
namespace mediakit {
class TsPlayer : public HttpTSPlayer, public PlayerBase {
public:
TsPlayer(const toolkit::EventPoller::Ptr &poller);
/**
*
*/
void play(const std::string &url) override;
/**
*
*/
void teardown() override;
protected:
void onResponseBody(const char *buf, size_t size) override;
void onResponseCompleted(const toolkit::SockException &ex) override;
private:
bool _play_result = true;
bool _benchmark_mode = false;
};
} // namespace mediakit
#endif // HTTP_TSPLAYER_H

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
* Created by alex on 2021/4/6.
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef HTTP_TSPLAYERIMP_H
#define HTTP_TSPLAYERIMP_H
#include <unordered_set>
#include "TsPlayer.h"
namespace mediakit {
class TsPlayerImp : public PlayerImp<TsPlayer, PlayerBase>, private TrackListener {
public:
using Ptr = std::shared_ptr<TsPlayerImp>;
TsPlayerImp(const toolkit::EventPoller::Ptr &poller = nullptr);
private:
//// TsPlayer override////
void onResponseBody(const char *buf, size_t size) override;
private:
//// PlayerBase override////
void onPlayResult(const toolkit::SockException &ex) override;
std::vector<Track::Ptr> getTracks(bool ready = true) const override;
void onShutdown(const toolkit::SockException &ex) override;
private:
//// TrackListener override////
bool addTrack(const Track::Ptr &track) override { return true; };
void addTrackCompleted() override;
private:
DecoderImp::Ptr _decoder;
MediaSinkInterface::Ptr _demuxer;
};
}//namespace mediakit
#endif //HTTP_TSPLAYERIMP_H

View File

@ -0,0 +1,420 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_WebSocketClient_H
#define ZLMEDIAKIT_WebSocketClient_H
#include "Util/util.h"
#include "Util/base64.h"
#include "Util/SHA1.h"
#include "Network/TcpClient.h"
#include "HttpClientImp.h"
#include "WebSocketSplitter.h"
namespace mediakit {
template <typename ClientType, WebSocketHeader::Type DataType>
class HttpWsClient;
/**
* ,TcpClient数据发送前的拦截
* @tparam ClientType TcpClient派生类
* @tparam DataType ,
*/
template <typename ClientType, WebSocketHeader::Type DataType>
class ClientTypeImp : public ClientType {
public:
friend class HttpWsClient<ClientType, DataType>;
using onBeforeSendCB = std::function<ssize_t(const toolkit::Buffer::Ptr &buf)>;
template <typename... ArgsType>
ClientTypeImp(ArgsType &&...args) : ClientType(std::forward<ArgsType>(args)...) {}
/**
* websocket协议
*/
ssize_t send(toolkit::Buffer::Ptr buf) override {
if (_beforeSendCB) {
return _beforeSendCB(buf);
}
return ClientType::send(std::move(buf));
}
protected:
/**
*
* @param cb
*/
void setOnBeforeSendCB(const onBeforeSendCB &cb) { _beforeSendCB = cb; }
private:
onBeforeSendCB _beforeSendCB;
};
/**
* weksocket TcpClient派生类事件的桥接
* @tparam ClientType TcpClient派生类
* @tparam DataType websocket负载类型TEXT还是BINARY类型
*/
template <typename ClientType, WebSocketHeader::Type DataType = WebSocketHeader::TEXT>
class HttpWsClient : public HttpClientImp, public WebSocketSplitter {
public:
using Ptr = std::shared_ptr<HttpWsClient>;
HttpWsClient(const std::shared_ptr<ClientTypeImp<ClientType, DataType>> &delegate) : _weak_delegate(delegate) {
_Sec_WebSocket_Key = encodeBase64(toolkit::makeRandStr(16, false));
setPoller(delegate->getPoller());
}
/**
* ws握手
* @param ws_url ws连接url
* @param fTimeOutSec
*/
void startWsClient(const std::string &ws_url, float fTimeOutSec) {
std::string http_url = ws_url;
toolkit::replace(http_url, "ws://", "http://");
toolkit::replace(http_url, "wss://", "https://");
setMethod("GET");
addHeader("Upgrade", "websocket");
addHeader("Connection", "Upgrade");
addHeader("Sec-WebSocket-Version", "13");
addHeader("Sec-WebSocket-Key", _Sec_WebSocket_Key);
_onRecv = nullptr;
setHeaderTimeout(fTimeOutSec * 1000);
sendRequest(http_url);
}
void closeWsClient() {
if (!_onRecv) {
// 未连接
return;
}
WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = CLOSE;
// 客户端需要加密
header._mask_flag = true;
WebSocketSplitter::encode(header, nullptr);
}
protected:
// HttpClientImp override
/**
* http回复头
* @param status :200 OK
* @param headers http头
*/
void onResponseHeader(const std::string &status, const HttpHeader &headers) override {
if (status == "101") {
auto Sec_WebSocket_Accept = encodeBase64(toolkit::SHA1::encode_bin(_Sec_WebSocket_Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
if (Sec_WebSocket_Accept == const_cast<HttpHeader &>(headers)["Sec-WebSocket-Accept"]) {
// success
onWebSocketException(toolkit::SockException());
// 防止ws服务器返回Content-Length
const_cast<HttpHeader &>(headers).erase("Content-Length");
return;
}
shutdown(toolkit::SockException(toolkit::Err_shutdown, StrPrinter << "Sec-WebSocket-Accept mismatch"));
return;
}
shutdown(toolkit::SockException(toolkit::Err_shutdown, StrPrinter << "bad http status code:" << status));
};
/**
* http回复完毕,
*/
void onResponseCompleted(const toolkit::SockException &ex) override {}
/**
* websocket负载数据
*/
void onResponseBody(const char *buf, size_t size) override {
if (_onRecv) {
// 完成websocket握手后拦截websocket数据并解析
_onRecv(buf, size);
}
};
// TcpClient override
void onRecv(const toolkit::Buffer::Ptr &buf) override {
HttpClientImp::onRecv(buf);
}
/**
*
*/
void onManager() override {
if (_onRecv) {
// websocket连接成功了
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onManager();
}
} else {
// websocket连接中...
HttpClientImp::onManager();
}
if (!_onRecv) {
// websocket尚未链接
return;
}
if (_recv_ticker.elapsedTime() > 30 * 1000) {
shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout"));
} else if (_recv_ticker.elapsedTime() > 10 * 1000) {
// 没收到回复每10秒发送次ping 包
WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = PING;
header._mask_flag = true;
WebSocketSplitter::encode(header, nullptr);
}
}
/**
*
*/
void onFlush() override {
if (_onRecv) {
// websocket连接成功了
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onFlush();
}
} else {
// websocket连接中...
HttpClientImp::onFlush();
}
}
/**
* tcp连接结果
*/
void onConnect(const toolkit::SockException &ex) override {
if (ex) {
// tcp连接失败直接返回失败
onWebSocketException(ex);
return;
}
// 开始websocket握手
HttpClientImp::onConnect(ex);
}
/**
* tcp连接断开
*/
void onError(const toolkit::SockException &ex) override {
// tcp断开或者shutdown导致的断开
onWebSocketException(ex);
}
// WebSocketSplitter override
/**
* webSocket数据包包头onWebSocketDecodePayload回调
* @param header
*/
void onWebSocketDecodeHeader(const WebSocketHeader &header) override { _payload_section.clear(); }
/**
* webSocket数据包负载
* @param header
* @param ptr
* @param len
* @param recved ()header._payload_len时则接受完毕
*/
void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) override {
_payload_section.append((char *)ptr, len);
}
/**
* webSocket数据包后回调
* @param header
*/
void onWebSocketDecodeComplete(const WebSocketHeader &header_in) override {
WebSocketHeader &header = const_cast<WebSocketHeader &>(header_in);
auto flag = header._mask_flag;
// websocket客户端发送数据需要加密
header._mask_flag = true;
_recv_ticker.resetTime();
switch (header._opcode) {
case WebSocketHeader::CLOSE: {
// 服务器主动关闭
WebSocketSplitter::encode(header, nullptr);
shutdown(toolkit::SockException(toolkit::Err_eof, "websocket server close the connection"));
break;
}
case WebSocketHeader::PING: {
// 心跳包
header._opcode = WebSocketHeader::PONG;
WebSocketSplitter::encode(header, std::make_shared<toolkit::BufferString>(std::move(_payload_section)));
break;
}
case WebSocketHeader::CONTINUATION:
case WebSocketHeader::TEXT:
case WebSocketHeader::BINARY: {
if (!header._fin) {
// 还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出
_payload_cache.append(std::move(_payload_section));
if (_payload_cache.size() < MAX_WS_PACKET) {
// 还有内存容量缓存分片数据
break;
}
// 分片缓存太大,需要清空
}
// 最后一个包
if (_payload_cache.empty()) {
// 这个包是唯一个分片
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_section)));
}
break;
}
// 这个包由多个分片组成
_payload_cache.append(std::move(_payload_section));
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onRecv(std::make_shared<WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_cache)));
}
_payload_cache.clear();
break;
}
default: break;
}
_payload_section.clear();
header._mask_flag = flag;
}
/**
* websocket数据编码回调
* @param ptr
* @param len
*/
void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override { HttpClientImp::send(std::move(buffer)); }
private:
void onWebSocketException(const toolkit::SockException &ex) {
if (!ex) {
// websocket握手成功
// 此处截取TcpClient派生类发送的数据并进行websocket协议打包
std::weak_ptr<HttpWsClient> weakSelf = std::static_pointer_cast<HttpWsClient>(shared_from_this());
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->setOnBeforeSendCB([weakSelf](const toolkit::Buffer::Ptr &buf) {
auto strong_self = weakSelf.lock();
if (strong_self) {
WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = DataType;
// 客户端需要加密
header._mask_flag = true;
strong_self->WebSocketSplitter::encode(header, buf);
}
return buf->size();
});
// 设置sock否则shutdown等接口都无效
strong_ref->setSock(HttpClientImp::getSock());
// 触发连接成功事件
strong_ref->onConnect(ex);
}
// 拦截websocket数据接收
_onRecv = [this](const char *data, size_t len) {
// 解析websocket数据包
this->WebSocketSplitter::decode((uint8_t *)data, len);
};
return;
}
// websocket握手失败或者tcp连接失败或者中途断开
if (_onRecv) {
// 握手成功之后的中途断开
_onRecv = nullptr;
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onError(ex);
}
return;
}
// websocket握手失败或者tcp连接失败
if (auto strong_ref = _weak_delegate.lock()) {
strong_ref->onConnect(ex);
}
}
private:
std::string _Sec_WebSocket_Key;
std::function<void(const char *data, size_t len)> _onRecv;
std::weak_ptr<ClientTypeImp<ClientType, DataType>> _weak_delegate;
std::string _payload_section;
std::string _payload_cache;
toolkit::Ticker _recv_ticker;
};
/**
* Tcp客户端转WebSocket客户端模板
* TcpClient派生类任何代码的情况下快速实现WebSocket协议的包装
* @tparam ClientType TcpClient派生类
* @tparam DataType websocket负载类型TEXT还是BINARY类型
* @tparam useWSS 使ws还是wss连接
*/
template <typename ClientType, WebSocketHeader::Type DataType = WebSocketHeader::TEXT, bool useWSS = false>
class WebSocketClient : public ClientTypeImp<ClientType, DataType> {
public:
using Ptr = std::shared_ptr<WebSocketClient>;
template <typename... ArgsType>
WebSocketClient(ArgsType &&...args) : ClientTypeImp<ClientType, DataType>(std::forward<ArgsType>(args)...) {}
~WebSocketClient() override { _wsClient->closeWsClient(); }
/**
* startConnect方法
* TcpClient的连接服务器行为使WebSocket握手
* @param host websocket服务器ip或域名
* @param iPort websocket服务器端口
* @param timeout_sec
* @param local_port
*/
void startConnect(const std::string &host, uint16_t port, float timeout_sec = 3, uint16_t local_port = 0) override {
std::string ws_url;
if (useWSS) {
// 加密的ws
ws_url = StrPrinter << "wss://" + host << ":" << port << "/";
} else {
// 明文ws
ws_url = StrPrinter << "ws://" + host << ":" << port << "/";
}
startWebSocket(ws_url, timeout_sec);
}
void startWebSocket(const std::string &ws_url, float fTimeOutSec = 3) {
_wsClient = std::make_shared<HttpWsClient<ClientType, DataType>>(std::static_pointer_cast<WebSocketClient>(this->shared_from_this()));
_wsClient->setOnCreateSocket([this](const toolkit::EventPoller::Ptr &) { return this->createSocket(); });
_wsClient->startWsClient(ws_url, fTimeOutSec);
}
HttpClient &getHttpClient() { return *_wsClient; }
private:
typename HttpWsClient<ClientType, DataType>::Ptr _wsClient;
};
} // namespace mediakit
#endif // ZLMEDIAKIT_WebSocketClient_H

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_WEBSOCKETSESSION_H
#define ZLMEDIAKIT_WEBSOCKETSESSION_H
#include "HttpSession.h"
#include "Network/TcpServer.h"
/**
*
*/
class SendInterceptor{
public:
using onBeforeSendCB =std::function<ssize_t (const toolkit::Buffer::Ptr &buf)>;
virtual ~SendInterceptor() = default;
virtual void setOnBeforeSendCB(const onBeforeSendCB &cb) = 0;
};
/**
* Session派生类发送数据的截取
* websocket协议的打包
*/
template <typename SessionType>
class SessionTypeImp : public SessionType, public SendInterceptor{
public:
using Ptr = std::shared_ptr<SessionTypeImp>;
SessionTypeImp(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock) :
SessionType(pSock) {}
/**
*
* @param cb
*/
void setOnBeforeSendCB(const onBeforeSendCB &cb) override {
_beforeSendCB = cb;
}
protected:
/**
* send函数截取数据
* @param buf
* @return
*/
ssize_t send(toolkit::Buffer::Ptr buf) override {
if (_beforeSendCB) {
return _beforeSendCB(buf);
}
return SessionType::send(std::move(buf));
}
private:
onBeforeSendCB _beforeSendCB;
};
template <typename SessionType>
class SessionCreator {
public:
//返回的Session必须派生于SendInterceptor可以返回null
toolkit::Session::Ptr operator()(const mediakit::Parser &header, const mediakit::HttpSession &parent, const toolkit::Socket::Ptr &pSock, mediakit::WebSocketHeader::Type &data_type){
return std::make_shared<SessionTypeImp<SessionType> >(header,parent,pSock);
}
};
/**
* WebSocket协议
* WebSock协议下的具体业务协议WebSocket协议的Rtmp协议等
*/
template<typename Creator, typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
class WebSocketSessionBase : public HttpSessionType {
public:
WebSocketSessionBase(const toolkit::Socket::Ptr &pSock) : HttpSessionType(pSock){}
//收到eof或其他导致脱离TcpServer事件的回调
void onError(const toolkit::SockException &err) override{
HttpSessionType::onError(err);
if(_session){
_session->onError(err);
}
}
//每隔一段时间触发,用来做超时管理
void onManager() override{
if (_session) {
_session->onManager();
} else {
HttpSessionType::onManager();
}
if (!_session) {
// websocket尚未链接
return;
}
if (_recv_ticker.elapsedTime() > 30 * 1000) {
HttpSessionType::shutdown(toolkit::SockException(toolkit::Err_timeout, "websocket timeout"));
} else if (_recv_ticker.elapsedTime() > 10 * 1000) {
// 没收到回复每10秒发送次ping 包
mediakit::WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = mediakit::WebSocketHeader::PING;
header._mask_flag = false;
HttpSessionType::encode(header, nullptr);
}
}
void attachServer(const toolkit::Server &server) override{
HttpSessionType::attachServer(server);
_weak_server = const_cast<toolkit::Server &>(server).shared_from_this();
}
protected:
/**
* websocket客户端连接上事件
* @param header http头
* @return true代表允许websocket连接
*/
bool onWebSocketConnect(const mediakit::Parser &header) override{
//创建websocket session类
auto data_type = DataType;
_session = _creator(header, *this, HttpSessionType::getSock(), data_type);
if (!_session) {
// 此url不允许创建websocket连接
return false;
}
auto strongServer = _weak_server.lock();
if (strongServer) {
_session->attachServer(*strongServer);
}
//此处截取数据并进行websocket协议打包
std::weak_ptr<WebSocketSessionBase> weakSelf = std::static_pointer_cast<WebSocketSessionBase>(HttpSessionType::shared_from_this());
std::dynamic_pointer_cast<SendInterceptor>(_session)->setOnBeforeSendCB([weakSelf, data_type](const toolkit::Buffer::Ptr &buf) {
auto strongSelf = weakSelf.lock();
if (strongSelf) {
mediakit::WebSocketHeader header;
header._fin = true;
header._reserved = 0;
header._opcode = data_type;
header._mask_flag = false;
strongSelf->HttpSessionType::encode(header, buf);
}
return buf->size();
});
//允许websocket客户端
return true;
}
/**
* webSocket数据包
*/
void onWebSocketDecodeHeader(const mediakit::WebSocketHeader &packet) override{
//新包,原来的包残余数据清空掉
_payload_section.clear();
}
/**
* websocket数据包负载
*/
void onWebSocketDecodePayload(const mediakit::WebSocketHeader &packet,const uint8_t *ptr,size_t len,size_t recved) override {
_payload_section.append((char *)ptr,len);
}
/**
* webSocket数据包后回调
* @param header
*/
void onWebSocketDecodeComplete(const mediakit::WebSocketHeader &header_in) override {
auto header = const_cast<mediakit::WebSocketHeader&>(header_in);
auto flag = header._mask_flag;
header._mask_flag = false;
_recv_ticker.resetTime();
switch (header._opcode){
case mediakit::WebSocketHeader::CLOSE:{
HttpSessionType::encode(header,nullptr);
HttpSessionType::shutdown(toolkit::SockException(toolkit::Err_shutdown, "recv close request from client"));
break;
}
case mediakit::WebSocketHeader::PING:{
header._opcode = mediakit::WebSocketHeader::PONG;
HttpSessionType::encode(header,std::make_shared<toolkit::BufferString>(_payload_section));
break;
}
case mediakit::WebSocketHeader::CONTINUATION:
case mediakit::WebSocketHeader::TEXT:
case mediakit::WebSocketHeader::BINARY:{
if (!header._fin) {
//还有后续分片数据, 我们先缓存数据,所有分片收集完成才一次性输出
_payload_cache.append(std::move(_payload_section));
if (_payload_cache.size() < MAX_WS_PACKET) {
//还有内存容量缓存分片数据
break;
}
//分片缓存太大,需要清空
}
//最后一个包
if (_payload_cache.empty()) {
//这个包是唯一个分片
_session->onRecv(std::make_shared<mediakit::WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_section)));
break;
}
//这个包由多个分片组成
_payload_cache.append(std::move(_payload_section));
_session->onRecv(std::make_shared<mediakit::WebSocketBuffer>(header._opcode, header._fin, std::move(_payload_cache)));
_payload_cache.clear();
break;
}
default: break;
}
_payload_section.clear();
header._mask_flag = flag;
}
/**
* websocket协议打包后回调
*/
void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer) override{
HttpSessionType::send(std::move(buffer));
}
private:
std::string _payload_cache;
std::string _payload_section;
std::weak_ptr<toolkit::Server> _weak_server;
toolkit::Session::Ptr _session;
Creator _creator;
toolkit::Ticker _recv_ticker;
};
template<typename SessionType,typename HttpSessionType = mediakit::HttpSession, mediakit::WebSocketHeader::Type DataType = mediakit::WebSocketHeader::TEXT>
class WebSocketSession : public WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>{
public:
WebSocketSession(const toolkit::Socket::Ptr &pSock) : WebSocketSessionBase<SessionCreator<SessionType>,HttpSessionType,DataType>(pSock){}
};
#endif //ZLMEDIAKIT_WEBSOCKETSESSION_H

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_WEBSOCKETSPLITTER_H
#define ZLMEDIAKIT_WEBSOCKETSPLITTER_H
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include "Network/Buffer.h"
//websocket组合包最大不得超过4MB(防止内存爆炸)
#define MAX_WS_PACKET (4 * 1024 * 1024)
namespace mediakit {
class WebSocketHeader {
public:
using Ptr = std::shared_ptr<WebSocketHeader>;
typedef enum {
CONTINUATION = 0x0,
TEXT = 0x1,
BINARY = 0x2,
RSV3 = 0x3,
RSV4 = 0x4,
RSV5 = 0x5,
RSV6 = 0x6,
RSV7 = 0x7,
CLOSE = 0x8,
PING = 0x9,
PONG = 0xA,
CONTROL_RSVB = 0xB,
CONTROL_RSVC = 0xC,
CONTROL_RSVD = 0xD,
CONTROL_RSVE = 0xE,
CONTROL_RSVF = 0xF
} Type;
public:
WebSocketHeader() : _mask(4){
//获取_mask内部buffer的内存地址该内存是malloc开辟的地址为随机
uint64_t ptr = (uint64_t)(&_mask[0]);
//根据内存地址设置掩码随机数
_mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4);
}
virtual ~WebSocketHeader() = default;
public:
bool _fin;
uint8_t _reserved;
Type _opcode;
bool _mask_flag;
size_t _payload_len;
std::vector<uint8_t > _mask;
};
//websocket协议收到的字符串类型缓存用户协议层获取该数据传输的方式
class WebSocketBuffer : public toolkit::BufferString {
public:
using Ptr = std::shared_ptr<WebSocketBuffer>;
template<typename ...ARGS>
WebSocketBuffer(WebSocketHeader::Type headType, bool fin, ARGS &&...args)
: toolkit::BufferString(std::forward<ARGS>(args)...), _fin(fin), _head_type(headType){}
WebSocketHeader::Type headType() const { return _head_type; }
bool isFinished() const { return _fin; };
private:
bool _fin;
WebSocketHeader::Type _head_type;
};
class WebSocketSplitter : public WebSocketHeader{
public:
/**
* 便webSocket数据以及处理粘包问题
* onWebSocketDecodeHeader和onWebSocketDecodePayload回调
* @param data
* @param len
*/
void decode(uint8_t *data, size_t len);
/**
*
* 2onWebSocketEncodeData回调
* @param header
* @param buffer
*/
void encode(const WebSocketHeader &header,const toolkit::Buffer::Ptr &buffer);
protected:
/**
* webSocket数据包包头onWebSocketDecodePayload回调
* @param header
*/
virtual void onWebSocketDecodeHeader(const WebSocketHeader &header) {};
/**
* webSocket数据包负载
* @param header
* @param ptr
* @param len
* @param recved ()header._payload_len时则接受完毕
*/
virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, size_t len, size_t recved) {};
/**
* webSocket数据包后回调
* @param header
*/
virtual void onWebSocketDecodeComplete(const WebSocketHeader &header) {};
/**
* websocket数据编码回调
* @param ptr
* @param len
*/
virtual void onWebSocketEncodeData(toolkit::Buffer::Ptr buffer){};
private:
void onPayloadData(uint8_t *data, size_t len);
private:
bool _got_header = false;
int _mask_offset = 0;
size_t _payload_offset = 0;
std::string _remain_data;
};
} /* namespace mediakit */
#endif //ZLMEDIAKIT_WEBSOCKETSPLITTER_H

View File

@ -0,0 +1,449 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLTOOLKIT_BUFFER_H
#define ZLTOOLKIT_BUFFER_H
#include <cassert>
#include <memory>
#include <string>
#include <vector>
#include <type_traits>
#include <functional>
#include "Util/util.h"
#include "Util/ResourcePool.h"
namespace toolkit {
template <typename T> struct is_pointer : public std::false_type {};
template <typename T> struct is_pointer<std::shared_ptr<T> > : public std::true_type {};
template <typename T> struct is_pointer<std::shared_ptr<T const> > : public std::true_type {};
template <typename T> struct is_pointer<T*> : public std::true_type {};
template <typename T> struct is_pointer<const T*> : public std::true_type {};
//缓存基类
class Buffer : public noncopyable {
public:
using Ptr = std::shared_ptr<Buffer>;
Buffer() = default;
virtual ~Buffer() = default;
//返回数据长度
virtual char *data() const = 0;
virtual size_t size() const = 0;
virtual std::string toString() const {
return std::string(data(), size());
}
virtual size_t getCapacity() const {
return size();
}
private:
//对象个数统计
ObjectStatistic<Buffer> _statistic;
};
template <typename C>
class BufferOffset : public Buffer {
public:
using Ptr = std::shared_ptr<BufferOffset>;
BufferOffset(C data, size_t offset = 0, size_t len = 0) : _data(std::move(data)) {
setup(offset, len);
}
~BufferOffset() override = default;
char *data() const override {
return const_cast<char *>(getPointer<C>(_data)->data()) + _offset;
}
size_t size() const override {
return _size;
}
std::string toString() const override {
return std::string(data(), size());
}
private:
void setup(size_t offset = 0, size_t size = 0) {
auto max_size = getPointer<C>(_data)->size();
assert(offset + size <= max_size);
if (!size) {
size = max_size - offset;
}
_size = size;
_offset = offset;
}
template<typename T>
static typename std::enable_if<::toolkit::is_pointer<T>::value, const T &>::type
getPointer(const T &data) {
return data;
}
template<typename T>
static typename std::enable_if<!::toolkit::is_pointer<T>::value, const T *>::type
getPointer(const T &data) {
return &data;
}
private:
C _data;
size_t _size;
size_t _offset;
};
using BufferString = BufferOffset<std::string>;
//指针式缓存对象,
class BufferRaw : public Buffer {
public:
using Ptr = std::shared_ptr<BufferRaw>;
static Ptr create();
~BufferRaw() override {
if (_data) {
delete[] _data;
}
}
//在写入数据时请确保内存是否越界
char *data() const override {
return _data;
}
//有效数据大小
size_t size() const override {
return _size;
}
//分配内存大小
void setCapacity(size_t capacity) {
if (_data) {
do {
if (capacity > _capacity) {
//请求的内存大于当前内存,那么重新分配
break;
}
if (_capacity < 2 * 1024) {
//2K以下不重复开辟内存直接复用
return;
}
if (2 * capacity > _capacity) {
//如果请求的内存大于当前内存的一半,那么也复用
return;
}
} while (false);
delete[] _data;
}
_data = new char[capacity];
_capacity = capacity;
}
//设置有效数据大小
virtual void setSize(size_t size) {
if (size > _capacity) {
throw std::invalid_argument("Buffer::setSize out of range");
}
_size = size;
}
//赋值数据
void assign(const char *data, size_t size = 0) {
if (size <= 0) {
size = strlen(data);
}
setCapacity(size + 1);
memcpy(_data, data, size);
_data[size] = '\0';
setSize(size);
}
size_t getCapacity() const override {
return _capacity;
}
protected:
friend class ResourcePool_l<BufferRaw>;
BufferRaw(size_t capacity = 0) {
if (capacity) {
setCapacity(capacity);
}
}
BufferRaw(const char *data, size_t size = 0) {
assign(data, size);
}
private:
size_t _size = 0;
size_t _capacity = 0;
char *_data = nullptr;
//对象个数统计
ObjectStatistic<BufferRaw> _statistic;
};
class BufferLikeString : public Buffer {
public:
~BufferLikeString() override = default;
BufferLikeString() {
_erase_head = 0;
_erase_tail = 0;
}
BufferLikeString(std::string str) {
_str = std::move(str);
_erase_head = 0;
_erase_tail = 0;
}
BufferLikeString &operator=(std::string str) {
_str = std::move(str);
_erase_head = 0;
_erase_tail = 0;
return *this;
}
BufferLikeString(const char *str) {
_str = str;
_erase_head = 0;
_erase_tail = 0;
}
BufferLikeString &operator=(const char *str) {
_str = str;
_erase_head = 0;
_erase_tail = 0;
return *this;
}
BufferLikeString(BufferLikeString &&that) {
_str = std::move(that._str);
_erase_head = that._erase_head;
_erase_tail = that._erase_tail;
that._erase_head = 0;
that._erase_tail = 0;
}
BufferLikeString &operator=(BufferLikeString &&that) {
_str = std::move(that._str);
_erase_head = that._erase_head;
_erase_tail = that._erase_tail;
that._erase_head = 0;
that._erase_tail = 0;
return *this;
}
BufferLikeString(const BufferLikeString &that) {
_str = that._str;
_erase_head = that._erase_head;
_erase_tail = that._erase_tail;
}
BufferLikeString &operator=(const BufferLikeString &that) {
_str = that._str;
_erase_head = that._erase_head;
_erase_tail = that._erase_tail;
return *this;
}
char *data() const override {
return (char *) _str.data() + _erase_head;
}
size_t size() const override {
return _str.size() - _erase_tail - _erase_head;
}
BufferLikeString &erase(size_t pos = 0, size_t n = std::string::npos) {
if (pos == 0) {
//移除前面的数据
if (n != std::string::npos) {
//移除部分
if (n > size()) {
//移除太多数据了
throw std::out_of_range("BufferLikeString::erase out_of_range in head");
}
//设置起始便宜量
_erase_head += n;
data()[size()] = '\0';
return *this;
}
//移除全部数据
_erase_head = 0;
_erase_tail = _str.size();
data()[0] = '\0';
return *this;
}
if (n == std::string::npos || pos + n >= size()) {
//移除末尾所有数据
if (pos >= size()) {
//移除太多数据
throw std::out_of_range("BufferLikeString::erase out_of_range in tail");
}
_erase_tail += size() - pos;
data()[size()] = '\0';
return *this;
}
//移除中间的
if (pos + n > size()) {
//超过长度限制
throw std::out_of_range("BufferLikeString::erase out_of_range in middle");
}
_str.erase(_erase_head + pos, n);
return *this;
}
BufferLikeString &append(const BufferLikeString &str) {
return append(str.data(), str.size());
}
BufferLikeString &append(const std::string &str) {
return append(str.data(), str.size());
}
BufferLikeString &append(const char *data) {
return append(data, strlen(data));
}
BufferLikeString &append(const char *data, size_t len) {
if (len <= 0) {
return *this;
}
if (_erase_head > _str.capacity() / 2) {
moveData();
}
if (_erase_tail == 0) {
_str.append(data, len);
return *this;
}
_str.insert(_erase_head + size(), data, len);
return *this;
}
void push_back(char c) {
if (_erase_tail == 0) {
_str.push_back(c);
return;
}
data()[size()] = c;
--_erase_tail;
data()[size()] = '\0';
}
BufferLikeString &insert(size_t pos, const char *s, size_t n) {
_str.insert(_erase_head + pos, s, n);
return *this;
}
BufferLikeString &assign(const char *data) {
return assign(data, strlen(data));
}
BufferLikeString &assign(const char *data, size_t len) {
if (len <= 0) {
return *this;
}
if (data >= _str.data() && data < _str.data() + _str.size()) {
_erase_head = data - _str.data();
if (data + len > _str.data() + _str.size()) {
throw std::out_of_range("BufferLikeString::assign out_of_range");
}
_erase_tail = _str.data() + _str.size() - (data + len);
return *this;
}
_str.assign(data, len);
_erase_head = 0;
_erase_tail = 0;
return *this;
}
void clear() {
_erase_head = 0;
_erase_tail = 0;
_str.clear();
}
char &operator[](size_t pos) {
if (pos >= size()) {
throw std::out_of_range("BufferLikeString::operator[] out_of_range");
}
return data()[pos];
}
const char &operator[](size_t pos) const {
return (*const_cast<BufferLikeString *>(this))[pos];
}
size_t capacity() const {
return _str.capacity();
}
void reserve(size_t size) {
_str.reserve(size);
}
void resize(size_t size, char c = '\0') {
_str.resize(size, c);
_erase_head = 0;
_erase_tail = 0;
}
bool empty() const {
return size() <= 0;
}
std::string substr(size_t pos, size_t n = std::string::npos) const {
if (n == std::string::npos) {
//获取末尾所有的
if (pos >= size()) {
throw std::out_of_range("BufferLikeString::substr out_of_range");
}
return _str.substr(_erase_head + pos, size() - pos);
}
//获取部分
if (pos + n > size()) {
throw std::out_of_range("BufferLikeString::substr out_of_range");
}
return _str.substr(_erase_head + pos, n);
}
private:
void moveData() {
if (_erase_head) {
_str.erase(0, _erase_head);
_erase_head = 0;
}
}
private:
size_t _erase_head;
size_t _erase_tail;
std::string _str;
//对象个数统计
ObjectStatistic<BufferLikeString> _statistic;
};
}//namespace toolkit
#endif //ZLTOOLKIT_BUFFER_H

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLTOOLKIT_BUFFERSOCK_H
#define ZLTOOLKIT_BUFFERSOCK_H
#if !defined(_WIN32)
#include <sys/uio.h>
#include <limits.h>
#endif
#include <cassert>
#include <memory>
#include <string>
#include <vector>
#include <type_traits>
#include <functional>
#include "Util/util.h"
#include "Util/List.h"
#include "Util/ResourcePool.h"
#include "sockutil.h"
#include "Buffer.h"
namespace toolkit {
#if !defined(IOV_MAX)
#define IOV_MAX 1024
#endif
class BufferSock : public Buffer {
public:
using Ptr = std::shared_ptr<BufferSock>;
BufferSock(Buffer::Ptr ptr, struct sockaddr *addr = nullptr, int addr_len = 0);
~BufferSock() override = default;
char *data() const override;
size_t size() const override;
const struct sockaddr *sockaddr() const;
socklen_t socklen() const;
private:
int _addr_len = 0;
struct sockaddr_storage _addr;
Buffer::Ptr _buffer;
};
class BufferList : public noncopyable {
public:
using Ptr = std::shared_ptr<BufferList>;
using SendResult = std::function<void(const Buffer::Ptr &buffer, bool send_success)>;
BufferList() = default;
virtual ~BufferList() = default;
virtual bool empty() = 0;
virtual size_t count() = 0;
virtual ssize_t send(int fd, int flags) = 0;
static Ptr create(List<std::pair<Buffer::Ptr, bool> > list, SendResult cb, bool is_udp);
private:
//对象个数统计
ObjectStatistic<BufferList> _statistic;
};
}
#endif //ZLTOOLKIT_BUFFERSOCK_H

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLTOOLKIT_SERVER_H
#define ZLTOOLKIT_SERVER_H
#include <unordered_map>
#include "Util/mini.h"
#include "Session.h"
namespace toolkit {
// 全局的 Session 记录对象, 方便后面管理
// 线程安全的
class SessionMap : public std::enable_shared_from_this<SessionMap> {
public:
friend class SessionHelper;
using Ptr = std::shared_ptr<SessionMap>;
//单例
static SessionMap &Instance();
~SessionMap() = default;
//获取Session
Session::Ptr get(const std::string &tag);
void for_each_session(const std::function<void(const std::string &id, const Session::Ptr &session)> &cb);
private:
SessionMap() = default;
//移除Session
bool del(const std::string &tag);
//添加Session
bool add(const std::string &tag, const Session::Ptr &session);
private:
std::mutex _mtx_session;
std::unordered_map<std::string, std::weak_ptr<Session> > _map_session;
};
class Server;
class SessionHelper {
public:
bool enable = true;
using Ptr = std::shared_ptr<SessionHelper>;
SessionHelper(const std::weak_ptr<Server> &server, Session::Ptr session, std::string cls);
~SessionHelper();
const Session::Ptr &session() const;
const std::string &className() const;
private:
std::string _cls;
std::string _identifier;
Session::Ptr _session;
SessionMap::Ptr _session_map;
std::weak_ptr<Server> _server;
};
// server 基类, 暂时仅用于剥离 SessionHelper 对 TcpServer 的依赖
// 后续将 TCP 与 UDP 服务通用部分加到这里.
class Server : public std::enable_shared_from_this<Server>, public mINI {
public:
using Ptr = std::shared_ptr<Server>;
explicit Server(EventPoller::Ptr poller = nullptr);
virtual ~Server() = default;
protected:
EventPoller::Ptr _poller;
};
} // namespace toolkit
#endif // ZLTOOLKIT_SERVER_H

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLTOOLKIT_SESSION_H
#define ZLTOOLKIT_SESSION_H
#include <memory>
#include "Socket.h"
#include "Util/util.h"
#include "Util/SSLBox.h"
namespace toolkit {
// 会话, 用于存储一对客户端与服务端间的关系
class Server;
class TcpSession;
class UdpSession;
class Session : public SocketHelper {
public:
using Ptr = std::shared_ptr<Session>;
Session(const Socket::Ptr &sock);
~Session() override = default;
/**
* Session , Server Session
* @param server,
*/
virtual void attachServer(const Server &server) {}
/**
* Session
* @return
*/
std::string getIdentifier() const override;
private:
mutable std::string _id;
std::unique_ptr<toolkit::ObjectStatistic<toolkit::TcpSession> > _statistic_tcp;
std::unique_ptr<toolkit::ObjectStatistic<toolkit::UdpSession> > _statistic_udp;
};
// 通过该模板可以让TCP服务器快速支持TLS
template <typename SessionType>
class SessionWithSSL : public SessionType {
public:
template <typename... ArgsType>
SessionWithSSL(ArgsType &&...args)
: SessionType(std::forward<ArgsType>(args)...) {
_ssl_box.setOnEncData([&](const Buffer::Ptr &buf) { public_send(buf); });
_ssl_box.setOnDecData([&](const Buffer::Ptr &buf) { public_onRecv(buf); });
}
~SessionWithSSL() override { _ssl_box.flush(); }
void onRecv(const Buffer::Ptr &buf) override { _ssl_box.onRecv(buf); }
// 添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug
inline void public_onRecv(const Buffer::Ptr &buf) { SessionType::onRecv(buf); }
inline void public_send(const Buffer::Ptr &buf) { SessionType::send(buf); }
bool overSsl() const override { return true; }
protected:
ssize_t send(Buffer::Ptr buf) override {
auto size = buf->size();
_ssl_box.onSend(std::move(buf));
return size;
}
private:
SSL_Box _ssl_box;
};
} // namespace toolkit
#endif // ZLTOOLKIT_SESSION_H

View File

@ -0,0 +1,754 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef NETWORK_SOCKET_H
#define NETWORK_SOCKET_H
#include <memory>
#include <string>
#include <mutex>
#include <atomic>
#include <sstream>
#include <functional>
#include "Util/SpeedStatistic.h"
#include "sockutil.h"
#include "Poller/Timer.h"
#include "Poller/EventPoller.h"
#include "BufferSock.h"
namespace toolkit {
#if defined(MSG_NOSIGNAL)
#define FLAG_NOSIGNAL MSG_NOSIGNAL
#else
#define FLAG_NOSIGNAL 0
#endif //MSG_NOSIGNAL
#if defined(MSG_MORE)
#define FLAG_MORE MSG_MORE
#else
#define FLAG_MORE 0
#endif //MSG_MORE
#if defined(MSG_DONTWAIT)
#define FLAG_DONTWAIT MSG_DONTWAIT
#else
#define FLAG_DONTWAIT 0
#endif //MSG_DONTWAIT
//默认的socket flags:不触发SIGPIPE,非阻塞发送
#define SOCKET_DEFAULE_FLAGS (FLAG_NOSIGNAL | FLAG_DONTWAIT )
//发送超时时间如果在规定时间内一直没有发送数据成功那么将触发onErr事件
#define SEND_TIME_OUT_SEC 10
//错误类型枚举
typedef enum {
Err_success = 0, //成功 success
Err_eof, //eof
Err_timeout, //超时 socket timeout
Err_refused,//连接被拒绝 socket refused
Err_reset,//连接被重置 socket reset
Err_dns,//dns解析失败 dns resolve failed
Err_shutdown,//主动关闭 socket shutdown
Err_other = 0xFF,//其他错误 other error
} ErrCode;
//错误信息类
class SockException : public std::exception {
public:
SockException(ErrCode code = Err_success, const std::string &msg = "", int custom_code = 0) {
_msg = msg;
_code = code;
_custom_code = custom_code;
}
//重置错误
void reset(ErrCode code, const std::string &msg, int custom_code = 0) {
_msg = msg;
_code = code;
_custom_code = custom_code;
}
//错误提示
const char *what() const noexcept override {
return _msg.c_str();
}
//错误代码
ErrCode getErrCode() const {
return _code;
}
//用户自定义错误代码
int getCustomCode() const {
return _custom_code;
}
//判断是否真的有错
operator bool() const {
return _code != Err_success;
}
private:
ErrCode _code;
int _custom_code = 0;
std::string _msg;
};
//std::cout等输出流可以直接输出SockException对象
std::ostream &operator<<(std::ostream &ost, const SockException &err);
class SockNum {
public:
using Ptr = std::shared_ptr<SockNum>;
typedef enum {
Sock_Invalid = -1,
Sock_TCP = 0,
Sock_UDP = 1,
Sock_TCP_Server = 2
} SockType;
SockNum(int fd, SockType type) {
_fd = fd;
_type = type;
}
~SockNum() {
#if defined (OS_IPHONE)
unsetSocketOfIOS(_fd);
#endif //OS_IPHONE
// 停止socket收发能力
#if defined(_WIN32)
::shutdown(_fd, SD_BOTH);
#else
::shutdown(_fd, SHUT_RDWR);
#endif
close(_fd);
}
int rawFd() const {
return _fd;
}
SockType type() {
return _type;
}
void setConnected() {
#if defined (OS_IPHONE)
setSocketOfIOS(_fd);
#endif //OS_IPHONE
}
#if defined (OS_IPHONE)
private:
void *readStream=nullptr;
void *writeStream=nullptr;
bool setSocketOfIOS(int socket);
void unsetSocketOfIOS(int socket);
#endif //OS_IPHONE
private:
int _fd;
SockType _type;
};
//socket 文件描述符的包装
//在析构时自动溢出监听并close套接字
//防止描述符溢出
class SockFD : public noncopyable {
public:
using Ptr = std::shared_ptr<SockFD>;
/**
* fd对象
* @param num int数字
* @param poller
*/
SockFD(SockNum::Ptr num, const EventPoller::Ptr &poller) {
_num = std::move(num);
_poller = poller;
}
/**
* fd对象
* @param that
* @param poller
*/
SockFD(const SockFD &that, const EventPoller::Ptr &poller) {
_num = that._num;
_poller = poller;
if (_poller == that._poller) {
throw std::invalid_argument("Copy a SockFD with same poller");
}
}
~SockFD() { delEvent(); }
void delEvent() {
if (_poller) {
auto num = _num;
// 移除io事件成功后再close fd
_poller->delEvent(num->rawFd(), [num](bool) {});
_poller = nullptr;
}
}
void setConnected() {
_num->setConnected();
}
int rawFd() const {
return _num->rawFd();
}
const SockNum::Ptr& sockNum() const {
return _num;
}
SockNum::SockType type() {
return _num->type();
}
const EventPoller::Ptr& getPoller() const {
return _poller;
}
private:
SockNum::Ptr _num;
EventPoller::Ptr _poller;
};
template<class Mtx = std::recursive_mutex>
class MutexWrapper {
public:
MutexWrapper(bool enable) {
_enable = enable;
}
~MutexWrapper() = default;
inline void lock() {
if (_enable) {
_mtx.lock();
}
}
inline void unlock() {
if (_enable) {
_mtx.unlock();
}
}
private:
bool _enable;
Mtx _mtx;
};
class SockInfo {
public:
SockInfo() = default;
virtual ~SockInfo() = default;
//获取本机ip
virtual std::string get_local_ip() = 0;
//获取本机端口号
virtual uint16_t get_local_port() = 0;
//获取对方ip
virtual std::string get_peer_ip() = 0;
//获取对方端口号
virtual uint16_t get_peer_port() = 0;
//获取标识符
virtual std::string getIdentifier() const { return ""; }
};
#define TraceP(ptr) TraceL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") "
#define DebugP(ptr) DebugL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") "
#define InfoP(ptr) InfoL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") "
#define WarnP(ptr) WarnL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") "
#define ErrorP(ptr) ErrorL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") "
//异步IO Socket对象包括tcp客户端、服务器和udp套接字
class Socket : public std::enable_shared_from_this<Socket>, public noncopyable, public SockInfo {
public:
using Ptr = std::shared_ptr<Socket>;
//接收数据回调
using onReadCB = std::function<void(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len)>;
//发生错误回调
using onErrCB = std::function<void(const SockException &err)>;
//tcp监听接收到连接请求
using onAcceptCB = std::function<void(Socket::Ptr &sock, std::shared_ptr<void> &complete)>;
//socket发送缓存清空事件返回true代表下次继续监听该事件否则停止
using onFlush = std::function<bool()>;
//在接收到连接请求前拦截Socket默认生成方式
using onCreateSocket = std::function<Ptr(const EventPoller::Ptr &poller)>;
//发送buffer成功与否回调
using onSendResult = BufferList::SendResult;
/**
* socket对象
* @param poller poller线程
* @param enable_mutex (线)
*/
static Ptr createSocket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true);
~Socket() override;
/**
* tcp客户端并异步连接服务器
* @param url ip或域名
* @param port
* @param con_cb
* @param timeout_sec
* @param local_ip ip
* @param local_port
*/
void connect(const std::string &url, uint16_t port, const onErrCB &con_cb, float timeout_sec = 5, const std::string &local_ip = "::", uint16_t local_port = 0);
/**
* tcp监听服务器
* @param port 0
* @param local_ip ip
* @param backlog tcp最大积压数
* @return
*/
bool listen(uint16_t port, const std::string &local_ip = "::", int backlog = 1024);
/**
* udp套接字,udp是无连接的
* @param port 0
* @param local_ip ip
* @return
*/
bool bindUdpSock(uint16_t port, const std::string &local_ip = "::", bool enable_reuse = true);
/**
* fdclose fd
* fd为NoBlocked,NoSigpipe,CloExec
* 使SockUtil进行设置
*/
bool fromSock(int fd, SockNum::SockType type);
/**
* Socket克隆
* socket可以被多个poller对象监听Socket归属线程的迁移
* @param other socket对象
* @return
*/
bool cloneSocket(const Socket &other);
////////////设置事件回调////////////
/**
* ,tcp或udp客户端有效
* @param cb
*/
void setOnRead(onReadCB cb);
/**
* (eof等)
* @param cb
*/
void setOnErr(onErrCB cb);
/**
* tcp监听接收到连接回调
* @param cb
*/
void setOnAccept(onAcceptCB cb);
/**
* socket写缓存清空事件回调
*
* @param cb
*/
void setOnFlush(onFlush cb);
/**
* accept时socket构造事件回调
* @param cb
*/
void setOnBeforeAccept(onCreateSocket cb);
/**
* buffer结果回调
* @param cb
*/
void setOnSendResult(onSendResult cb);
////////////发送数据相关接口////////////
/**
*
* @param buf
* @param size
* @param addr
* @param addr_len
* @param try_flush socket
* @return -1(socket无效)00
*/
ssize_t send(const char *buf, size_t size = 0, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true);
/**
* string
*/
ssize_t send(std::string buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true);
/**
* Buffer对象Socket对象发送数据的统一出口
* socket对象发送数据的统一出口
*/
ssize_t send(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true);
/**
* socket
* @return -1(socket无效或者发送超时)0?
*/
int flushAll();
/**
* socket且触发onErr回调onErr回调将在poller线程中进行
* @param err
* @return onErr回调
*/
bool emitErr(const SockException &err) noexcept;
/**
*
* @param enabled
*/
void enableRecv(bool enabled);
/**
* close操作(Socket对象会管理其生命周期)
* @return
*/
int rawFD() const;
/**
* tcp客户端是否处于连接状态
* Sock_TCP类型socket
*/
bool alive() const;
/**
* socket类型
*/
SockNum::SockType sockType() const;
/**
* ;10
* @param second
*/
void setSendTimeOutSecond(uint32_t second);
/**
* true
* @return
*/
bool isSocketBusy() const;
/**
* poller线程对象
* @return poller线程对象
*/
const EventPoller::Ptr &getPoller() const;
/**
* udp
* @param dst_addr
* @param addr_len
* @param soft_bind udp connect接口sendto函数
* @return
*/
bool bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len = 0, bool soft_bind = false);
/**
* flags
* @param flags flag
*/
void setSendFlags(int flags = SOCKET_DEFAULE_FLAGS);
/**
*
* @param close_fd fd还是只移除io事件监听
*/
void closeSock(bool close_fd = true);
/**
* ()
*/
size_t getSendBufferCount();
/**
* socket发送缓存清空至今的毫秒数,
*/
uint64_t elapsedTimeAfterFlushed();
/**
* bytes/s
*/
int getRecvSpeed();
/**
* bytes/s
*/
int getSendSpeed();
////////////SockInfo override////////////
std::string get_local_ip() override;
uint16_t get_local_port() override;
std::string get_peer_ip() override;
uint16_t get_peer_port() override;
std::string getIdentifier() const override;
private:
Socket(EventPoller::Ptr poller, bool enable_mutex = true);
void setSock(SockNum::Ptr sock);
int onAccept(const SockNum::Ptr &sock, int event) noexcept;
ssize_t onRead(const SockNum::Ptr &sock, const BufferRaw::Ptr &buffer) noexcept;
void onWriteAble(const SockNum::Ptr &sock);
void onConnected(const SockNum::Ptr &sock, const onErrCB &cb);
void onFlushed();
void startWriteAbleEvent(const SockNum::Ptr &sock);
void stopWriteAbleEvent(const SockNum::Ptr &sock);
bool flushData(const SockNum::Ptr &sock, bool poller_thread);
bool attachEvent(const SockNum::Ptr &sock);
ssize_t send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush = true);
void connect_l(const std::string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const std::string &local_ip, uint16_t local_port);
bool fromSock_l(SockNum::Ptr sock);
private:
//send socket时的flag
int _sock_flags = SOCKET_DEFAULE_FLAGS;
//最大发送缓存,单位毫秒,距上次发送缓存清空时间不能超过该参数
uint32_t _max_send_buffer_ms = SEND_TIME_OUT_SEC * 1000;
//控制是否接收监听socket可读事件关闭后可用于流量控制
std::atomic<bool> _enable_recv {true};
//标记该socket是否可写socket写缓存满了就不可写
std::atomic<bool> _sendable {true};
//是否已经触发err回调了
bool _err_emit = false;
//是否启用网速统计
bool _enable_speed = false;
// udp发送目标地址
std::shared_ptr<struct sockaddr_storage> _udp_send_dst;
//接收速率统计
BytesSpeed _recv_speed;
//发送速率统计
BytesSpeed _send_speed;
//tcp连接超时定时器
Timer::Ptr _con_timer;
//tcp连接结果回调对象
std::shared_ptr<void> _async_con_cb;
//记录上次发送缓存(包括socket写缓存、应用层缓存)清空的计时器
Ticker _send_flush_ticker;
//socket fd的抽象类
SockFD::Ptr _sock_fd;
//本socket绑定的poller线程事件触发于此线程
EventPoller::Ptr _poller;
//跨线程访问_sock_fd时需要上锁
mutable MutexWrapper<std::recursive_mutex> _mtx_sock_fd;
//socket异常事件(比如说断开)
onErrCB _on_err;
//收到数据事件
onReadCB _on_read;
//socket缓存清空事件(可用于发送流速控制)
onFlush _on_flush;
//tcp监听收到accept请求事件
onAcceptCB _on_accept;
//tcp监听收到accept请求自定义创建peer Socket事件(可以控制子Socket绑定到其他poller线程)
onCreateSocket _on_before_accept;
//设置上述回调函数的锁
MutexWrapper<std::recursive_mutex> _mtx_event;
//一级发送缓存, socket可写时会把一级缓存批量送入到二级缓存
List<std::pair<Buffer::Ptr, bool> > _send_buf_waiting;
//一级发送缓存锁
MutexWrapper<std::recursive_mutex> _mtx_send_buf_waiting;
//二级发送缓存, socket可写时会把二级缓存批量写入到socket
List<BufferList::Ptr> _send_buf_sending;
//二级发送缓存锁
MutexWrapper<std::recursive_mutex> _mtx_send_buf_sending;
//发送buffer结果回调
BufferList::SendResult _send_result;
//对象个数统计
ObjectStatistic<Socket> _statistic;
//链接缓存地址,防止tcp reset 导致无法获取对端的地址
struct sockaddr_storage _local_addr;
struct sockaddr_storage _peer_addr;
};
class SockSender {
public:
SockSender() = default;
virtual ~SockSender() = default;
virtual ssize_t send(Buffer::Ptr buf) = 0;
virtual void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) = 0;
//发送char *
SockSender &operator << (const char *buf);
//发送字符串
SockSender &operator << (std::string buf);
//发送Buffer对象
SockSender &operator << (Buffer::Ptr buf);
//发送其他类型是数据
template<typename T>
SockSender &operator << (T &&buf) {
std::ostringstream ss;
ss << std::forward<T>(buf);
send(ss.str());
return *this;
}
ssize_t send(std::string buf);
ssize_t send(const char *buf, size_t size = 0);
};
//Socket对象的包装类
class SocketHelper : public SockSender, public SockInfo, public TaskExecutorInterface, public std::enable_shared_from_this<SocketHelper> {
public:
using Ptr = std::shared_ptr<SocketHelper>;
SocketHelper(const Socket::Ptr &sock);
~SocketHelper() override = default;
///////////////////// Socket util std::functions /////////////////////
/**
* poller线程
*/
const EventPoller::Ptr& getPoller() const;
/**
* ,
* @param try_flush
*/
void setSendFlushFlag(bool try_flush);
/**
* socket发送flags
* @param flags socket发送flags
*/
void setSendFlags(int flags);
/**
* true
*/
bool isSocketBusy() const;
/**
* Socket创建器Socket创建方式
* @param cb
*/
void setOnCreateSocket(Socket::onCreateSocket cb);
/**
* socket对象
*/
Socket::Ptr createSocket();
/**
* socket对象
*/
const Socket::Ptr &getSock() const;
/**
* socket
* @return -1(socket无效或者发送超时)0?
*/
int flushAll();
/**
* ssl加密
*/
virtual bool overSsl() const { return false; }
///////////////////// SockInfo override /////////////////////
std::string get_local_ip() override;
uint16_t get_local_port() override;
std::string get_peer_ip() override;
uint16_t get_peer_port() override;
///////////////////// TaskExecutorInterface override /////////////////////
/**
* poller线程执行
* @param task
* @param may_sync
*/
Task::Ptr async(TaskIn task, bool may_sync = true) override;
Task::Ptr async_first(TaskIn task, bool may_sync = true) override;
///////////////////// SockSender override /////////////////////
/**
* 使 SockSender send重载函数
*/
using SockSender::send;
/**
*
*/
ssize_t send(Buffer::Ptr buf) override;
/**
* onErr事件
*/
void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override;
/**
* 线 Server onError
* @param ex onError
*/
void safeShutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown"));
///////////////////// event functions /////////////////////
/**
*
* @param buf 使,使
*/
virtual void onRecv(const Buffer::Ptr &buf) = 0;
/**
* eof Server
* ,
* @param err
*/
virtual void onError(const SockException &err) = 0;
/**
*
*/
virtual void onFlush() {}
/**
* ,
*/
virtual void onManager() = 0;
protected:
void setPoller(const EventPoller::Ptr &poller);
void setSock(const Socket::Ptr &sock);
private:
bool _try_flush = true;
Socket::Ptr _sock;
EventPoller::Ptr _poller;
Socket::onCreateSocket _on_create_socket;
};
} // namespace toolkit
#endif /* NETWORK_SOCKET_H */

View File

@ -0,0 +1,176 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef NETWORK_TCPCLIENT_H
#define NETWORK_TCPCLIENT_H
#include <memory>
#include "Socket.h"
#include "Util/SSLBox.h"
namespace toolkit {
//Tcp客户端Socket对象默认开始互斥锁
class TcpClient : public SocketHelper {
public:
using Ptr = std::shared_ptr<TcpClient>;
TcpClient(const EventPoller::Ptr &poller = nullptr);
~TcpClient() override;
/**
* tcp服务器
* @param url ip或域名
* @param port
* @param timeout_sec ,
* @param local_port
*/
virtual void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0);
/**
* tcp服务器
* @param url ip或域名
* @proxy_host ip
* @proxy_port
* @param timeout_sec ,
* @param local_port
*/
virtual void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0){};
/**
*
* @param ex onErr事件时的参数
*/
void shutdown(const SockException &ex = SockException(Err_shutdown, "self shutdown")) override;
/**
* truefalse
*/
virtual bool alive() const;
/**
* ,使
* @param local_ip ip
*/
virtual void setNetAdapter(const std::string &local_ip);
/**
*
*/
std::string getIdentifier() const override;
protected:
/**
*
* @param ex
*/
virtual void onConnect(const SockException &ex) = 0;
/**
* tcp连接成功后每2秒触发一次该事件
*/
void onManager() override {}
private:
void onSockConnect(const SockException &ex);
private:
mutable std::string _id;
std::string _net_adapter = "::";
std::shared_ptr<Timer> _timer;
//对象个数统计
ObjectStatistic<TcpClient> _statistic;
};
//用于实现TLS客户端的模板对象
template<typename TcpClientType>
class TcpClientWithSSL : public TcpClientType {
public:
using Ptr = std::shared_ptr<TcpClientWithSSL>;
template<typename ...ArgsType>
TcpClientWithSSL(ArgsType &&...args):TcpClientType(std::forward<ArgsType>(args)...) {}
~TcpClientWithSSL() override {
if (_ssl_box) {
_ssl_box->flush();
}
}
void onRecv(const Buffer::Ptr &buf) override {
if (_ssl_box) {
_ssl_box->onRecv(buf);
} else {
TcpClientType::onRecv(buf);
}
}
// 使能其他未被重写的send函数
using TcpClientType::send;
ssize_t send(Buffer::Ptr buf) override {
if (_ssl_box) {
auto size = buf->size();
_ssl_box->onSend(std::move(buf));
return size;
}
return TcpClientType::send(std::move(buf));
}
//添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug
inline void public_onRecv(const Buffer::Ptr &buf) {
TcpClientType::onRecv(buf);
}
inline void public_send(const Buffer::Ptr &buf) {
TcpClientType::send(buf);
}
void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0) override {
_host = url;
TcpClientType::startConnect(url, port, timeout_sec, local_port);
}
void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0) override {
_host = url;
TcpClientType::startConnect(proxy_host, proxy_port, timeout_sec, local_port);
}
bool overSsl() const override { return (bool)_ssl_box; }
protected:
void onConnect(const SockException &ex) override {
if (!ex) {
_ssl_box = std::make_shared<SSL_Box>(false);
_ssl_box->setOnDecData([this](const Buffer::Ptr &buf) {
public_onRecv(buf);
});
_ssl_box->setOnEncData([this](const Buffer::Ptr &buf) {
public_send(buf);
});
if (!isIP(_host.data())) {
//设置ssl域名
_ssl_box->setHost(_host.data());
}
}
TcpClientType::onConnect(ex);
}
/**
* ssl, 302http与https的转换
*/
void setDoNotUseSSL() {
_ssl_box.reset();
}
private:
std::string _host;
std::shared_ptr<SSL_Box> _ssl_box;
};
} /* namespace toolkit */
#endif /* NETWORK_TCPCLIENT_H */

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TCPSERVER_TCPSERVER_H
#define TCPSERVER_TCPSERVER_H
#include <memory>
#include <functional>
#include <unordered_map>
#include "Server.h"
#include "Session.h"
#include "Poller/Timer.h"
#include "Util/util.h"
namespace toolkit {
//TCP服务器可配置的配置通过Session::attachServer方法传递给会话对象
class TcpServer : public Server {
public:
using Ptr = std::shared_ptr<TcpServer>;
/**
* tcp服务器listen fd的accept事件会加入到所有的poller线程中监听
* TcpServer::start函数时TcpServer对象
* TcpServer对象通过Socket对象克隆的方式在多个poller线程中监听同一个listen fd
* TCP服务器将会通过抢占式accept的方式把客户端均匀的分布到不同的poller线程
*
*/
explicit TcpServer(const EventPoller::Ptr &poller = nullptr);
~TcpServer() override;
/**
* @brief tcp server
* @param port 0
* @param host ip
* @param backlog tcp listen backlog
*/
template<typename SessionType>
void start(uint16_t port, const std::string &host = "::", uint32_t backlog = 1024) {
static std::string cls_name = toolkit::demangle(typeid(SessionType).name());
//Session创建器通过它创建不同类型的服务器
_session_alloc = [](const TcpServer::Ptr &server, const Socket::Ptr &sock) {
auto session = std::shared_ptr<SessionType>(new SessionType(sock), [](SessionType * ptr) {
TraceP(static_cast<Session *>(ptr)) << "~" << cls_name;
delete ptr;
});
TraceP(static_cast<Session *>(session.get())) << cls_name;
session->setOnCreateSocket(server->_on_create_socket);
return std::make_shared<SessionHelper>(server, std::move(session), cls_name);
};
start_l(port, host, backlog);
}
/**
* @brief ,
*/
uint16_t getPort();
/**
* @brief socket构建行为
*/
void setOnCreateSocket(Socket::onCreateSocket cb);
/**
* socket对象创建Session对象
* socket归属poller线程执行本函数
*/
Session::Ptr createSession(const Socket::Ptr &socket);
protected:
virtual void cloneFrom(const TcpServer &that);
virtual TcpServer::Ptr onCreatServer(const EventPoller::Ptr &poller);
virtual Session::Ptr onAcceptConnection(const Socket::Ptr &sock);
virtual Socket::Ptr onBeforeAcceptConnection(const EventPoller::Ptr &poller);
private:
void onManagerSession();
Socket::Ptr createSocket(const EventPoller::Ptr &poller);
void start_l(uint16_t port, const std::string &host, uint32_t backlog);
Ptr getServer(const EventPoller *) const;
void setupEvent();
private:
bool _is_on_manager = false;
bool _main_server = true;
std::weak_ptr<TcpServer> _parent;
Socket::Ptr _socket;
std::shared_ptr<Timer> _timer;
Socket::onCreateSocket _on_create_socket;
std::unordered_map<SessionHelper *, SessionHelper::Ptr> _session_map;
std::function<SessionHelper::Ptr(const TcpServer::Ptr &server, const Socket::Ptr &)> _session_alloc;
std::unordered_map<const EventPoller *, Ptr> _cloned_server;
//对象个数统计
ObjectStatistic<TcpServer> _statistic;
};
} /* namespace toolkit */
#endif /* TCPSERVER_TCPSERVER_H */

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2021 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TOOLKIT_NETWORK_UDPSERVER_H
#define TOOLKIT_NETWORK_UDPSERVER_H
#include "Server.h"
#include "Session.h"
namespace toolkit {
class UdpServer : public Server {
public:
using Ptr = std::shared_ptr<UdpServer>;
using PeerIdType = std::string;
using onCreateSocket = std::function<Socket::Ptr(const EventPoller::Ptr &, const Buffer::Ptr &, struct sockaddr *, int)>;
explicit UdpServer(const EventPoller::Ptr &poller = nullptr);
~UdpServer() override;
/**
* @brief
*/
template<typename SessionType>
void start(uint16_t port, const std::string &host = "::") {
static std::string cls_name = toolkit::demangle(typeid(SessionType).name());
// Session 创建器, 通过它创建不同类型的服务器
_session_alloc = [](const UdpServer::Ptr &server, const Socket::Ptr &sock) {
auto session = std::shared_ptr<SessionType>(new SessionType(sock), [](SessionType * ptr) {
TraceP(static_cast<Session *>(ptr)) << "~" << cls_name;
delete ptr;
});
TraceP(static_cast<Session *>(session.get())) << cls_name;
auto sock_creator = server->_on_create_socket;
session->setOnCreateSocket([sock_creator](const EventPoller::Ptr &poller) {
return sock_creator(poller, nullptr, nullptr, 0);
});
return std::make_shared<SessionHelper>(server, std::move(session), cls_name);
};
start_l(port, host);
}
/**
* @brief ,
*/
uint16_t getPort();
/**
* @brief socket构建行为
*/
void setOnCreateSocket(onCreateSocket cb);
protected:
virtual Ptr onCreatServer(const EventPoller::Ptr &poller);
virtual void cloneFrom(const UdpServer &that);
private:
/**
* @brief udp server
* @param port 0
* @param host ip
*/
void start_l(uint16_t port, const std::string &host = "::");
/**
* @brief Session, UDP
*/
void onManagerSession();
void onRead(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len);
/**
* @brief ,server fdpeer fd
* @param is_server_fd server fd
* @param id id
* @param buf
* @param addr
* @param addr_len
*/
void onRead_l(bool is_server_fd, const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len);
/**
* @brief
*/
SessionHelper::Ptr getOrCreateSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len, bool &is_new);
/**
* @brief ,
*/
SessionHelper::Ptr createSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len);
/**
* @brief socket
*/
Socket::Ptr createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf = nullptr, struct sockaddr *addr = nullptr, int addr_len = 0);
void setupEvent();
private:
bool _cloned = false;
Socket::Ptr _socket;
std::shared_ptr<Timer> _timer;
onCreateSocket _on_create_socket;
//cloned server共享主server的session map防止数据在不同server间漂移
std::shared_ptr<std::recursive_mutex> _session_mutex;
std::shared_ptr<std::unordered_map<PeerIdType, SessionHelper::Ptr> > _session_map;
//主server持有cloned server的引用
std::unordered_map<EventPoller *, Ptr> _cloned_server;
std::function<SessionHelper::Ptr(const UdpServer::Ptr &, const Socket::Ptr &)> _session_alloc;
// 对象个数统计
ObjectStatistic<UdpServer> _statistic;
};
} // namespace toolkit
#endif // TOOLKIT_NETWORK_UDPSERVER_H

View File

@ -0,0 +1,354 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef NETWORK_SOCKUTIL_H
#define NETWORK_SOCKUTIL_H
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#pragma comment (lib, "Ws2_32.lib")
#pragma comment(lib,"Iphlpapi.lib")
#else
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif // defined(_WIN32)
#include <cstring>
#include <cstdint>
#include <map>
#include <vector>
#include <string>
namespace toolkit {
#if defined(_WIN32)
#ifndef socklen_t
#define socklen_t int
#endif //!socklen_t
int ioctl(int fd, long cmd, u_long *ptr);
int close(int fd);
#endif // defined(_WIN32)
#if !defined(SOCKET_DEFAULT_BUF_SIZE)
#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024)
#else
#if SOCKET_DEFAULT_BUF_SIZE == 0 && !defined(__linux__)
// just for linux, because in some high-throughput environments,
// kernel control is more efficient and reasonable than program
// settings. For example, refer to cloudflare's blog
#undef SOCKET_DEFAULT_BUF_SIZE
#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024)
#endif
#endif
#define TCP_KEEPALIVE_INTERVAL 30
#define TCP_KEEPALIVE_PROBE_TIMES 9
#define TCP_KEEPALIVE_TIME 120
//套接字工具类封装了socket、网络的一些基本操作
class SockUtil {
public:
/**
* tcp客户端套接字并连接服务器
* @param host ip或域名
* @param port
* @param async
* @param local_ip ip
* @param local_port
* @return -1socket fd号
*/
static int connect(const char *host, uint16_t port, bool async = true, const char *local_ip = "::", uint16_t local_port = 0);
/**
* tcp监听套接字
* @param port
* @param local_ip ip
* @param back_log accept列队长度
* @return -1socket fd号
*/
static int listen(const uint16_t port, const char *local_ip = "::", int back_log = 1024);
/**
* udp套接字
* @param port
* @param local_ip ip
* @param enable_reuse bind端口
* @return -1socket fd号
*/
static int bindUdpSock(const uint16_t port, const char *local_ip = "::", bool enable_reuse = true);
/**
* @brief sock
* @param sock, socket fd
* @return 0 , -1
*/
static int dissolveUdpSock(int sock);
/**
* TCP_NODELAYTCP交互延时
* @param fd socket fd号
* @param on
* @return 0-1
*/
static int setNoDelay(int fd, bool on = true);
/**
* socket不触发SIG_PIPE信号(mac有效)
* @param fd socket fd号
* @return 0-1
*/
static int setNoSigpipe(int fd);
/**
* socket是否阻塞
* @param fd socket fd号
* @param noblock
* @return 0-1
*/
static int setNoBlocked(int fd, bool noblock = true);
/**
* socket接收缓存8K左右
*
* @param fd socket fd号
* @param size
* @return 0-1
*/
static int setRecvBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE);
/**
* socket接收缓存8K左右
*
* @param fd socket fd号
* @param size
* @return 0-1
*/
static int setSendBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE);
/**
* (TIME_WAITE状态)
* @param fd socket fd号
* @param on
* @return 0-1
*/
static int setReuseable(int fd, bool on = true, bool reuse_port = true);
/**
* udp广播信息
* @param fd socket fd号
* @param on
* @return 0-1
*/
static int setBroadcast(int fd, bool on = true);
/**
* TCP KeepAlive特性
* @param fd socket fd号
* @param on
* @param idle keepalive空闲时间
* @param interval keepalive探测时间间隔
* @param times keepalive探测次数
* @return 0-1
*/
static int setKeepAlive(int fd, bool on = true, int interval = TCP_KEEPALIVE_INTERVAL, int idle = TCP_KEEPALIVE_TIME, int times = TCP_KEEPALIVE_PROBE_TIMES);
/**
* FD_CLOEXEC特性()
* @param fd fd号socket
* @param on
* @return 0-1
*/
static int setCloExec(int fd, bool on = true);
/**
* SO_LINGER特性
* @param sock socket fd号
* @param second socket超时时间
* @return 0-1
*/
static int setCloseWait(int sock, int second = 0);
/**
* dns解析
* @param host ip
* @param port
* @param addr sockaddr结构体
* @return
*/
static bool getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, int ai_family = AF_INET,
int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60);
/**
* ttl
* @param sock socket fd号
* @param ttl ttl值
* @return 0-1
*/
static int setMultiTTL(int sock, uint8_t ttl = 64);
/**
*
* @param sock socket fd号
* @param local_ip ip
* @return 0-1
*/
static int setMultiIF(int sock, const char *local_ip);
/**
*
* @param fd socket fd号
* @param acc
* @return 0-1
*/
static int setMultiLOOP(int fd, bool acc = false);
/**
*
* @param fd socket fd号
* @param addr
* @param local_ip ip
* @return 0-1
*/
static int joinMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0");
/**
* 退
* @param fd socket fd号
* @param addr
* @param local_ip ip
* @return 0-1
*/
static int leaveMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0");
/**
*
* @param sock socket fd号
* @param addr
* @param src_ip
* @param local_ip ip
* @return 0-1
*/
static int joinMultiAddrFilter(int sock, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0");
/**
* 退
* @param fd socket fd号
* @param addr
* @param src_ip
* @param local_ip ip
* @return 0-1
*/
static int leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0");
/**
* socket当前发生的错误
* @param fd socket fd号
* @return
*/
static int getSockError(int fd);
/**
*
* @return vector<map<ip:name> >
*/
static std::vector<std::map<std::string, std::string>> getInterfaceList();
/**
* ip
*/
static std::string get_local_ip();
/**
* socket绑定的本地ip
* @param sock socket fd号
*/
static std::string get_local_ip(int sock);
/**
* socket绑定的本地端口
* @param sock socket fd号
*/
static uint16_t get_local_port(int sock);
/**
* socket绑定的远端ip
* @param sock socket fd号
*/
static std::string get_peer_ip(int sock);
/**
* socket绑定的远端端口
* @param sock socket fd号
*/
static uint16_t get_peer_port(int sock);
static bool support_ipv6();
/**
* 线in_addr转ip字符串
*/
static std::string inet_ntoa(const struct in_addr &addr);
static std::string inet_ntoa(const struct in6_addr &addr);
static std::string inet_ntoa(const struct sockaddr *addr);
static uint16_t inet_port(const struct sockaddr *addr);
static struct sockaddr_storage make_sockaddr(const char *ip, uint16_t port);
static socklen_t get_sock_len(const struct sockaddr *addr);
static bool get_sock_local_addr(int fd, struct sockaddr_storage &addr);
static bool get_sock_peer_addr(int fd, struct sockaddr_storage &addr);
/**
* ip
* @param if_name
*/
static std::string get_ifr_ip(const char *if_name);
/**
*
* @param local_op ip
*/
static std::string get_ifr_name(const char *local_op);
/**
*
* @param if_name
*/
static std::string get_ifr_mask(const char *if_name);
/**
* 广
* @param if_name
*/
static std::string get_ifr_brdaddr(const char *if_name);
/**
* ip是否为同一网段
* @param src_ip ip
* @param dts_ip ip
*/
static bool in_same_lan(const char *src_ip, const char *dts_ip);
/**
* ipv4地址
*/
static bool is_ipv4(const char *str);
/**
* ipv6地址
*/
static bool is_ipv6(const char *str);
};
} // namespace toolkit
#endif // !NETWORK_SOCKUTIL_H

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_PLAYER_MEDIAPLAYER_H_
#define SRC_PLAYER_MEDIAPLAYER_H_
#include <memory>
#include <string>
#include "PlayerBase.h"
namespace mediakit {
class MediaPlayer : public PlayerImp<PlayerBase, PlayerBase> {
public:
using Ptr = std::shared_ptr<MediaPlayer>;
MediaPlayer(const toolkit::EventPoller::Ptr &poller = nullptr);
void play(const std::string &url) override;
toolkit::EventPoller::Ptr getPoller();
void setOnCreateSocket(toolkit::Socket::onCreateSocket cb);
private:
toolkit::EventPoller::Ptr _poller;
toolkit::Socket::onCreateSocket _on_create_socket;
};
} /* namespace mediakit */
#endif /* SRC_PLAYER_MEDIAPLAYER_H_ */

View File

@ -0,0 +1,238 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_PLAYER_PLAYERBASE_H_
#define SRC_PLAYER_PLAYERBASE_H_
#include <map>
#include <memory>
#include <string>
#include <functional>
#include "Network/Socket.h"
#include "Util/mini.h"
#include "Common/MediaSource.h"
#include "Common/MediaSink.h"
#include "Extension/Frame.h"
#include "Extension/Track.h"
namespace mediakit {
class PlayerBase : public TrackSource, public toolkit::mINI {
public:
using Ptr = std::shared_ptr<PlayerBase>;
using Event = std::function<void(const toolkit::SockException &ex)>;
static Ptr createPlayer(const toolkit::EventPoller::Ptr &poller, const std::string &strUrl);
PlayerBase();
/**
*
* @param url urlrtsp/rtmp
*/
virtual void play(const std::string &url) {};
/**
*
* @param flag true:false:
*/
virtual void pause(bool flag) {};
/**
*
*/
virtual float getDuration() const { return 0; };
/**
*
* @param speed 1.0 2.0 0.5
*/
virtual void speed(float speed) {};
/**
*
*/
virtual void teardown() {};
/**
* 0.0 ~ 1.0
*/
virtual float getProgress() const { return 0; };
/**
* pos
*/
virtual uint32_t getProgressPos() const { return 0; };
/**
*
* @param progress 0.0 ~ 1.0
*/
virtual void seekTo(float progress) {};
/**
*
* @param pos
*/
virtual void seekTo(uint32_t pos) {};
/**
* rtsp
* @param type TrackInvalid时为总丢包率
*/
virtual float getPacketLossRate(TrackType type) const { return -1; };
/**
* track
*/
std::vector<Track::Ptr> getTracks(bool ready = true) const override { return std::vector<Track::Ptr>(); };
/**
* MediaSourcertsp/rtmp代理
*/
virtual void setMediaSource(const MediaSource::Ptr &src) = 0;
/**
*
*/
virtual void setOnShutdown(const Event &cb) = 0;
/**
*
*/
virtual void setOnPlayResult(const Event &cb) = 0;
/**
*
*/
virtual void setOnResume(const std::function<void()> &cb) = 0;
protected:
virtual void onResume() = 0;
virtual void onShutdown(const toolkit::SockException &ex) = 0;
virtual void onPlayResult(const toolkit::SockException &ex) = 0;
};
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
using Ptr = std::shared_ptr<PlayerImp>;
template<typename ...ArgsType>
PlayerImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(args)...) {}
void play(const std::string &url) override {
return _delegate ? _delegate->play(url) : Parent::play(url);
}
void pause(bool flag) override {
return _delegate ? _delegate->pause(flag) : Parent::pause(flag);
}
void speed(float speed) override {
return _delegate ? _delegate->speed(speed) : Parent::speed(speed);
}
void teardown() override {
return _delegate ? _delegate->teardown() : Parent::teardown();
}
float getPacketLossRate(TrackType type) const override {
return _delegate ? _delegate->getPacketLossRate(type) : Parent::getPacketLossRate(type);
}
float getDuration() const override {
return _delegate ? _delegate->getDuration() : Parent::getDuration();
}
float getProgress() const override {
return _delegate ? _delegate->getProgress() : Parent::getProgress();
}
uint32_t getProgressPos() const override {
return _delegate ? _delegate->getProgressPos() : Parent::getProgressPos();
}
void seekTo(float progress) override {
return _delegate ? _delegate->seekTo(progress) : Parent::seekTo(progress);
}
void seekTo(uint32_t pos) override {
return _delegate ? _delegate->seekTo(pos) : Parent::seekTo(pos);
}
std::vector<Track::Ptr> getTracks(bool ready = true) const override {
return _delegate ? _delegate->getTracks(ready) : Parent::getTracks(ready);
}
std::shared_ptr<toolkit::SockInfo> getSockInfo() const {
return std::dynamic_pointer_cast<toolkit::SockInfo>(_delegate);
}
void setMediaSource(const MediaSource::Ptr &src) override {
if (_delegate) {
_delegate->setMediaSource(src);
}
_media_src = src;
}
void setOnShutdown(const std::function<void(const toolkit::SockException &)> &cb) override {
if (_delegate) {
_delegate->setOnShutdown(cb);
}
_on_shutdown = cb;
}
void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
if (_delegate) {
_delegate->setOnPlayResult(cb);
}
_on_play_result = cb;
}
void setOnResume(const std::function<void()> &cb) override {
if (_delegate) {
_delegate->setOnResume(cb);
}
_on_resume = cb;
}
protected:
void onShutdown(const toolkit::SockException &ex) override {
if (_on_shutdown) {
_on_shutdown(ex);
_on_shutdown = nullptr;
}
}
void onPlayResult(const toolkit::SockException &ex) override {
if (_on_play_result) {
_on_play_result(ex);
_on_play_result = nullptr;
}
}
void onResume() override {
if (_on_resume) {
_on_resume();
}
}
protected:
std::function<void()> _on_resume;
PlayerBase::Event _on_shutdown;
PlayerBase::Event _on_play_result;
MediaSource::Ptr _media_src;
std::shared_ptr<Delegate> _delegate;
};
} /* namespace mediakit */
#endif /* SRC_PLAYER_PLAYERBASE_H_ */

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_DEVICE_PLAYERPROXY_H_
#define SRC_DEVICE_PLAYERPROXY_H_
#include "Common/MultiMediaSourceMuxer.h"
#include "Player/MediaPlayer.h"
#include "Util/TimeTicker.h"
#include <memory>
namespace mediakit {
struct StreamInfo
{
TrackType codec_type;
std::string codec_name;
int bitrate;
int audio_sample_rate;
int audio_sample_bit;
int audio_channel;
int video_width;
int video_height;
float video_fps;
StreamInfo()
{
codec_type = TrackInvalid;
codec_name = "none";
bitrate = -1;
audio_sample_rate = -1;
audio_channel = -1;
audio_sample_bit = -1;
video_height = -1;
video_width = -1;
video_fps = -1.0;
}
};
struct TranslationInfo
{
std::vector<StreamInfo> stream_info;
int byte_speed;
uint64_t start_time_stamp;
TranslationInfo()
{
byte_speed = -1;
start_time_stamp = 0;
}
};
class PlayerProxy
: public MediaPlayer
, public MediaSourceEvent
, public std::enable_shared_from_this<PlayerProxy> {
public:
using Ptr = std::shared_ptr<PlayerProxy>;
// 如果retry_count<0,则一直重试播放否则重试retry_count次数
// 默认一直重试
PlayerProxy(
const std::string &vhost, const std::string &app, const std::string &stream_id, const ProtocolOption &option, int retry_count = -1,
const toolkit::EventPoller::Ptr &poller = nullptr, int reconnect_delay_min = 2, int reconnect_delay_max = 60, int reconnect_delay_step = 3);
~PlayerProxy() override;
/**
* play结果回调play执行之前有效
* @param cb
*/
void setPlayCallbackOnce(std::function<void(const toolkit::SockException &ex)> cb);
/**
*
* @param cb
*/
void setOnClose(std::function<void(const toolkit::SockException &ex)> cb);
/**
* Set a callback for failed server connection
* @param cb
*/
void setOnDisconnect(std::function<void()> cb);
/**
* Set a callback for a successful connection to the server
* @param cb
*/
void setOnConnect(std::function<void(const TranslationInfo&)> cb);
/**
*
* @param strUrl
*/
void play(const std::string &strUrl) override;
/**
*
*/
int totalReaderCount();
int getStatus();
uint64_t getLiveSecs();
uint64_t getRePullCount();
// Using this only makes sense after a successful connection to the server
TranslationInfo getTranslationInfo();
private:
// MediaSourceEvent override
bool close(MediaSource &sender) override;
int totalReaderCount(MediaSource &sender) override;
MediaOriginType getOriginType(MediaSource &sender) const override;
std::string getOriginUrl(MediaSource &sender) const override;
std::shared_ptr<toolkit::SockInfo> getOriginSock(MediaSource &sender) const override;
float getLossRate(MediaSource &sender, TrackType type) override;
void rePlay(const std::string &strUrl, int iFailedCnt);
void onPlaySuccess();
void setDirectProxy();
void setTranslationInfo();
private:
ProtocolOption _option;
int _retry_count;
int _reconnect_delay_min;
int _reconnect_delay_max;
int _reconnect_delay_step;
MediaTuple _tuple;
std::string _pull_url;
toolkit::Timer::Ptr _timer;
std::function<void()> _on_disconnect;
std::function<void(const TranslationInfo &info)> _on_connect;
std::function<void(const toolkit::SockException &ex)> _on_close;
std::function<void(const toolkit::SockException &ex)> _on_play;
TranslationInfo _transtalion_info;
MultiMediaSourceMuxer::Ptr _muxer;
toolkit::Ticker _live_ticker;
// 0 表示正常 1 表示正在尝试拉流
std::atomic<int> _live_status;
std::atomic<uint64_t> _live_secs;
std::atomic<uint64_t> _repull_count;
};
} /* namespace mediakit */
#endif /* SRC_DEVICE_PLAYERPROXY_H_ */

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef EventPoller_h
#define EventPoller_h
#include <mutex>
#include <thread>
#include <string>
#include <functional>
#include <memory>
#include <unordered_map>
#include "PipeWrap.h"
#include "Util/logger.h"
#include "Util/List.h"
#include "Thread/TaskExecutor.h"
#include "Thread/ThreadPool.h"
#include "Network/Buffer.h"
#if defined(__linux__) || defined(__linux)
#define HAS_EPOLL
#endif //__linux__
namespace toolkit {
class EventPoller : public TaskExecutor, public AnyStorage, public std::enable_shared_from_this<EventPoller> {
public:
friend class TaskExecutorGetterImp;
using Ptr = std::shared_ptr<EventPoller>;
using PollEventCB = std::function<void(int event)>;
using PollCompleteCB = std::function<void(bool success)>;
using DelayTask = TaskCancelableImp<uint64_t(void)>;
typedef enum {
Event_Read = 1 << 0, //读事件
Event_Write = 1 << 1, //写事件
Event_Error = 1 << 2, //错误事件
Event_LT = 1 << 3,//水平触发
} Poll_Event;
~EventPoller();
/**
* EventPollerPool单例中的第一个EventPoller实例
*
* @return
*/
static EventPoller &Instance();
/**
*
* @param fd
* @param event Event_Read | Event_Write
* @param cb functional
* @return -1:0:
*/
int addEvent(int fd, int event, PollEventCB cb);
/**
*
* @param fd
* @param cb functional
* @return -1:0:
*/
int delEvent(int fd, PollCompleteCB cb = nullptr);
/**
*
* @param fd
* @param event Event_Read | Event_Write
* @return -1:0:
*/
int modifyEvent(int fd, int event, PollCompleteCB cb = nullptr);
/**
*
* @param task
* @param may_sync 线线may_sync为true时就是同步执行任务
* @return true
*/
Task::Ptr async(TaskIn task, bool may_sync = true) override;
/**
* async方法
* @param task
* @param may_sync 线线may_sync为true时就是同步执行任务
* @return true
*/
Task::Ptr async_first(TaskIn task, bool may_sync = true) override;
/**
* 线线
* @return 线
*/
bool isCurrentThread();
/**
*
* @param delay_ms
* @param task 0
* @return
*/
DelayTask::Ptr doDelayTask(uint64_t delay_ms, std::function<uint64_t()> task);
/**
* 线Poller实例
*/
static EventPoller::Ptr getCurrentPoller();
/**
* 线socket共享的读缓存
*/
BufferRaw::Ptr getSharedBuffer();
/**
* poller线程id
*/
std::thread::id getThreadId() const;
/**
* 线
*/
const std::string& getThreadName() const;
private:
/**
* EventPollerPool中构造
*/
EventPoller(std::string name);
/**
*
* @param blocked 线
* @param ref_self thread local变量
*/
void runLoop(bool blocked, bool ref_self);
/**
* 线
*/
void onPipeEvent();
/**
* 线
* @param task
* @param may_sync
* @param first
* @return nullptr
*/
Task::Ptr async_l(TaskIn task, bool may_sync = true, bool first = false);
/**
*
* 线
*/
void shutdown();
/**
*
*/
uint64_t flushDelayTask(uint64_t now);
/**
* select或epoll休眠时间
*/
uint64_t getMinDelay();
/**
*
*/
void addEventPipe();
private:
class ExitException : public std::exception {};
private:
//标记loop线程是否退出
bool _exit_flag;
//线程名
std::string _name;
//当前线程下所有socket共享的读缓存
std::weak_ptr<BufferRaw> _shared_buffer;
//执行事件循环的线程
std::thread *_loop_thread = nullptr;
//通知事件循环的线程已启动
semaphore _sem_run_started;
//内部事件管道
PipeWrap _pipe;
//从其他线程切换过来的任务
std::mutex _mtx_task;
List<Task::Ptr> _list_task;
//保持日志可用
Logger::Ptr _logger;
#if defined(HAS_EPOLL)
//epoll相关
int _epoll_fd = -1;
unordered_map<int, std::shared_ptr<PollEventCB> > _event_map;
#else
//select相关
struct Poll_Record {
using Ptr = std::shared_ptr<Poll_Record>;
int fd;
int event;
int attach;
PollEventCB call_back;
};
unordered_map<int, Poll_Record::Ptr> _event_map;
#endif //HAS_EPOLL
unordered_map<int, bool> _event_cache_expired_map;
//定时器相关
std::multimap<uint64_t, DelayTask::Ptr> _delay_task_map;
};
class EventPollerPool : public std::enable_shared_from_this<EventPollerPool>, public TaskExecutorGetterImp {
public:
using Ptr = std::shared_ptr<EventPollerPool>;
static const std::string kOnStarted;
#define EventPollerPoolOnStartedArgs EventPollerPool &pool, size_t &size
~EventPollerPool() = default;
/**
*
* @return
*/
static EventPollerPool &Instance();
/**
* EventPoller个数EventPollerPool单例创建前有效
* thread::hardware_concurrency()EventPoller实例
* @param size EventPoller个数0thread::hardware_concurrency()
*/
static void setPoolSize(size_t size = 0);
/**
* 线cpu亲和性cpu亲和性
*/
static void enableCpuAffinity(bool enable);
/**
*
* @return
*/
EventPoller::Ptr getFirstPoller();
/**
*
* 线线
* 线线
* @param prefer_current_thread 线
*/
EventPoller::Ptr getPoller(bool prefer_current_thread = true);
/**
* getPoller() 线
* Socket对象时线
*
* @param flag 线
*/
void preferCurrentThread(bool flag = true);
private:
EventPollerPool();
private:
bool _prefer_current_thread = true;
};
} // namespace toolkit
#endif /* EventPoller_h */

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef Pipe_h
#define Pipe_h
#include <functional>
#include "PipeWrap.h"
#include "EventPoller.h"
namespace toolkit {
class Pipe {
public:
using onRead = std::function<void(int size, const char *buf)>;
Pipe(const onRead &cb = nullptr, const EventPoller::Ptr &poller = nullptr);
~Pipe();
void send(const char *send, int size = 0);
private:
std::shared_ptr<PipeWrap> _pipe;
EventPoller::Ptr _poller;
};
} // namespace toolkit
#endif /* Pipe_h */

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef PipeWarp_h
#define PipeWarp_h
namespace toolkit {
class PipeWrap {
public:
PipeWrap();
~PipeWrap();
int write(const void *buf, int n);
int read(void *buf, int n);
int readFD() const { return _pipe_fd[0]; }
int writeFD() const { return _pipe_fd[1]; }
void reOpen();
private:
void clearFD();
private:
int _pipe_fd[2] = { -1, -1 };
};
} /* namespace toolkit */
#endif // !PipeWarp_h

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_POLLER_SELECTWRAP_H_
#define SRC_POLLER_SELECTWRAP_H_
#include "Util/util.h"
namespace toolkit {
class FdSet {
public:
FdSet();
~FdSet();
void fdZero();
void fdSet(int fd);
void fdClr(int fd);
bool isSet(int fd);
void *_ptr;
};
int zl_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv);
} /* namespace toolkit */
#endif /* SRC_POLLER_SELECTWRAP_H_ */

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
*
* This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef Timer_h
#define Timer_h
#include <functional>
#include "EventPoller.h"
namespace toolkit {
class Timer {
public:
using Ptr = std::shared_ptr<Timer>;
/**
*
* @param second
* @param cb true表示重复下次任务
* @param poller EventPoller对象nullptr
*/
Timer(float second, const std::function<bool()> &cb, const EventPoller::Ptr &poller);
~Timer();
private:
std::weak_ptr<EventPoller::DelayTask> _tag;
//定时器保持EventPoller的强引用
EventPoller::Ptr _poller;
};
} // namespace toolkit
#endif /* Timer_h */

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_PUSHER_MEDIAPUSHER_H_
#define SRC_PUSHER_MEDIAPUSHER_H_
#include <memory>
#include <string>
#include "PusherBase.h"
namespace mediakit {
class MediaPusher : public PusherImp<PusherBase,PusherBase> {
public:
using Ptr = std::shared_ptr<MediaPusher>;
MediaPusher(const std::string &schema,
const std::string &vhost,
const std::string &app,
const std::string &stream,
const toolkit::EventPoller::Ptr &poller = nullptr);
MediaPusher(const MediaSource::Ptr &src,
const toolkit::EventPoller::Ptr &poller = nullptr);
void publish(const std::string &url) override;
toolkit::EventPoller::Ptr getPoller();
void setOnCreateSocket(toolkit::Socket::onCreateSocket cb);
private:
std::weak_ptr<MediaSource> _src;
toolkit::EventPoller::Ptr _poller;
toolkit::Socket::onCreateSocket _on_create_socket;
};
} /* namespace mediakit */
#endif /* SRC_PUSHER_MEDIAPUSHER_H_ */

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_PUSHER_PUSHERBASE_H_
#define SRC_PUSHER_PUSHERBASE_H_
#include <map>
#include <memory>
#include <string>
#include <functional>
#include "Network/Socket.h"
#include "Util/mini.h"
#include "Common/MediaSource.h"
namespace mediakit {
class PusherBase : public toolkit::mINI {
public:
using Ptr = std::shared_ptr<PusherBase>;
using Event = std::function<void(const toolkit::SockException &ex)>;
static Ptr createPusher(const toolkit::EventPoller::Ptr &poller,
const MediaSource::Ptr &src,
const std::string &strUrl);
PusherBase();
virtual ~PusherBase() = default;
/**
*
* @param strUrl urlrtsp/rtmp
*/
virtual void publish(const std::string &strUrl) {};
/**
*
*/
virtual void teardown() {};
/**
*
*/
virtual void setOnPublished(const Event &cb) = 0;
/**
*
*/
virtual void setOnShutdown(const Event &cb) = 0;
protected:
virtual void onShutdown(const toolkit::SockException &ex) = 0;
virtual void onPublishResult(const toolkit::SockException &ex) = 0;
};
template<typename Parent, typename Delegate>
class PusherImp : public Parent {
public:
using Ptr = std::shared_ptr<PusherImp>;
template<typename ...ArgsType>
PusherImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(args)...) {}
/**
*
* @param url urlrtsp/rtmp
*/
void publish(const std::string &url) override {
return _delegate ? _delegate->publish(url) : Parent::publish(url);
}
/**
*
*/
void teardown() override {
return _delegate ? _delegate->teardown() : Parent::teardown();
}
std::shared_ptr<toolkit::SockInfo> getSockInfo() const {
return std::dynamic_pointer_cast<toolkit::SockInfo>(_delegate);
}
/**
*
*/
void setOnPublished(const PusherBase::Event &cb) override {
if (_delegate) {
_delegate->setOnPublished(cb);
}
_on_publish = cb;
}
/**
*
*/
void setOnShutdown(const PusherBase::Event &cb) override {
if (_delegate) {
_delegate->setOnShutdown(cb);
}
_on_shutdown = cb;
}
protected:
void onShutdown(const toolkit::SockException &ex) override {
if (_on_shutdown) {
_on_shutdown(ex);
_on_shutdown = nullptr;
}
}
void onPublishResult(const toolkit::SockException &ex) override {
if (_on_publish) {
_on_publish(ex);
_on_publish = nullptr;
}
}
protected:
PusherBase::Event _on_shutdown;
PusherBase::Event _on_publish;
std::shared_ptr<Delegate> _delegate;
};
} /* namespace mediakit */
#endif /* SRC_PUSHER_PUSHERBASE_H_ */

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef SRC_DEVICE_PUSHERPROXY_H
#define SRC_DEVICE_PUSHERPROXY_H
#include "Pusher/MediaPusher.h"
#include "Util/TimeTicker.h"
namespace mediakit {
class PusherProxy
: public MediaPusher
, public std::enable_shared_from_this<PusherProxy> {
public:
using Ptr = std::shared_ptr<PusherProxy>;
// 如果retry_count<0,则一直重试播放否则重试retry_count次数
// 默认一直重试创建此对象时候需要外部保证MediaSource存在
PusherProxy(const MediaSource::Ptr &src, int retry_count = -1, const toolkit::EventPoller::Ptr &poller = nullptr);
~PusherProxy() override;
/**
* push结果回调publish执行之前有效
* @param cb
*/
void setPushCallbackOnce(const std::function<void(const toolkit::SockException &ex)> &cb);
/**
*
* @param cb
*/
void setOnClose(const std::function<void(const toolkit::SockException &ex)> &cb);
/**
*
* @param dstUrl
*/
void publish(const std::string &dstUrl) override;
int getStatus();
uint64_t getLiveSecs();
uint64_t getRePublishCount();
private:
// 重推逻辑函数
void rePublish(const std::string &dstUrl, int iFailedCnt);
private:
int _retry_count;
toolkit::Timer::Ptr _timer;
toolkit::Ticker _live_ticker;
// 0 表示正常 1 表示正在尝试推流
std::atomic<int> _live_status;
std::atomic<uint64_t> _live_secs;
std::atomic<uint64_t> _republish_count;
std::weak_ptr<MediaSource> _weak_src;
std::function<void(const toolkit::SockException &ex)> _on_close;
std::function<void(const toolkit::SockException &ex)> _on_publish;
};
} /* namespace mediakit */
#endif // SRC_DEVICE_PUSHERPROXY_H

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