技术选型

RTSP转RTMP推送到流媒体服务器,说起来技术实现不难,简单来说,获取RTSP流后,拿到未经解码的H.264/H.265和audio数据,重新打包RTMP发送出去即可。需要注意的是,大多RTSP转RTMP模块,需要长时间运行,所以,需要有好多错误处理和自动重连机制,确保转发模块的稳定性。以下是两个可选的技术方案:

方案1:FFMPEG命令转发

ffmpeg -i rtsp://[摄像头地址]/[流媒体地址] -c:v libx264 -preset veryfast -maxrate 3000k -bufsize 6000k -pix_fmt yuv420p -g 50 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmp://[服务器地址]/[应用名称]/[流密钥]
  • 1.

  • rtsp://[摄像头地址]/[流媒体地址] 是摄像头的RTSP流地址。
  • -c:v libx264 指定视频编码器为libx264。
  • -preset veryfast 设置编码速度为非常快,以牺牲一些压缩效率换取更快的编码速度。
  • -maxrate 和 -bufsize 设置最大码率和缓冲区大小。
  • -pix_fmt yuv420p 设置像素格式为YUV420P,这是RTMP兼容的格式。
  • -g 50 设置关键帧间隔为50帧。
  • -c:a aac 指定音频编码器为AAC。
  • -b:a-ac 和 -ar 分别设置音频比特率、声道数和采样率。
  • -f flv 指定输出格式为FLV,RTMP流通常以FLV格式封装。
  • rtmp://[服务器地址]/[应用名称]/[流密钥] 是目标RTMP服务器的推送地址。

上述命令中的参数可能需要根据实际情况进行调整。

方案2:SmartRelaySDK

大牛直播SDK发布的RTSP转RTMP推送模块(SmartRelaySDK)C#的界面如下:

多路RTSP转RTMP推送方案的两个选择_多路rtsp转推rtmp

技术设计:

1. 拉流:通过RTSP直播播放SDK的数据回调接口,拿到音视频数据;

2. 转推:通过RTMP直播推送SDK的编码后数据输入接口,把回调上来的数据,传给RTMP直播推送模块,实现RTSP数据流到RTMP服务器的转发;

3. 录像:如果需要录像,借助RTSP直播播放SDK,拉到音视频数据后,直接存储MP4文件即可;

4. 快照:如果需要实时快照,拉流后,解码调用播放端快照接口,生成快照,因为快照涉及到video数据解码,如无必要,可不必开启,不然会额外消耗性能。

5. 拉流预览:如需预览拉流数据,只要调用播放端的播放接口,即可实现拉流数据预览;

6. 数据转AAC后转发:考虑到好多监控设备出来的音频可能是PCMA/PCMU的,如需要更通用的音频格式,可以转AAC后,在通过RTMP推送;

7. 转推RTMP实时静音:只需要在传audio数据的地方,加个判断即可;

8. 拉流速度反馈:通过RTSP播放端的实时码率反馈event,拿到实时带宽占用即可;

9. 整体网络状态反馈:考虑到有些摄像头可能会临时或异常关闭,RTMP服务器亦是,可以通过推拉流的event回调状态,查看那整体网络情况,如此界定:是拉不到流,还是推不到RTMP服务器。

上述是C#的基础demo,如果对C++比较熟悉,也可以直接用C++的,大牛直播SDK的RTSP转RTMP推送模块,通过配置xml的形式,程序启动后,从configure.xml读取相关的参数,实现一键拉流转发。

常规的参数配置,比如推拉流的rtsp rtmp url,如果需要自采集audio,设置采集的audio类型,比如rtsp自带audio、麦克风、扬声器或麦克风扬声器混音。

<?xml version="1.0" encoding="utf-8" ?>
<StreamRelays>
  <Relay>
    <id>0</id>
	<AudioOption>4</AudioOption>
    <PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream00</PushUrl>
  </Relay>
  <Relay>
    <id>1</id>
	<AudioOption>1</AudioOption>
    <PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream01</PushUrl>
  </Relay>
  <Relay>
    <id>2</id>
	<AudioOption>3</AudioOption>
    <PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream02</PushUrl>
  </Relay>
  <Relay>
    <id>3</id>
	<AudioOption>3</AudioOption>
    <PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream03</PushUrl>
  </Relay>
    <Relay>
    <id>4</id>
	<AudioOption>4</AudioOption>
    <PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream04</PushUrl>
  </Relay>
  <Relay>
    <id>5</id>
	<AudioOption>1</AudioOption>
    <PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream05</PushUrl>
  </Relay>
  <Relay>
    <id>6</id>
	<AudioOption>4</AudioOption>
    <PullUrl>rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream06</PushUrl>
  </Relay>
  <Relay>
    <id>7</id>
	<AudioOption>2</AudioOption>
    <PullUrl>rtsp://admin:admin123456@192.168.0.121:554/cam/realmonitor?channel=1<![CDATA[&]]>subtype=0</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/hls/stream07</PushUrl>
  </Relay>
</StreamRelays>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

封装代码如下:

/*
 * nt_relay_wrapper.cs.cs
 * nt_relay_wrapper.cs
 * 
 * WebSite: https://daniusdk.com
 * WeChat: xinsheng120
 * 
 * Created by DaniuLive on 2017/11/14.
 * Copyright © 2014~2024 DaniuLive. All rights reserved.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace SmartRelayDemo
{
    class nt_relay_wrapper
    {
        int relay_index_;
        nt_player_wrapper player_wrapper_;
        nt_publisher_wrapper publisher_wrapper_;
 
        UInt32 video_option_ = (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_ENCODED_DATA;
        UInt32 audio_option_ = (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA;
 
        public nt_player_wrapper GetPlayerWrapper() { return player_wrapper_; }
        public nt_publisher_wrapper GetPublisherWrapper() { return publisher_wrapper_; }
 
        public nt_relay_wrapper(int index, System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
        {
            relay_index_ = index;
            player_wrapper_ = new nt_player_wrapper(index, render_wnd, sync_invoke);
            publisher_wrapper_ = new nt_publisher_wrapper(index, render_wnd, sync_invoke);
        }
 
        ~nt_relay_wrapper() { }
 
        private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
            UInt32 video_codec_id, IntPtr data, UInt32 size,
            IntPtr info, IntPtr reserve)
        {
            if (publisher_wrapper_.is_rtmp_publishing())
            {
                publisher_wrapper_.OnVideoDataHandle(handle, user_data, video_codec_id, data, size, info, reserve);
            }
        }
 
        private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
            UInt32 audio_codec_id, IntPtr data, UInt32 size,
            IntPtr info, IntPtr reserve)
        {
            if (publisher_wrapper_.is_rtmp_publishing())
            {
                publisher_wrapper_.OnAudioDataHandle(handle, user_data, audio_codec_id, data, size, info, reserve);
            }
        }
 
        public void StartPull(String url)
        {
            if (!player_wrapper_.is_pulling())
            {
                player_wrapper_.SetBuffer(0);
 
                if (!player_wrapper_.StartPull(url, false))
                    return;
 
                player_wrapper_.EventOnVideoDataHandle += new nt_player_wrapper.DelOnVideoDataHandle(OnVideoDataHandle);
 
                if (audio_option_ == (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA)
                {
                    player_wrapper_.EventOnAudioDataHandle += new nt_player_wrapper.DelOnAudioDataHandle(OnAudioDataHandle);
                }
            }
        }
 
        public void StopPull()
        {
            player_wrapper_.StopPull();
        }
 
        public void StartPlayer(String url, bool is_rtsp_tcp_mode, bool is_mute)
        {
            player_wrapper_.SetBuffer(0);
 
            if (!player_wrapper_.StartPlay(url, is_rtsp_tcp_mode, is_mute))
                return;
        }
 
        public void StopPlayer()
        {
            player_wrapper_.StopPlay();
        }
 
        public void PlayerDispose()
        {
            player_wrapper_.Dispose();
        }
 
        public void SetPusherOption(UInt32 video_option, UInt32 audio_option)
        {
            video_option_ = video_option;
            audio_option_ = audio_option;
        }
 
        public void StartPublisher(String url)
        {
            if (!publisher_wrapper_.OpenPublisherHandle(video_option_, audio_option_))
                return;
 
            if (url.Length < 8)
            {
                publisher_wrapper_.try_close_handle();
                return;
            }
 
            if (!publisher_wrapper_.StartPublisher(url))
            {
                return;
            }
        }
 
        public void StopPublisher()
        {
            publisher_wrapper_.StopPublisher();
        }
 
        public void PublisherDispose()
        {
            publisher_wrapper_.Dispose();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.

如需播放,播放端封装逻辑实现如下:

/*
 * nt_player_wrapper.cs
 * nt_player_wrapper
 * 
 * WebSite: https://daniusdk.com
 * 
 * Created by DaniuLive on 2017/11/14.
 * Copyright © 2014~2024 DaniuLive. All rights reserved.
 */
 
public bool is_playing() {  return is_playing_; }
 
public bool is_pulling() {  return is_pulling_; }
	
public bool is_recording() { return is_recording_; }
 
public static bool is_zero_ptr(IntPtr ptr) { return IntPtr.Zero == ptr; }
 
public bool is_empty_handle() { return is_zero_ptr(player_handle_); }
 
private bool is_running()
{
	if (is_empty_handle())
		return false;
 
	return is_playing_ || is_recording_ || is_pulling_;
}
 
public bool OpenPullHandle(String url, bool is_rtsp_tcp_mode, bool is_mute)
{
	if ( player_handle_ != IntPtr.Zero )
		return true;
 
	if ( String.IsNullOrEmpty(url) )
		return false;
 
	IntPtr pull_handle = IntPtr.Zero;
 
	if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_Open(out pull_handle, IntPtr.Zero, 0, IntPtr.Zero))
	{
		return false;
	}
 
	if (pull_handle == IntPtr.Zero)
	{
		return false;
	}
 
	pull_event_call_back_ = new SP_SDKEventCallBack(SDKPullEventCallBack);
	NTSmartPlayerSDK.NT_SP_SetEventCallBack(pull_handle, IntPtr.Zero, pull_event_call_back_);
 
	resolution_notify_callback_ = new ResolutionNotifyCallback(PlaybackWindowResized);
 
	set_video_frame_call_back_ = new VideoFrameCallBack(SDKVideoFrameCallBack);
 
	NTSmartPlayerSDK.NT_SP_SetBuffer(pull_handle, play_buffer_);
	NTSmartPlayerSDK.NT_SP_SetFastStartup(pull_handle, 1);
	NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(pull_handle, 1);
	NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(pull_handle, is_rtsp_tcp_mode ? 1 : 0);
 
	NTSmartPlayerSDK.NT_SP_SetMute(pull_handle, is_mute_ ? 1 : 0);
 
	NTSmartPlayerSDK.NT_SP_SetAudioVolume(pull_handle, cur_audio_volume_);
 
	Int32 is_report = 1;
	Int32 report_interval = 3;
	NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(pull_handle, is_report, report_interval);
 
	//RTSP timeout设置
	Int32 rtsp_timeout = 10;
	NTSmartPlayerSDK.NT_SP_SetRtspTimeout(pull_handle, rtsp_timeout);
 
	//RTSP TCP/UDP自动切换设置
	Int32 is_auto_switch_tcp_udp = 1;
	NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(pull_handle, is_auto_switch_tcp_udp);
 
	if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_SetURL(pull_handle, url))
	{
		NTSmartPlayerSDK.NT_SP_Close(pull_handle);
		pull_handle = IntPtr.Zero;
		return false;
	}
 
	player_handle_ = pull_handle;
 
	return true;
}
 
private void PlaybackWindowResized(Int32 width, Int32 height)
{
	String resolution = width + "*" + height;
	EventGetVideoSize(player_index_, resolution);
}
 
public void SP_SDKVideoSizeHandle(IntPtr handle, IntPtr userData, Int32 width, Int32 height)
{
	if (null == sync_invoke_)
		return;
 
	System.ComponentModel.ISynchronizeInvoke sync_invoke_target = sync_invoke_.Target as System.ComponentModel.ISynchronizeInvoke;
 
	if (sync_invoke_target != null)
	{
		if (sync_invoke_target.InvokeRequired)
		{
			sync_invoke_target.BeginInvoke(resolution_notify_callback_, new object[] { width, height });
		}
		else
		{
			resolution_notify_callback_(width, height);
		}
	}
 
}
 
public bool StartPlay(String url, bool is_rtsp_tcp_mode, bool is_mute)
{
	if ( is_playing_ )
		return false;
 
	if (!is_pulling() && !is_recording())
	{
		if (!OpenPullHandle(url, is_rtsp_tcp_mode, is_mute))
			return false;
	}
 
	//video resolution callback
	video_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
	NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);
 
	bool is_support_d3d_render = false;
	Int32 in_support_d3d_render = 0;
 
	if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_, render_wnd_.Handle, ref in_support_d3d_render))
	{
		if (1 == in_support_d3d_render)
		{
			is_support_d3d_render = true;
		}
	}
 
	// is_support_d3d_render = false;
 
	if (is_support_d3d_render)
	{
		// 支持d3d绘制的话,就用D3D绘制
		NTSmartPlayerSDK.NT_SP_SetRenderWindow(player_handle_, render_wnd_.Handle);
		NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 1);
	}
	else
	{
		// 不支持D3D就让播放器吐出数据来,用GDI绘制,本demo仅用来展示一对一互动使用,具体可参考播放端的demo
 
		//video frame callback (YUV/RGB)
		//format请参见 NT_SP_E_VIDEO_FRAME_FORMAT,如需回调YUV,请设置为 NT_SP_E_VIDEO_FRAME_FROMAT_I420
		video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBack);
		NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, IntPtr.Zero, video_frame_call_back_);
	}
 
	uint ret = NTSmartPlayerSDK.NT_SP_StartPlay(player_handle_);
	if ( NTBaseCodeDefine.NT_ERC_OK != ret )
	{
		NTSmartPlayerSDK.NT_SP_Close(player_handle_);
		player_handle_ = IntPtr.Zero;
 
		return false;
	}
 
	is_playing_ = true;
 
	return true;
}
 
public void StopPlay(bool is_update_ui =true)
{
	if ( !is_playing_ )
		return;
 
	NTSmartPlayerSDK.NT_SP_StopPlay(player_handle_);
 
	if (!is_pulling() && !is_recording())
	{
		NTSmartPlayerSDK.NT_SP_Close(player_handle_);
		player_handle_ = IntPtr.Zero;
	}
 
	is_playing_ = false;
 
	if (is_update_ui && render_wnd_ != null)
	{
		render_wnd_.Invalidate();
	}
}
 
public bool StartPull(String url, bool is_rtsp_tcp_mode)
{
	if (is_pulling())
		return false;
 
	if (!is_playing() && !is_recording())
	{
		if (!OpenPullHandle(url, is_rtsp_tcp_mode, is_mute_))
			return false;
	}
 
	pull_stream_video_data_call_back_ = new SP_SDKPullStreamVideoDataCallBack(OnVideoDataHandle);
	pull_stream_audio_data_call_back_ = new SP_SDKPullStreamAudioDataCallBack(OnAudioDataHandle);
 
	NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(player_handle_, IntPtr.Zero, pull_stream_video_data_call_back_);
	NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(player_handle_, IntPtr.Zero, pull_stream_audio_data_call_back_);
 
	int is_transcode_aac = 1;   //PCMA/PCMU/Speex格式转AAC后 再转发
	NTSmartPlayerSDK.NT_SP_SetPullStreamAudioTranscodeAAC(player_handle_, is_transcode_aac);
 
	UInt32 ret = NTSmartPlayerSDK.NT_SP_StartPullStream(player_handle_);
 
	if (NTBaseCodeDefine.NT_ERC_OK != ret)
	{
		if (!is_playing_)
		{
			NTSmartPlayerSDK.NT_SP_Close(player_handle_);
			player_handle_ = IntPtr.Zero;
		}
 
		return false;
	}
 
	is_pulling_ = true;
 
	return true;
}
 
public void StopPull()
{
	if (!is_pulling_)
		return;
 
	NTSmartPlayerSDK.NT_SP_StopPullStream(player_handle_);
 
	if (!is_playing() && !is_recording())
	{
		NTSmartPlayerSDK.NT_SP_Close(player_handle_);
		player_handle_ = IntPtr.Zero;
	}
 
	is_pulling_ = false;
}
 
private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
	UInt32 video_codec_id, IntPtr data, UInt32 size,
	IntPtr info, IntPtr reserve)
{
	EventOnVideoDataHandle(handle, user_data, video_codec_id, data, size, info, reserve);
}
 
private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
					UInt32 audio_codec_id, IntPtr data, UInt32 size,
					IntPtr info, IntPtr reserve)
{
	EventOnAudioDataHandle(handle, user_data, audio_codec_id, data, size, info, reserve);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.

转推RTMP部分,推送端封装核心代码设计如下:

/*
 * nt_publisher_wrapper.cs
 * nt_publisher_wrapper
 * 
 * WebSite: https://daniusdk.com
 * 
 * Created by DaniuLive on 2017/11/14.
 * Copyright © 2014~2024 DaniuLive. All rights reserved.
 */
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using NT;
using System.Threading;
 
namespace SmartRelayDemo
{
    public struct NT_VideoFrame
    {
        public Int32 width_;   // 图像宽
        public Int32 height_;  // 图像高
        public IntPtr plane_;
        public Int32 stride_;
    }
 
    public struct CameraInfo
    {
        public String name_;
        public String id_;
        public List<NT_PB_VideoCaptureCapability> capabilities_;
    };
 
    class nt_publisher_wrapper : IDisposable
    {
        public delegate void DelGetPublisherEventMsg(int publisher_index, String msg);   //推送端Event消息
 
        [DllImport("kernel32", EntryPoint = "CopyMemory")]
        static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
 
        private bool disposed_ = false;
 
        private System.Windows.Forms.Control render_wnd_ = null;
        private System.Windows.Forms.PaintEventHandler render_wnd_paint_event_ = null;
 
        private IntPtr rtsp_handle_ = IntPtr.Zero;
 
        private const int enter_read_lock_timeout_ms_ = 1; // 暂时定义1毫秒, 也可以考虑0毫秒
        private ReaderWriterLockSlim shared_lock_ = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        private volatile IntPtr handle_ = IntPtr.Zero;
        private volatile int handle_reference_count_ = 0;
 
        private volatile bool is_rtmp_publishing_ = false;
        private volatile bool is_previewing_ = false;
        private volatile bool is_recording_ = false;
        private volatile bool is_rtsp_service_running_ = false;    //RTSP服务状态
        private volatile bool is_rtsp_publishing_ = false;  //RTSP流发布状态
 
        private WeakReference sync_invoke_ = null;
 
        private uint video_option_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_ENCODED_DATA;
        private uint audio_option_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA;
 
        private int video_width_ = 1280;
        private int video_height_ = 720;
 
        private volatile int external_video_layer_index_ = 0;
        private volatile int text_layer_index_ = 1;
 
        //event事件回调
        NT_PB_SDKEventCallBack pb_event_call_back_;
        delegate void PbSetEventCallBack(UInt32 event_id,
                Int64 param1,
                Int64 param2,
                UInt64 param3,
                UInt64 param4,
                [MarshalAs(UnmanagedType.LPStr)] String param5,
                [MarshalAs(UnmanagedType.LPStr)] String param6,
                IntPtr param7);
        PbSetEventCallBack pb_set_event_call_back_;
 
        //预览数据回调
        NT_PB_SDKVideoPreviewImageCallBack video_preview_image_callback_;
        delegate void SetVideoPreviewImageCallBack(NT_VideoFrame frame);
        SetVideoPreviewImageCallBack set_video_preview_image_callback_;
 
        private NT_VideoFrame cur_image_ = new NT_VideoFrame();
 
        public event DelGetPublisherEventMsg EventGetPublisherEventMsg;
 
        private NT_PB_RectRegion layer_regin_ = new NT_PB_RectRegion();
 
        private int publisher_index_ = 0;
 
        public nt_publisher_wrapper(int index, System.Windows.Forms.Control render_wnd, System.ComponentModel.ISynchronizeInvoke sync_invoke)
        {
            publisher_index_ = index;
            render_wnd_ = render_wnd;
            sync_invoke_ = new WeakReference(sync_invoke);
            pb_set_event_call_back_ = new PbSetEventCallBack(PbEventCallBack);
 
            if (render_wnd_ != null)
            {
                render_wnd_paint_event_ = new System.Windows.Forms.PaintEventHandler(this.OnRenderWindowPaint);
                render_wnd_.Paint += render_wnd_paint_event_;
            }
 
            layer_regin_.x_ = 0;
            layer_regin_.y_ = 0;
            layer_regin_.width_ = 0;
            layer_regin_.height_ = 0;
        }
 
        public void Dispose()
        {
            Dispose(true);
 
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            // GC.SuppressFinalize(this);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed_)
            {
                if (disposing)
                {
                }
 
                if (render_wnd_ != null && render_wnd_paint_event_ != null)
                {
                    render_wnd_.Paint -= render_wnd_paint_event_;
                }
 
                render_wnd_paint_event_ = null;
 
                if (cur_image_.plane_ != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(cur_image_.plane_);
                    cur_image_.plane_ = IntPtr.Zero;
                }
 
                // Note disposing has been done.
                disposed_ = true;
            }
        }
 
        ~nt_publisher_wrapper()
        {
            Dispose(false);
        }
 
        private void PbEventCallBack(UInt32 event_id,
            Int64 param1,
            Int64 param2,
            UInt64 param3,
            UInt64 param4,
            [MarshalAs(UnmanagedType.LPStr)] String param5,
            [MarshalAs(UnmanagedType.LPStr)] String param6,
            IntPtr param7)
        {
            String event_log = "";
 
            switch (event_id)
            {
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTING:
                    event_log = "连接中";
                    if (!String.IsNullOrEmpty(param5))
                    {
                        event_log = event_log + " url:" + param5;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTION_FAILED:
                    event_log = "连接失败";
                    if (!String.IsNullOrEmpty(param5))
                    {
                        event_log = event_log + " url:" + param5;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTED:
                    event_log = "已连接";
                    if (!String.IsNullOrEmpty(param5))
                    {
                        event_log = event_log + " url:" + param5;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_RECORDER_START_NEW_FILE:
                    event_log = " start new recorder file";
 
                    byte[] utf8_bytes = Encoding.Default.GetBytes(param5);
                    byte[] default_bytes = Encoding.Convert(Encoding.UTF8, Encoding.Default, utf8_bytes);
                    String file_name = Encoding.Default.GetString(default_bytes);
 
                    if (!String.IsNullOrEmpty(file_name))
                    {
                        event_log = event_log + " file name:" + file_name;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_ONE_RECORDER_FILE_FINISHED:
                    event_log = " finish recorder file";
                    byte[] finished_utf8_bytes = Encoding.Default.GetBytes(param5);
                    byte[] finished_default_bytes = Encoding.Convert(Encoding.UTF8, Encoding.Default, finished_utf8_bytes);
                    String finished_file_name = Encoding.Default.GetString(finished_default_bytes);
 
                    if (!String.IsNullOrEmpty(finished_file_name))
                    {
                        event_log = event_log + " file name:" + finished_file_name;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_DISCONNECTED:
                    event_log = "断开连接";
                    if (!String.IsNullOrEmpty(param5))
                    {
                        event_log = event_log + " url:" + param5;
                    }
                    break;
 
                case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_RTSP_URL:
                    event_log = "RTSP URL: " + param5;
                    break;
 
                default:
                    break;
            }
 
            EventGetPublisherEventMsg(publisher_index_, event_log);
        }
 
        public void SetResolution(int width, int height)
        {
            video_width_ = width;
            video_height_ = height;
        }
 
        public int CalBitRate(int frame_rate, int w, int h)
        {
            int kbit_rate = 2000;
 
            int area = w * h;
 
            if (area <= (320 * 300))
            {
                kbit_rate = 280;
            }
            else if (area <= (360 * 320))
            {
                kbit_rate = 360;
            }
            else if (area <= (640 * 480))
            {
                kbit_rate = 580;
            }
            else if (area <= (800 * 600))
            {
                kbit_rate = 620;
            }
            else if (area <= (900 * 700))
            {
                kbit_rate = 820;
            }
            else if (area <= (1280 * 720))
            {
                kbit_rate = 2000;
            }
            else if (area <= (1366 * 768))
            {
                kbit_rate = 2100;
            }
            else if (area <= (1600 * 900))
            {
                kbit_rate = 2500;
            }
            else if (area <= (1600 * 1050))
            {
                kbit_rate = 2700;
            }
            else if (area <= (1920 * 1088))
            {
                kbit_rate = 4500;
            }
            else
            {
                kbit_rate = 6000;
            }
 
            kbit_rate = kbit_rate * frame_rate / 25;
 
            if (kbit_rate < 80)
                kbit_rate = 80;
 
            return kbit_rate;
        }
 
        public int CalMaxKBitRate(int frame_rate, int w, int h, bool is_var_bitrate)
        {
            int max_kbit_rate = 2000;
 
            int area = w * h;
 
            if (area <= (320 * 300))
            {
                max_kbit_rate = is_var_bitrate ? 320 : 600;
            }
            else if (area <= (360 * 320))
            {
                max_kbit_rate = is_var_bitrate ? 400 : 800;
            }
            else if (area <= (640 * 360))
            {
                max_kbit_rate = is_var_bitrate ? 600 : 1000;
            }
            else if (area <= (640 * 480))
            {
                max_kbit_rate = is_var_bitrate ? 680 : 1300;
            }
            else if (area <= (800 * 600))
            {
                max_kbit_rate = is_var_bitrate ? 700 : 1500;
            }
            else if (area <= (900 * 700))
            {
                max_kbit_rate = is_var_bitrate ? 920 : 2200;
            }
            else if (area <= (1280 * 720))
            {
                max_kbit_rate = is_var_bitrate ? 2200 : 3200;
            }
            else if (area <= (1366 * 768))
            {
                max_kbit_rate = is_var_bitrate ? 2300 : 3300;
            }
            else if (area <= (1600 * 900))
            {
                max_kbit_rate = is_var_bitrate ? 2600 : 3500;
            }
            else if (area <= (1600 * 1050))
            {
                max_kbit_rate = is_var_bitrate ? 2700 : 3700;
            }
            else if (area <= (1920 * 1088))
            {   
                max_kbit_rate = is_var_bitrate ? 4000 : 6000;
            }
            else
            {
                max_kbit_rate = is_var_bitrate ? 6000 : 7000;
            }
 
            max_kbit_rate = max_kbit_rate * frame_rate / 25;
 
            if (area <= (320 * 240))
            {
                if (max_kbit_rate < 150)
                    max_kbit_rate = 150;
            }
            else if (area <= (640 * 480))
            {
                if (max_kbit_rate < 300)
                    max_kbit_rate = 300;
            }
            else if (area <= (1280 * 720))
            {
                if (max_kbit_rate < 600)
                    max_kbit_rate = 600;
            }
            else if (area <= (1920 * 1080))
            {
                if (max_kbit_rate < 960)
                    max_kbit_rate = 960;
            }
            else
            {
                if (max_kbit_rate < 1500)
                    max_kbit_rate = 1500;
            }
 
            return max_kbit_rate;
        }
 
        public int CalVideoQuality(int w, int h, bool is_h264)
        {
            int area = w * h;
 
            int quality = is_h264 ? 23 : 28;
 
            if (area <= (320 * 240))
            {
                quality = is_h264 ? 23 : 27;
            }
            else if (area <= (640 * 360))
            {
                quality = is_h264 ? 25 : 28;
            }
            else if (area <= (640 * 480))
            {
                quality = is_h264 ? 25 : 28;
            }
            else if (area <= (960 * 600))
            {
                quality = is_h264 ? 26 : 28;
            }
            else if (area <= (1280 * 720))
            {
                quality = is_h264 ? 27 : 29;
            }
            else if (area <= (1600 * 900))
            {
                quality = is_h264 ? 28 : 30;
            }
            else if (area <= (1920 * 1080))
            {
                quality = is_h264 ? 29 : 31;
            }
            else
            {
                quality = is_h264 ? 30 : 32;
            }
 
            return quality;
        }
 
        public int CalVideoEncoderSpeed(int w, int h, bool is_h264)
        {
            int area = w * h;
 
            if (is_h264)
            {
                if (area <= (1280 * 720))
                {
                    return 6;
                }
                else
                {
                    return 2;
                }
            }
 
            if (area <= (960 * 600))
            {
                return 3;
            }
            else if (area <= (1280 * 720))
            {
                return 2;
            }
            else
            {
                return 1;
            }
        }
 
        public int GetAudioInputDeviceNumber()
        {
            int auido_devices = 0;
 
            NTSmartPublisherSDK.NT_PB_GetAuidoInputDeviceNumber(ref auido_devices);
 
            return auido_devices;
        }
 
        public List<String> GetAudioInputDeviceName(int auido_devices)
        {
            List<String> audio_device_name = new List<string>();
 
            if (auido_devices > 0)
            {
                for (int i = 0; i < auido_devices; ++i)
                {
                    byte[] deviceNameBuffer = new byte[512];
 
                    string name = "";
 
                    if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_GetAuidoInputDeviceName((uint)i, deviceNameBuffer, 512))
                    {
                        int count = 0;
                        for (int j = 0; j < deviceNameBuffer.Length; ++j)
                        {
                            if (deviceNameBuffer[j] != 0)
                            {
                                count++;
                            }
                            else
                            {
                                break;
                            }
                        }
 
                        if (count > 0)
                        {
                            name = Encoding.UTF8.GetString(deviceNameBuffer, 0, count);
                        }
                    }
 
                    var audio_name = "";
 
                    if (name.Length == 0)
                    {
                        audio_name = "音频采集设备-";
                    }
                    else
                    {
                        audio_name = name + "-";
                    }
 
                    audio_name = audio_name + (i + 1);
 
                    audio_device_name.Add(name);
                }
            }
 
            return audio_device_name;
        }
 
        public bool IsCanCaptureSpeaker()
        {
            int is_capture_speaker = 0;
            if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_IsCanCaptureSpeaker(ref is_capture_speaker))
            {
                if (1 == is_capture_speaker)
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private void SetCommonOptionToPublisherSDK()
        {
            if (is_empty_handle())
            {
                Console.Write("SetCommonOptionToPublisherSDK, publisher_handle_ with null..");
                return;
            }
 
            if (handle_reference_count() > 0)
                return;
 
            // 音频相关设置
 
            if (audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC ||
                audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER)
            {
                int audio_device_number = GetAudioInputDeviceNumber();
 
                if(audio_device_number > 0 )
                {
                    NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(handle_, 0);
                }
            }
            else if (audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER ||
                audio_option_ == (UInt32)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC_SPEAKER_MIXER)
            {
                if (IsCanCaptureSpeaker())
                {
                    NTSmartPublisherSDK.NT_PB_SetCaptureSpeakerCompensateMute(handle_, 1);
                }
            }
 
            NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(handle_, 1);
            
        }
 
        public bool OpenPublisherHandle(UInt32 video_option, UInt32 audio_option)
        {
            if (!is_empty_handle())
                return false;
 
            video_option_ = video_option;
            audio_option_ = audio_option;
 
            IntPtr handle = IntPtr.Zero;
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_Open(out handle, video_option_, audio_option_, 0, IntPtr.Zero))
                return false;
 
            if (is_zero_ptr(handle))
                return false;
 
            if (null == pb_event_call_back_)
                pb_event_call_back_ = new NT_PB_SDKEventCallBack(PbSDKEventCallBack);
 
            NTSmartPublisherSDK.NT_PB_SetEventCallBack(handle, IntPtr.Zero, pb_event_call_back_);
 
            shared_lock_.EnterWriteLock();
            try
            {
                handle_reference_count_ = 0;
                handle_ = handle;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            if (null == set_video_preview_image_callback_)
                set_video_preview_image_callback_ = new SetVideoPreviewImageCallBack(VideoPreviewImageCallBack);
 
            return true;
        }
 
        public void PbSDKEventCallBack(IntPtr handle, IntPtr user_data,
            UInt32 event_id,
            Int64 param1,
            Int64 param2,
            UInt64 param3,
            UInt64 param4,
            [MarshalAs(UnmanagedType.LPStr)] String param5,
            [MarshalAs(UnmanagedType.LPStr)] String param6,
            IntPtr param7)
        {
            if (sync_invoke_ != null)
            {
                object target = sync_invoke_.Target;
                if (target != null)
                {
                    System.ComponentModel.ISynchronizeInvoke sync_invoke_target = target as System.ComponentModel.ISynchronizeInvoke;
                    if (sync_invoke_target != null)
                    {
                        if (sync_invoke_target.InvokeRequired)
                        {
                            sync_invoke_target.BeginInvoke(pb_set_event_call_back_, new object[] { event_id, param1, param2, param3, param4, param5, param6, param7 });
                        }
                        else
                        {
                            pb_set_event_call_back_(event_id, param1, param2, param3, param4, param5, param6, param7);
                        }
                    }
                }
            }
        }
 
        //预览数据回调
        public void SDKVideoPreviewImageCallBack(IntPtr handle, IntPtr user_data, IntPtr image)
        {
            NT_PB_Image pb_image = (NT_PB_Image)Marshal.PtrToStructure(image, typeof(NT_PB_Image));
 
            NT_VideoFrame pVideoFrame = new NT_VideoFrame();
 
            pVideoFrame.width_ = pb_image.width_;
            pVideoFrame.height_ = pb_image.height_;
 
            pVideoFrame.stride_ = pb_image.stride_[0];
 
            Int32 argb_size = pb_image.stride_[0] * pb_image.height_;
 
            pVideoFrame.plane_ = Marshal.AllocHGlobal(argb_size);
 
            CopyMemory(pVideoFrame.plane_, pb_image.plane_[0], (UInt32)argb_size);
 
            if (sync_invoke_ != null)
            {
                System.ComponentModel.ISynchronizeInvoke sync_invoke_target = sync_invoke_.Target as System.ComponentModel.ISynchronizeInvoke;
 
                if (sync_invoke_target != null)
                {
 
                    if (sync_invoke_target.InvokeRequired)
                    {
                        sync_invoke_target.BeginInvoke(set_video_preview_image_callback_, new object[] { pVideoFrame });
                    }
                    else
                    {
                        set_video_preview_image_callback_(pVideoFrame);
                    }
                }
            }
        }
 
        public void VideoPreviewImageCallBack(NT_VideoFrame frame)
        {
            if (cur_image_.plane_ != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(cur_image_.plane_);
                cur_image_.plane_ = IntPtr.Zero;
            }
 
            cur_image_ = frame;
 
            if (render_wnd_ != null)
            {
                render_wnd_.Invalidate();
            }
        }
 
        public List<CameraInfo> GetCameraInfos()
        {
            List<CameraInfo> cameras = new List<CameraInfo>();
 
            int device_number = 0;
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceNumber(ref device_number))
            {
                return cameras;
            }
 
            if (device_number < 1)
            {
                return cameras;
            }
 
            for (int i = 0; i < device_number; ++i)
            {
                CameraInfo info = new CameraInfo();
 
                info.capabilities_ = new List<NT_PB_VideoCaptureCapability>();
 
                StringBuilder name = new StringBuilder(256);
                StringBuilder id = new StringBuilder(1024);
 
                if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceInfo(i,
                    name, 256,
                    id, 1024))
                {
                    continue;
                }
 
                info.name_ = name.ToString();
                info.id_ = id.ToString();
 
                int capability_number = 0;
                if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceCapabilityNumber(
                    id.ToString(), ref capability_number))
                {
                    continue;
                }
 
                bool is_failed = false;
 
                for (int j = 0; j < capability_number; ++j)
                {
                    NT_PB_VideoCaptureCapability capability = new NT_PB_VideoCaptureCapability();
 
                    if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetVideoCaptureDeviceCapability(
                        id.ToString(), j, ref capability))
                    {
                        is_failed = true;
                        break;
                    }
 
                    info.capabilities_.Add(capability);
                }
 
                if (!is_failed)
                {
                    cameras.Add(info);
                }
            }
 
            return cameras;
        }
 
        public bool StartPreview()
        {
            if (is_empty_handle() || is_previewing())
                return false;
 
            video_preview_image_callback_ = new NT_PB_SDKVideoPreviewImageCallBack(SDKVideoPreviewImageCallBack);
            NTSmartPublisherSDK.NT_PB_SetVideoPreviewImageCallBack(handle_, (int)NTSmartPublisherDefine.NT_PB_E_IMAGE_FORMAT.NT_PB_E_IMAGE_FORMAT_RGB32, IntPtr.Zero, video_preview_image_callback_);
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPreview(handle_, 0x800000, IntPtr.Zero))
            {
                try_close_handle();
                return false;
            }
 
            shared_lock_.EnterWriteLock();
            try
            {
                handle_reference_count_++;
                is_previewing_ = true;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            return true;
        }
 
        public void StopPreview()
        {
            if (is_empty_handle() || !is_previewing())
                return;
 
            shared_lock_.EnterWriteLock();
            try
            {
                is_previewing_ = false;
                handle_reference_count_--;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            NTSmartPublisherSDK.NT_PB_StopPreview(handle_);
            try_close_handle();
 
            if (render_wnd_ != null)
                render_wnd_.Invalidate();
        }
 
        public bool StartPublisher(String url)
        {
            if (is_empty_handle() || is_rtmp_publishing())
                return false;
 
            SetCommonOptionToPublisherSDK();
 
            if (!String.IsNullOrEmpty(url))
                NTSmartPublisherSDK.NT_PB_SetURL(handle_, url, IntPtr.Zero);
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPublisher(handle_, IntPtr.Zero))
            {
                try_close_handle();
                return false;
            }
 
            shared_lock_.EnterWriteLock();
            try
            {
                handle_reference_count_++;
                is_rtmp_publishing_ = true;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            return true;
        }
 
        public void StopPublisher()
        {
            if (is_empty_handle() || !is_rtmp_publishing())
                return;
 
            shared_lock_.EnterWriteLock();
            try
            {
                is_rtmp_publishing_ = false;
                handle_reference_count_--;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            NTSmartPublisherSDK.NT_PB_StopPublisher(handle_);
            try_close_handle();
        }
 
        public bool StartRecorder()
        {
            if (is_empty_handle() || is_recording())
                return false;
 
            //string edit_rec_dir = "D:\\dntest";
            string edit_rec_dir = "D:\\推送端录像\\上海站";
 
            if (String.IsNullOrEmpty(edit_rec_dir))
            {
                Console.WriteLine("请设置录像目录");
                return false;
            }
 
            uint ret = NTSmartPublisherSDK.NT_PB_SetRecorderDirectoryW(handle_, edit_rec_dir, IntPtr.Zero);
 
            if (NTBaseCodeDefine.NT_ERC_OK != ret)
            {
                try_close_handle();
                return false;
            }
 
            uint rec_max_file_size = 512 * 1024;
            NTSmartPublisherSDK.NT_PB_SetRecorderFileMaxSize(handle_, rec_max_file_size);
 
            NT_PB_RecorderFileNameRuler rec_name_ruler = new NT_PB_RecorderFileNameRuler();
 
            String rec_file_name_prefix_ = "transcode-rec";
            rec_name_ruler.file_name_prefix_ = rec_file_name_prefix_.ToString();
            rec_name_ruler.append_date_ = 1;
            rec_name_ruler.append_time_ = 1;
 
            NTSmartPublisherSDK.NT_PB_SetRecorderFileNameRuler(handle_, ref rec_name_ruler);
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRecorder(handle_, IntPtr.Zero))
            {
                try_close_handle();
                return false;
            }
 
            shared_lock_.EnterWriteLock();
            try
            {
                handle_reference_count_++;
                is_recording_ = true;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            return true;
        }
 
        public UInt32 PauseRecorder(bool is_pause)
        {
            if (is_empty_handle() || !is_recording())
                return NTBaseCodeDefine.NT_ERC_FAILED;
 
            UInt32 ret = NTBaseCodeDefine.NT_ERC_OK;
 
            if (is_pause)
            {
                ret = NTSmartPublisherSDK.NT_PB_PauseRecorder(handle_, 1);
                if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret)
                {
                    Console.WriteLine("暂停录像失败, 请重新尝试!");
                    return ret;
                }
                else if (NTBaseCodeDefine.NT_ERC_OK == ret)
                {
                    //btn_pause_rec.Text = "恢复录像";
                }
            }
            else
            {
                ret = NTSmartPublisherSDK.NT_PB_PauseRecorder(handle_, 0);
                if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret)
                {
                    Console.WriteLine("恢复录像失败, 请重新尝试!");
                    return ret;
                }
                else if (NTBaseCodeDefine.NT_ERC_OK == ret)
                {
                    //btn_pause_rec.Text = "暂停录像";
                }
            }
 
            return ret;
        }
 
        public void StopRecorder()
        {
            if (is_empty_handle() || !is_recording())
                return;
 
            shared_lock_.EnterWriteLock();
            try
            {
                is_recording_ = false;
                handle_reference_count_--;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            NTSmartPublisherSDK.NT_PB_StopRecorder(handle_);
            try_close_handle();
        }
 
        public bool is_previewing() { return is_previewing_; }
 
        public bool is_rtmp_publishing() { return is_rtmp_publishing_; }
 
        public bool is_recording() { return is_recording_; }
 
        public bool IsRTSPSerivceRunning()
        {
            return is_rtsp_service_running_;
        }
 
        public bool is_rtsp_publishing() { return is_rtsp_publishing_; }
 
        public static bool is_zero_ptr(IntPtr ptr) { return IntPtr.Zero == ptr; }
 
        public bool is_empty_handle() { return is_zero_ptr(handle_); }
 
        public int handle_reference_count() { return handle_reference_count_; }
 
        private bool is_running()
        {
            if (is_empty_handle())
                return false;
 
            return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_ || is_previewing_;
        }
 
        private bool is_audio_running()
        {
            if (is_empty_handle())
                return false;
 
            return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_;
        }
 
        private bool is_video_running()
        {
            if (is_empty_handle())
                return false;
 
            return is_rtmp_publishing_ || is_recording_ || is_rtsp_publishing_;
        }
 
 
        public void post_audio_pcm_data(IntPtr data, UInt32 size, UInt64 timestamp,
            Int32 sample_rate, Int32 channels, Int32 per_channel_sample_number)
        {
            if (is_zero_ptr(data) || size < 1)
                return;
 
            if (!is_audio_running())
                return;
 
            if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
            {
                try
                {
                    if (is_audio_running())
                        NTSmartPublisherSDK.NT_PB_PostAudioPCMData(handle_, data, size, timestamp, sample_rate, channels, per_channel_sample_number);
                }
                finally
                {
                    shared_lock_.ExitReadLock();
                }
            }
        }
 
        public void OnVideoDataHandle(IntPtr handle, IntPtr user_data, 
            UInt32 video_codec_id, IntPtr data, UInt32 size,
            IntPtr info, IntPtr reserve)
        {
            if (is_zero_ptr(data) || size < 1)
                return;
 
            if (!is_video_running())
                return;
 
            if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
            {
                try
                {
                    if (is_video_running())
                    {
                        NT_SP_PullStreamVideoDataInfo video_info = (NT_SP_PullStreamVideoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamVideoDataInfo));
 
                        NTSmartPublisherSDK.NT_PB_PostVideoEncodedDataV2(handle_, video_codec_id,
                            data, size, video_info.is_key_frame_, video_info.timestamp_, video_info.presentation_timestamp_);
                    }
                }
                finally
                {
                    shared_lock_.ExitReadLock();
                }
            }
        }
 
 
        public void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
                            UInt32 audio_codec_id, IntPtr data, UInt32 size,
                            IntPtr info, IntPtr reserve)
        {
            if (is_zero_ptr(data) || size < 1)
                return;
 
            if (!is_audio_running())
                return;
 
            if (shared_lock_.TryEnterReadLock(enter_read_lock_timeout_ms_))
            {
                try
                {
                    if (is_audio_running() && audio_option_ == (UInt32)NT.NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_ENCODED_DATA)
                    {
                        NT_SP_PullStreamAuidoDataInfo audio_info = (NT_SP_PullStreamAuidoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamAuidoDataInfo));
 
                        NTSmartPublisherSDK.NT_PB_PostAudioEncodedData(handle_, audio_codec_id, data, size,
                        audio_info.is_key_frame_, audio_info.timestamp_,
                        audio_info.parameter_info_, audio_info.parameter_info_size_);
                    }
                }
                finally
                {
                    shared_lock_.ExitReadLock();
                }
            }
        }
 
        public bool enable_layer(int index, bool enable)
        {
            if (index < 1)
                return false;
 
            if (is_empty_handle())
                return false;
 
            return NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_EnableLayer(handle_, 0, index, enable ? 1 : 0);
        }
 
        public bool update_layer_region(int index, int x, int y, int w, int h)
        {
            if (index < 1)
                return false;
 
            if (is_empty_handle())
                return false;
 
            NT_PB_RectRegion region = new NT_PB_RectRegion();
            region.x_ = x;
            region.y_ = y;
            region.width_ = w;
            region.height_ = h;
 
            return NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_UpdateLayerRegion(handle_, 0, index, ref region);
        }
 
 
        private bool add_layer_config(object layer_config, int layer_type)
        {
            IntPtr layer_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(layer_config));
            Marshal.StructureToPtr(layer_config, layer_ptr, false);
 
            UInt32 ret = NTSmartPublisherSDK.NT_PB_AddLayerConfig(handle_, 0,
                            layer_ptr, layer_type, 0, IntPtr.Zero);
 
            Marshal.FreeHGlobal(layer_ptr);
 
            return NTBaseCodeDefine.NT_ERC_OK == ret;
        }
 
        private void fill_layer_base(object layer, out NT_PB_LayerBaseConfig layer_base, int type, int index, bool enable, int x, int y, int w, int h)
        {
            layer_base.type_ = type;
            layer_base.index_ = index;
            layer_base.enable_ = enable ? 1 : 0;
            layer_base.region_.x_ = 0;
            layer_base.region_.y_ = 0;
            layer_base.region_.width_ = w;
            layer_base.region_.height_ = h;
 
            layer_base.offset_ = Marshal.OffsetOf(layer.GetType(), "base_").ToInt32();
            layer_base.cb_size_ = (uint)Marshal.SizeOf(layer);
        }
 
        public bool config_layers(bool is_add_rgbx_zero_layer)
        {
            if (video_option_ != (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER)
                return false;
 
            if (is_empty_handle())
                return false;
 
            int w = video_width_;
            int h = video_height_;
 
            if ((w & 0x1) != 0)
                --w;
 
            if ((h & 0x1) != 0)
                --h;
 
            if (w < 2 || h < 2)
                return false;
 
            NTSmartPublisherSDK.NT_PB_ClearLayersConfig(handle_, 0, 0, IntPtr.Zero);
 
            int type, index = 0;
            if (is_add_rgbx_zero_layer)
            {
                NT_PB_RGBARectangleLayerConfig rgba_layer = new NT_PB_RGBARectangleLayerConfig();
                type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_RGBA_RECTANGLE;
                fill_layer_base(rgba_layer, out rgba_layer.base_, type, index, true, 0, 0, w, h);
                rgba_layer.red_ = 0;
                rgba_layer.green_ = 0;
                rgba_layer.blue_ = 0;
                rgba_layer.alpha_ = 255;
                if (add_layer_config(rgba_layer, type))
                    index++;
            }
 
            NT_PB_ExternalVideoFrameLayerConfig external_video_layer = new NT_PB_ExternalVideoFrameLayerConfig();
            type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
            fill_layer_base(external_video_layer, out external_video_layer.base_, type, index, true, 0, 0, w, h);
            if (add_layer_config(external_video_layer, type))
                external_video_layer_index_ = index++;
 
 
            //叠加的文本层
            NT_PB_ExternalVideoFrameLayerConfig text_layer = new NT_PB_ExternalVideoFrameLayerConfig();
            type = (Int32)NTSmartPublisherDefine.NT_PB_E_LAYER_TYPE.NT_PB_E_LAYER_TYPE_EXTERNAL_VIDEO_FRAME;
            fill_layer_base(text_layer, out text_layer.base_, type, index, false, 0, 0, 64, 64);
            if (add_layer_config(text_layer, type))
                text_layer_index_ = index++;
 
            return index > 0;
        }
 
        public int get_external_video_layer_index() { return external_video_layer_index_; }
 
        public int get_text_layer_index() { return text_layer_index_; }
 
        public void SetVideoCaptureDeviceBaseParameter(String camera_id, UInt32 width, UInt32 height)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoCaptureDeviceBaseParameter(handle_, camera_id, width, height);
        }
 
        public void SetFrameRate(UInt32 frame_rate)
        {
            NTSmartPublisherSDK.NT_PB_SetFrameRate(handle_, frame_rate);
        }
 
        public void SetVideoEncoder(Int32 type, Int32 encoder_id, UInt32 codec_id, Int32 param1)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoder(handle_, type, encoder_id, codec_id, param1);
        }
 
        public void SetVideoQualityV2(Int32 quality)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoQualityV2(handle_, quality);
        }
 
        public void SetVideoMaxBitRate(Int32 kbit_rate)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoMaxBitRate(handle_, kbit_rate);
        }
 
        public void SetVideoBitRate(Int32 kbit_rate)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoBitRate(handle_, kbit_rate);
        }
 
        public void SetVideoKeyFrameInterval(Int32 interval)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoKeyFrameInterval(handle_, interval);
        }
 
        public void SetVideoEncoderProfile(Int32 profile)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderProfile(handle_, profile);
        }
 
        public void SetVideoEncoderSpeed(Int32 speed)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpeed(handle_, speed);
        }
 
        public void ClearVideoEncoderSpecialOptions()
        {
            // 清除编码器所有的特定的参数
            NTSmartPublisherSDK.NT_PB_ClearVideoEncoderSpecialOptions(handle_);
        }
 
        public void SetVideoEncoderQPMax(Int32 qp_max)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMax(handle_, qp_max);
        }
 
        public void SetVideoEncoderQPMin(Int32 qp_min)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderQPMin(handle_, qp_min);
        }
 
        public void SetVideoEncoderSpecialInt32Option(String option_name, Int32 option_value)
        {
            NTSmartPublisherSDK.NT_PB_SetVideoEncoderSpecialInt32Option(handle_, option_name, option_value);
        }
 
        public void SetAuidoInputDeviceId(UInt32 device_id)
        {
            NTSmartPublisherSDK.NT_PB_SetAuidoInputDeviceId(handle_, device_id);
        }
 
        public void SetPublisherAudioCodecType(Int32 type)
        {
            NTSmartPublisherSDK.NT_PB_SetPublisherAudioCodecType(handle_, type);
        }
 
        public void SetPublisherMute(bool is_mute)
        {
            NTSmartPublisherSDK.NT_PB_SetMute(handle_, is_mute ? 1 : 0);
        }
 
        public void SetEchoCancellation(Int32 isCancel, Int32 delay)
        {
            NTSmartPublisherSDK.NT_PB_SetEchoCancellation(handle_, isCancel, delay);
        }
 
        public void SetNoiseSuppression(Int32 isNS)
        {
            NTSmartPublisherSDK.NT_PB_SetNoiseSuppression(handle_, isNS);
        }
 
        public void SetAGC(Int32 isAGC)
        {
            NTSmartPublisherSDK.NT_PB_SetAGC(handle_, isAGC);
        }
 
        public void SetVAD(Int32 isVAD)
        {
            NTSmartPublisherSDK.NT_PB_SetVAD(handle_, isVAD);
        }
 
        public void SetInputAudioVolume(float audio_input_volume)
        {
            NTSmartPublisherSDK.NT_PB_SetInputAudioVolume(handle_, 0, audio_input_volume);
        }
 
 
        public bool StartRtspService()
        {
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_OpenRtspServer(ref rtsp_handle_, 0))
            {
                Console.WriteLine("创建rtsp server实例失败! 请检查sdk有效性.");
                return false;
            }
 
            if (IntPtr.Zero == rtsp_handle_)
            {
                Console.WriteLine("创建rtsp server实例失败! 请检查sdk有效性.");
                return false;
            }
 
            int port = 28554;
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_SetRtspServerPort(rtsp_handle_, port))
            {
                NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
                rtsp_handle_ = IntPtr.Zero;
                Console.WriteLine("设置rtsp server端口失败,请检查端口是否重复或者端口不在范围内!");
                return false;
            }
 
            //String user_name = "admin";
            //String password = "123456";
 
            //NTSmartPublisherSDK.NT_PB_SetRtspServerUserNamePassword(rtsp_handle, user_name, password);
 
            if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_StartRtspServer(rtsp_handle_, 0))
            {
                Console.WriteLine("StartRtspServer suc..");
            }
            else
            {
                NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
                rtsp_handle_ = IntPtr.Zero;
                Console.WriteLine("启动rtsp server失败, 请检查设置的端口是否被占用!");
                return false;
            }
 
            is_rtsp_service_running_ = true;
 
            return true;
        }
 
        public void StopRtspService()
        {
            NTSmartPublisherSDK.NT_PB_StopRtspServer(rtsp_handle_);
            NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
            rtsp_handle_ = IntPtr.Zero;
 
            is_rtsp_service_running_ = false;
        }
 
        public bool StartRtspStream()
        {
            if (is_empty_handle() || is_rtsp_publishing())
                return false;
 
            String rtsp_stream_name = "stream1";
            NTSmartPublisherSDK.NT_PB_SetRtspStreamName(handle_, rtsp_stream_name);
 
            NTSmartPublisherSDK.NT_PB_ClearRtspStreamServer(handle_);
            NTSmartPublisherSDK.NT_PB_AddRtspStreamServer(handle_, rtsp_handle_, 0);
 
            if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRtspStream(handle_, 0))
            {
                try_close_handle();
                return false;
            }
 
            shared_lock_.EnterWriteLock();
            try
            {
                handle_reference_count_++;
                is_rtsp_publishing_ = true;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            return true;
        }
 
        public void StopRtspStream()
        {
            if (is_empty_handle() || !is_rtsp_publishing())
                return;
 
            shared_lock_.EnterWriteLock();
            try
            {
                is_rtsp_publishing_ = false;
                handle_reference_count_--;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            NTSmartPublisherSDK.NT_PB_StopRtspStream(handle_);
            try_close_handle();
        }
 
        public int GetRtspSessionNumbers()
        {
            int num = 0;
            if (rtsp_handle_ != IntPtr.Zero)
            {
                if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetRtspServerClientSessionNumbers(rtsp_handle_, ref num))
                {
                    Console.WriteLine("Call NT_PB_GetRtspServerClientSessionNumbers failed..");
                }
            }
 
            return num;
        }
 
        public void try_close_handle()
        {
            if (is_empty_handle() || handle_reference_count_ > 0)
                return;
 
            IntPtr handle = IntPtr.Zero;
            shared_lock_.EnterWriteLock();
            try
            {
                handle = handle_;
                handle_ = IntPtr.Zero;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            if (!is_zero_ptr(handle))
                NTSmartPublisherSDK.NT_PB_Close(handle);
        }
 
        public void close()
        {
            if (is_empty_handle())
                return;
 
            IntPtr handle = IntPtr.Zero;
            shared_lock_.EnterWriteLock();
            try
            {
                handle = handle_;
                handle_ = IntPtr.Zero;
            }
            finally
            {
                shared_lock_.ExitWriteLock();
            }
 
            if (!is_zero_ptr(handle))
                NTSmartPublisherSDK.NT_PB_Close(handle);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.
  • 335.
  • 336.
  • 337.
  • 338.
  • 339.
  • 340.
  • 341.
  • 342.
  • 343.
  • 344.
  • 345.
  • 346.
  • 347.
  • 348.
  • 349.
  • 350.
  • 351.
  • 352.
  • 353.
  • 354.
  • 355.
  • 356.
  • 357.
  • 358.
  • 359.
  • 360.
  • 361.
  • 362.
  • 363.
  • 364.
  • 365.
  • 366.
  • 367.
  • 368.
  • 369.
  • 370.
  • 371.
  • 372.
  • 373.
  • 374.
  • 375.
  • 376.
  • 377.
  • 378.
  • 379.
  • 380.
  • 381.
  • 382.
  • 383.
  • 384.
  • 385.
  • 386.
  • 387.
  • 388.
  • 389.
  • 390.
  • 391.
  • 392.
  • 393.
  • 394.
  • 395.
  • 396.
  • 397.
  • 398.
  • 399.
  • 400.
  • 401.
  • 402.
  • 403.
  • 404.
  • 405.
  • 406.
  • 407.
  • 408.
  • 409.
  • 410.
  • 411.
  • 412.
  • 413.
  • 414.
  • 415.
  • 416.
  • 417.
  • 418.
  • 419.
  • 420.
  • 421.
  • 422.
  • 423.
  • 424.
  • 425.
  • 426.
  • 427.
  • 428.
  • 429.
  • 430.
  • 431.
  • 432.
  • 433.
  • 434.
  • 435.
  • 436.
  • 437.
  • 438.
  • 439.
  • 440.
  • 441.
  • 442.
  • 443.
  • 444.
  • 445.
  • 446.
  • 447.
  • 448.
  • 449.
  • 450.
  • 451.
  • 452.
  • 453.
  • 454.
  • 455.
  • 456.
  • 457.
  • 458.
  • 459.
  • 460.
  • 461.
  • 462.
  • 463.
  • 464.
  • 465.
  • 466.
  • 467.
  • 468.
  • 469.
  • 470.
  • 471.
  • 472.
  • 473.
  • 474.
  • 475.
  • 476.
  • 477.
  • 478.
  • 479.
  • 480.
  • 481.
  • 482.
  • 483.
  • 484.
  • 485.
  • 486.
  • 487.
  • 488.
  • 489.
  • 490.
  • 491.
  • 492.
  • 493.
  • 494.
  • 495.
  • 496.
  • 497.
  • 498.
  • 499.
  • 500.
  • 501.
  • 502.
  • 503.
  • 504.
  • 505.
  • 506.
  • 507.
  • 508.
  • 509.
  • 510.
  • 511.
  • 512.
  • 513.
  • 514.
  • 515.
  • 516.
  • 517.
  • 518.
  • 519.
  • 520.
  • 521.
  • 522.
  • 523.
  • 524.
  • 525.
  • 526.
  • 527.
  • 528.
  • 529.
  • 530.
  • 531.
  • 532.
  • 533.
  • 534.
  • 535.
  • 536.
  • 537.
  • 538.
  • 539.
  • 540.
  • 541.
  • 542.
  • 543.
  • 544.
  • 545.
  • 546.
  • 547.
  • 548.
  • 549.
  • 550.
  • 551.
  • 552.
  • 553.
  • 554.
  • 555.
  • 556.
  • 557.
  • 558.
  • 559.
  • 560.
  • 561.
  • 562.
  • 563.
  • 564.
  • 565.
  • 566.
  • 567.
  • 568.
  • 569.
  • 570.
  • 571.
  • 572.
  • 573.
  • 574.
  • 575.
  • 576.
  • 577.
  • 578.
  • 579.
  • 580.
  • 581.
  • 582.
  • 583.
  • 584.
  • 585.
  • 586.
  • 587.
  • 588.
  • 589.
  • 590.
  • 591.
  • 592.
  • 593.
  • 594.
  • 595.
  • 596.
  • 597.
  • 598.
  • 599.
  • 600.
  • 601.
  • 602.
  • 603.
  • 604.
  • 605.
  • 606.
  • 607.
  • 608.
  • 609.
  • 610.
  • 611.
  • 612.
  • 613.
  • 614.
  • 615.
  • 616.
  • 617.
  • 618.
  • 619.
  • 620.
  • 621.
  • 622.
  • 623.
  • 624.
  • 625.
  • 626.
  • 627.
  • 628.
  • 629.
  • 630.
  • 631.
  • 632.
  • 633.
  • 634.
  • 635.
  • 636.
  • 637.
  • 638.
  • 639.
  • 640.
  • 641.
  • 642.
  • 643.
  • 644.
  • 645.
  • 646.
  • 647.
  • 648.
  • 649.
  • 650.
  • 651.
  • 652.
  • 653.
  • 654.
  • 655.
  • 656.
  • 657.
  • 658.
  • 659.
  • 660.
  • 661.
  • 662.
  • 663.
  • 664.
  • 665.
  • 666.
  • 667.
  • 668.
  • 669.
  • 670.
  • 671.
  • 672.
  • 673.
  • 674.
  • 675.
  • 676.
  • 677.
  • 678.
  • 679.
  • 680.
  • 681.
  • 682.
  • 683.
  • 684.
  • 685.
  • 686.
  • 687.
  • 688.
  • 689.
  • 690.
  • 691.
  • 692.
  • 693.
  • 694.
  • 695.
  • 696.
  • 697.
  • 698.
  • 699.
  • 700.
  • 701.
  • 702.
  • 703.
  • 704.
  • 705.
  • 706.
  • 707.
  • 708.
  • 709.
  • 710.
  • 711.
  • 712.
  • 713.
  • 714.
  • 715.
  • 716.
  • 717.
  • 718.
  • 719.
  • 720.
  • 721.
  • 722.
  • 723.
  • 724.
  • 725.
  • 726.
  • 727.
  • 728.
  • 729.
  • 730.
  • 731.
  • 732.
  • 733.
  • 734.
  • 735.
  • 736.
  • 737.
  • 738.
  • 739.
  • 740.
  • 741.
  • 742.
  • 743.
  • 744.
  • 745.
  • 746.
  • 747.
  • 748.
  • 749.
  • 750.
  • 751.
  • 752.
  • 753.
  • 754.
  • 755.
  • 756.
  • 757.
  • 758.
  • 759.
  • 760.
  • 761.
  • 762.
  • 763.
  • 764.
  • 765.
  • 766.
  • 767.
  • 768.
  • 769.
  • 770.
  • 771.
  • 772.
  • 773.
  • 774.
  • 775.
  • 776.
  • 777.
  • 778.
  • 779.
  • 780.
  • 781.
  • 782.
  • 783.
  • 784.
  • 785.
  • 786.
  • 787.
  • 788.
  • 789.
  • 790.
  • 791.
  • 792.
  • 793.
  • 794.
  • 795.
  • 796.
  • 797.
  • 798.
  • 799.
  • 800.
  • 801.
  • 802.
  • 803.
  • 804.
  • 805.
  • 806.
  • 807.
  • 808.
  • 809.
  • 810.
  • 811.
  • 812.
  • 813.
  • 814.
  • 815.
  • 816.
  • 817.
  • 818.
  • 819.
  • 820.
  • 821.
  • 822.
  • 823.
  • 824.
  • 825.
  • 826.
  • 827.
  • 828.
  • 829.
  • 830.
  • 831.
  • 832.
  • 833.
  • 834.
  • 835.
  • 836.
  • 837.
  • 838.
  • 839.
  • 840.
  • 841.
  • 842.
  • 843.
  • 844.
  • 845.
  • 846.
  • 847.
  • 848.
  • 849.
  • 850.
  • 851.
  • 852.
  • 853.
  • 854.
  • 855.
  • 856.
  • 857.
  • 858.
  • 859.
  • 860.
  • 861.
  • 862.
  • 863.
  • 864.
  • 865.
  • 866.
  • 867.
  • 868.
  • 869.
  • 870.
  • 871.
  • 872.
  • 873.
  • 874.
  • 875.
  • 876.
  • 877.
  • 878.
  • 879.
  • 880.
  • 881.
  • 882.
  • 883.
  • 884.
  • 885.
  • 886.
  • 887.
  • 888.
  • 889.
  • 890.
  • 891.
  • 892.
  • 893.
  • 894.
  • 895.
  • 896.
  • 897.
  • 898.
  • 899.
  • 900.
  • 901.
  • 902.
  • 903.
  • 904.
  • 905.
  • 906.
  • 907.
  • 908.
  • 909.
  • 910.
  • 911.
  • 912.
  • 913.
  • 914.
  • 915.
  • 916.
  • 917.
  • 918.
  • 919.
  • 920.
  • 921.
  • 922.
  • 923.
  • 924.
  • 925.
  • 926.
  • 927.
  • 928.
  • 929.
  • 930.
  • 931.
  • 932.
  • 933.
  • 934.
  • 935.
  • 936.
  • 937.
  • 938.
  • 939.
  • 940.
  • 941.
  • 942.
  • 943.
  • 944.
  • 945.
  • 946.
  • 947.
  • 948.
  • 949.
  • 950.
  • 951.
  • 952.
  • 953.
  • 954.
  • 955.
  • 956.
  • 957.
  • 958.
  • 959.
  • 960.
  • 961.
  • 962.
  • 963.
  • 964.
  • 965.
  • 966.
  • 967.
  • 968.
  • 969.
  • 970.
  • 971.
  • 972.
  • 973.
  • 974.
  • 975.
  • 976.
  • 977.
  • 978.
  • 979.
  • 980.
  • 981.
  • 982.
  • 983.
  • 984.
  • 985.
  • 986.
  • 987.
  • 988.
  • 989.
  • 990.
  • 991.
  • 992.
  • 993.
  • 994.
  • 995.
  • 996.
  • 997.
  • 998.
  • 999.
  • 1000.
  • 1001.
  • 1002.
  • 1003.
  • 1004.
  • 1005.
  • 1006.
  • 1007.
  • 1008.
  • 1009.
  • 1010.
  • 1011.
  • 1012.
  • 1013.
  • 1014.
  • 1015.
  • 1016.
  • 1017.
  • 1018.
  • 1019.
  • 1020.
  • 1021.
  • 1022.
  • 1023.
  • 1024.
  • 1025.
  • 1026.
  • 1027.
  • 1028.
  • 1029.
  • 1030.
  • 1031.
  • 1032.
  • 1033.
  • 1034.
  • 1035.
  • 1036.
  • 1037.
  • 1038.
  • 1039.
  • 1040.
  • 1041.
  • 1042.
  • 1043.
  • 1044.
  • 1045.
  • 1046.
  • 1047.
  • 1048.
  • 1049.
  • 1050.
  • 1051.
  • 1052.
  • 1053.
  • 1054.
  • 1055.
  • 1056.
  • 1057.
  • 1058.
  • 1059.
  • 1060.
  • 1061.
  • 1062.
  • 1063.
  • 1064.
  • 1065.
  • 1066.
  • 1067.
  • 1068.
  • 1069.
  • 1070.
  • 1071.
  • 1072.
  • 1073.
  • 1074.
  • 1075.
  • 1076.
  • 1077.
  • 1078.
  • 1079.
  • 1080.
  • 1081.
  • 1082.
  • 1083.
  • 1084.
  • 1085.
  • 1086.
  • 1087.
  • 1088.
  • 1089.
  • 1090.
  • 1091.
  • 1092.
  • 1093.
  • 1094.
  • 1095.
  • 1096.
  • 1097.
  • 1098.
  • 1099.
  • 1100.
  • 1101.
  • 1102.
  • 1103.
  • 1104.
  • 1105.
  • 1106.
  • 1107.
  • 1108.
  • 1109.
  • 1110.
  • 1111.
  • 1112.
  • 1113.
  • 1114.
  • 1115.
  • 1116.
  • 1117.
  • 1118.
  • 1119.
  • 1120.
  • 1121.
  • 1122.
  • 1123.
  • 1124.
  • 1125.
  • 1126.
  • 1127.
  • 1128.
  • 1129.
  • 1130.
  • 1131.
  • 1132.
  • 1133.
  • 1134.
  • 1135.
  • 1136.
  • 1137.
  • 1138.
  • 1139.
  • 1140.
  • 1141.
  • 1142.
  • 1143.
  • 1144.
  • 1145.
  • 1146.
  • 1147.
  • 1148.
  • 1149.
  • 1150.
  • 1151.
  • 1152.
  • 1153.
  • 1154.
  • 1155.
  • 1156.
  • 1157.
  • 1158.
  • 1159.
  • 1160.
  • 1161.
  • 1162.
  • 1163.
  • 1164.
  • 1165.
  • 1166.
  • 1167.
  • 1168.
  • 1169.
  • 1170.
  • 1171.
  • 1172.
  • 1173.
  • 1174.
  • 1175.
  • 1176.
  • 1177.
  • 1178.
  • 1179.
  • 1180.
  • 1181.
  • 1182.
  • 1183.
  • 1184.
  • 1185.
  • 1186.
  • 1187.
  • 1188.
  • 1189.
  • 1190.
  • 1191.
  • 1192.
  • 1193.
  • 1194.
  • 1195.
  • 1196.
  • 1197.
  • 1198.
  • 1199.
  • 1200.
  • 1201.
  • 1202.
  • 1203.
  • 1204.
  • 1205.
  • 1206.
  • 1207.
  • 1208.
  • 1209.
  • 1210.
  • 1211.
  • 1212.
  • 1213.
  • 1214.
  • 1215.
  • 1216.
  • 1217.
  • 1218.
  • 1219.
  • 1220.
  • 1221.
  • 1222.
  • 1223.
  • 1224.
  • 1225.
  • 1226.
  • 1227.
  • 1228.
  • 1229.
  • 1230.
  • 1231.
  • 1232.
  • 1233.
  • 1234.
  • 1235.
  • 1236.
  • 1237.
  • 1238.
  • 1239.
  • 1240.
  • 1241.
  • 1242.
  • 1243.
  • 1244.
  • 1245.
  • 1246.
  • 1247.
  • 1248.
  • 1249.
  • 1250.
  • 1251.
  • 1252.
  • 1253.
  • 1254.
  • 1255.
  • 1256.
  • 1257.
  • 1258.
  • 1259.
  • 1260.
  • 1261.
  • 1262.
  • 1263.
  • 1264.
  • 1265.
  • 1266.
  • 1267.
  • 1268.
  • 1269.
  • 1270.
  • 1271.
  • 1272.
  • 1273.
  • 1274.
  • 1275.
  • 1276.
  • 1277.
  • 1278.
  • 1279.
  • 1280.
  • 1281.
  • 1282.
  • 1283.
  • 1284.
  • 1285.
  • 1286.
  • 1287.
  • 1288.
  • 1289.
  • 1290.
  • 1291.
  • 1292.
  • 1293.
  • 1294.
  • 1295.
  • 1296.
  • 1297.
  • 1298.
  • 1299.
  • 1300.
  • 1301.
  • 1302.
  • 1303.
  • 1304.
  • 1305.
  • 1306.
  • 1307.
  • 1308.
  • 1309.
  • 1310.
  • 1311.
  • 1312.
  • 1313.
  • 1314.
  • 1315.
  • 1316.
  • 1317.
  • 1318.
  • 1319.
  • 1320.
  • 1321.
  • 1322.
  • 1323.
  • 1324.
  • 1325.
  • 1326.
  • 1327.
  • 1328.
  • 1329.
  • 1330.
  • 1331.
  • 1332.
  • 1333.
  • 1334.
  • 1335.
  • 1336.
  • 1337.
  • 1338.
  • 1339.
  • 1340.
  • 1341.
  • 1342.
  • 1343.
  • 1344.
  • 1345.
  • 1346.
  • 1347.
  • 1348.
  • 1349.
  • 1350.
  • 1351.
  • 1352.
  • 1353.
  • 1354.
  • 1355.
  • 1356.
  • 1357.
  • 1358.
  • 1359.
  • 1360.
  • 1361.
  • 1362.
  • 1363.
  • 1364.
  • 1365.
  • 1366.
  • 1367.
  • 1368.
  • 1369.
  • 1370.
  • 1371.
  • 1372.
  • 1373.
  • 1374.
  • 1375.
  • 1376.
  • 1377.
  • 1378.
  • 1379.
  • 1380.
  • 1381.
  • 1382.
  • 1383.
  • 1384.
  • 1385.
  • 1386.
  • 1387.
  • 1388.
  • 1389.
  • 1390.
  • 1391.
  • 1392.
  • 1393.
  • 1394.
  • 1395.
  • 1396.
  • 1397.
  • 1398.
  • 1399.
  • 1400.
  • 1401.
  • 1402.
  • 1403.
  • 1404.
  • 1405.
  • 1406.
  • 1407.
  • 1408.
  • 1409.
  • 1410.
  • 1411.
  • 1412.
  • 1413.
  • 1414.
  • 1415.
  • 1416.
  • 1417.
  • 1418.
  • 1419.
  • 1420.
  • 1421.
  • 1422.
  • 1423.
  • 1424.
  • 1425.
  • 1426.
  • 1427.
  • 1428.
  • 1429.
  • 1430.
  • 1431.
  • 1432.
  • 1433.
  • 1434.
  • 1435.
  • 1436.
  • 1437.
  • 1438.
  • 1439.
  • 1440.
  • 1441.
  • 1442.
  • 1443.
  • 1444.
  • 1445.
  • 1446.
  • 1447.
  • 1448.
  • 1449.
  • 1450.
  • 1451.
  • 1452.
  • 1453.
  • 1454.
  • 1455.
  • 1456.
  • 1457.
  • 1458.
  • 1459.
  • 1460.
  • 1461.
  • 1462.
  • 1463.
  • 1464.
  • 1465.
  • 1466.
  • 1467.
  • 1468.
  • 1469.
  • 1470.
  • 1471.
  • 1472.
  • 1473.
  • 1474.
  • 1475.
  • 1476.
  • 1477.
  • 1478.
  • 1479.
  • 1480.
  • 1481.
  • 1482.
  • 1483.
  • 1484.
  • 1485.
  • 1486.
  • 1487.
  • 1488.

总结

RTSP转RTMP模块设计,可以用ffmpeg直接命令行转发,也可以用方案二的非常成熟的转发设计,ffmpeg转发,需要有一定的代码基础,有问题的话,bug修复需要对底层逻辑非常了解才行,方案二,技术成熟,二次开发难度不大,很同意集成到自己现有系统,感兴趣的开发者,可以单独跟我沟通交流。