Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

是否可以直接使用本库的RTMP协议推流? #35

Closed
LuoZijun opened this issue Apr 7, 2023 · 5 comments
Closed

是否可以直接使用本库的RTMP协议推流? #35

LuoZijun opened this issue Apr 7, 2023 · 5 comments
Labels
bug Something isn't working

Comments

@LuoZijun
Copy link

LuoZijun commented Apr 7, 2023

你好,我看到文档里面对于推流到RTMP服务器,建议采用通过 OBS 和 FFMPEG 这两种工具来做。但是我的推流过程当中可能有一些工作需要做,所以我是否可以直接通过本库的 RTMP 协议推流到一个远端的 RTMP 服务器呢?

我目前的使用场景是:对一个 HLS 流做一些处理后,然后推流给远端的 RTMP 服务器(B站),但是我对于 RTMP 协议不甚了解,不知作者能在代码 RTMP 推流这块能给一些 Example 吗?谢谢。

另外,我在 RTMP 协议代码里面的 ClientSession 里面,似乎也未曾看到 Auth 这块 -_-

@harlanc
Copy link
Owner

harlanc commented Apr 7, 2023

我理解你的这个场景需要把hls协议的数据转成rtmp协议的数据,然后推送到B站?现在还不支持hls到rtmp的转换。

Auth你指的是鉴权吗?正在开发中

@LuoZijun
Copy link
Author

LuoZijun commented Apr 7, 2023

把hls协议的数据转成rtmp协议的数据,然后推送到B站?现在还不支持hls到rtmp的转换。

是的,将 HLS 流的数据,推送到 B站 的 RTMP 服务器。但是这个 HLS 到 RTMP 的转换,我可以自己来做。我自己有一些音视频处理方面的经验。我想,把 视频流音频流 的数据放进 RTMP 的 Message 里面,应该不是一件太困难的事情(对于常见的 Codec 而言 )。

Auth你指的是鉴权吗?

一些直播网站,会开放给你一个 RTMP 的地址(也可能还有其它的协议),同时有一些身份上面的数据。我想,推流到服务器时,该服务器会对这些身份信息做鉴定。比如 B站 的数据是这样的:

服务器地址: rtmp://live-push.bilivideo.com/live-bvc/
身份码:CCTTTT783****
串流密钥:?streamname=live_883*********&key=54********&schedule=rtmp&pflag=1

# `live-bvc` 就是 RTMP 里面的 `app_name`

但是如何把这个身份信息,通过 RTMP 库的 ClientSession 发送给 RTMP Server,我目前不太清楚。

如果有比较高层的 API ,我想应该容易很多,比如像这样:

const RTMP_DEFAULT_PORT: u16 = 1935u16;

struct RtmpClietConfig {
	app_name: String,
	stream_name: String,
	identity: String,
	secret: String,
}

impl Client {
	pub fn connect<A: IntoScoketAddr>(addr: A, config: RtmpClietConfig) -> Result<Self, std::io::Error> {
		let mut stream = std::net::TcpStream::connect(addr)?;
		rtmp_handshake(&mut stream, &config)?;
		Ok(Self { stream: stream, config: config })
	}
	pub fn publish(&mut self) -> Publisher;
	pub fn playback(&mut self) -> Playback;
}

struct Playback { }
struct Publisher { }

impl Publisher {
	// 写入 HLS 的 TS Packet,可能需要转换容器的格式。
	pub fn write_video_segment(&mut self, segment: Segment) -> Result<(), std::io::Error>;
	pub fn write_audio_segment(&mut self, segment: Segment) -> Result<(), std::io::Error>;
}

impl Playback {
	pub fn read_video_segment(&mut self, buf: &mut Segment) -> Result<(), std::io::Error>;
	pub fn read_audio_segment(&mut self, buf: &mut Segment) -> Result<(), std::io::Error>;
}

@harlanc
Copy link
Owner

harlanc commented Apr 8, 2023

@LuoZijun

我在dev分支写了一个pullrtmppushrtmp的例子,你看下。

你如果实现了HLS2RTMP的逻辑,RTMP library是通过rust channel把音视频数据发送到Transmitter的,用下面的enum封起来:

pub enum ChannelData {
    Video { timestamp: u32, data: BytesMut },
    Audio { timestamp: u32, data: BytesMut },
    MetaData { timestamp: u32, data: BytesMut },
}

我理解你的Auth的意思,就是使用本库的RTMP推流,需要推流地址携带鉴权信息发送到B站,才能推流成功,应该现在就支持,你用我给的demo试下吧

@harlanc
Copy link
Owner

harlanc commented Apr 9, 2023

测试出来一个拉第三方流,转推RTMP,拉不下来流的问题,RTMP协议信令发送应该有问题。

@harlanc harlanc added the bug Something isn't working label Apr 9, 2023
@harlanc
Copy link
Owner

harlanc commented Apr 22, 2023

bug修复了,完善了推拉流demo:

Usage: pprtmp --pull_rtmp_url <path> --push_rtmp_url <path>

Options:
  -i, --pull_rtmp_url <path>  Specify the pull rtmp url.
  -o, --push_rtmp_url <path>  Specify the push rtmp url.
  -h, --help                  Print help
  -V, --version               Print version

把抽象出API来作为SDK使用的想法很好,但是现在还做不到,但是client_session里面的逻辑我感觉比较清楚,如果推流的话,会订阅channels里面的音视频数据,然后发送出去:

   self.state = ClientSessionState::StartPublish;
                //subscribe from local session and publish to remote rtmp server
                if let (Some(app_name), Some(stream_name)) =
                    (&self.sub_app_name, &self.sub_stream_name)
                {
                    self.common
                        .subscribe_from_channels(
                            app_name.clone(),
                            stream_name.clone(),
                            self.session_id,
                        )
                        .await?;
                } else {
                    self.common
                        .subscribe_from_channels(
                            self.app_name.clone(),
                            self.stream_name.clone(),
                            self.session_id,
                        )
                        .await?;
                }

我觉得有两种做法,一种是把hls提取出来的音视频数据/metadata pushlish到channel里面,然后订阅,另一种是绕过channel,直接抽象出来API来写音视频数据/metadata,我有时间可以弄下,或者你也可以尝试弄一下^_^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants