在 FFmpeg 的 libavformat 中修改 RTMP
Modify RTMP inside FFmpeg's libavformat
我需要向我的应用程序流式传输到的 RTMP 服务器发送 Stream Dry 消息。我创建了一个在 avformat.h 上声明并在 rtmpproto.c 上定义的新函数,其中包含以下内容:
int av_send_rtmp_streamdry(struct URLContext *s) {
RTMPContext *rt = s->priv_data;
PutByteContext pbc;
RTMPPacket spkt = { 0 };
int ret;
uint8_t *p;
av_log(s, AV_LOG_INFO, "%p", rt);
// Create StreamDry packet
// The packet type is the same as the PING response type. From RTMP spec,
// packet type 4 belongs to User Control Messages.
// The packet size is Event Type (16 bits / 2 bytes) + Stream ID (4 bytes)
if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
RTMP_PT_PING, 0, 6)) < 0) {
av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
return ret;
}
p = spkt.data;
bytestream2_init_writer(&pbc, spkt.data, spkt.size);
bytestream2_put_be16(&pbc, 2); // 2 -> Stream Dry
bytestream2_put_be32(&pbc, rt->stream_id);
spkt.size = p - spkt.data;
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
&rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
if(ret != 0){
av_log(NULL, AV_LOG_ERROR, "Stream Dry packet could not be sent");
return ret;
}
ff_rtmp_packet_destroy(&spkt);
return 0;}
我通过 static_cast<URLContext*>(ofmt_ctx->pb->opaque)
获取 URLContext,其中 ofmt 是我的 AVFormatContext。
我直接从我的程序中调用此方法,但问题是 s->priv_data 内容几乎不是有效指针。
我该如何实现?
我终于可以实现那个功能了。问题是试图从 AVFormatContext 获取 RTMPContext 的转换问题。
AVFormatContext 字段 'pb' 包含一个 AVIOContext*。
AVIOContext 字段'opaque' 包含一个void*,但在我的例子中(我不知道它是否总是这样)是一个AVIOInternal*。 AVIOInternal 包含我需要的 URLContext*,但由于它是在 'aviobuf.c' 文件中定义的,所以我无法访问它,但它只包含一个字段。所以解决方案是绕过 AVIOInternal 结构将 'opaque' 字段从 AVIOContext 转换为 URLContext**。
所以下面的代码就是解决方案:
AVIOContext *io = output->pb;
URLContext *url = *((URLContext**)(io->opaque));
RTMPContext *rt =(RTMPContext*)(url->priv_data);
编辑:这是完整的工作代码。
int av_send_rtmp_streamdry(AVFormatContext *output) {
AVIOContext *io = output->pb;
URLContext *url = *((URLContext**)(io->opaque));
RTMPContext *rt =(RTMPContext*)(url->priv_data);
PutByteContext pbc;
RTMPPacket spkt = { 0 };
int ret;
uint8_t *p;
// Create StreamDry packet
// The packet type is the same as the PING response type. From RTMP spec,
// packet type 4 belongs to User Control Messages.
// The packet size is Event Type (16 bits / 2 bytes) + Stream ID (4 bytes)
if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
RTMP_PT_PING, 0, 6)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Unable to create response packet\n");
return ret;
}
bytestream2_init_writer(&pbc, spkt.data, spkt.size);
bytestream2_put_be16(&pbc, 2); // 2 -> Stream Dry
bytestream2_put_be32(&pbc, rt->stream_id);
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
&rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&spkt);
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "Stream Dry packet could not be sent");
return ret;
}
return 0;
}
我需要向我的应用程序流式传输到的 RTMP 服务器发送 Stream Dry 消息。我创建了一个在 avformat.h 上声明并在 rtmpproto.c 上定义的新函数,其中包含以下内容:
int av_send_rtmp_streamdry(struct URLContext *s) {
RTMPContext *rt = s->priv_data;
PutByteContext pbc;
RTMPPacket spkt = { 0 };
int ret;
uint8_t *p;
av_log(s, AV_LOG_INFO, "%p", rt);
// Create StreamDry packet
// The packet type is the same as the PING response type. From RTMP spec,
// packet type 4 belongs to User Control Messages.
// The packet size is Event Type (16 bits / 2 bytes) + Stream ID (4 bytes)
if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
RTMP_PT_PING, 0, 6)) < 0) {
av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
return ret;
}
p = spkt.data;
bytestream2_init_writer(&pbc, spkt.data, spkt.size);
bytestream2_put_be16(&pbc, 2); // 2 -> Stream Dry
bytestream2_put_be32(&pbc, rt->stream_id);
spkt.size = p - spkt.data;
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
&rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
if(ret != 0){
av_log(NULL, AV_LOG_ERROR, "Stream Dry packet could not be sent");
return ret;
}
ff_rtmp_packet_destroy(&spkt);
return 0;}
我通过 static_cast<URLContext*>(ofmt_ctx->pb->opaque)
获取 URLContext,其中 ofmt 是我的 AVFormatContext。
我直接从我的程序中调用此方法,但问题是 s->priv_data 内容几乎不是有效指针。
我该如何实现?
我终于可以实现那个功能了。问题是试图从 AVFormatContext 获取 RTMPContext 的转换问题。
AVFormatContext 字段 'pb' 包含一个 AVIOContext*。
AVIOContext 字段'opaque' 包含一个void*,但在我的例子中(我不知道它是否总是这样)是一个AVIOInternal*。 AVIOInternal 包含我需要的 URLContext*,但由于它是在 'aviobuf.c' 文件中定义的,所以我无法访问它,但它只包含一个字段。所以解决方案是绕过 AVIOInternal 结构将 'opaque' 字段从 AVIOContext 转换为 URLContext**。 所以下面的代码就是解决方案:
AVIOContext *io = output->pb;
URLContext *url = *((URLContext**)(io->opaque));
RTMPContext *rt =(RTMPContext*)(url->priv_data);
编辑:这是完整的工作代码。
int av_send_rtmp_streamdry(AVFormatContext *output) {
AVIOContext *io = output->pb;
URLContext *url = *((URLContext**)(io->opaque));
RTMPContext *rt =(RTMPContext*)(url->priv_data);
PutByteContext pbc;
RTMPPacket spkt = { 0 };
int ret;
uint8_t *p;
// Create StreamDry packet
// The packet type is the same as the PING response type. From RTMP spec,
// packet type 4 belongs to User Control Messages.
// The packet size is Event Type (16 bits / 2 bytes) + Stream ID (4 bytes)
if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
RTMP_PT_PING, 0, 6)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Unable to create response packet\n");
return ret;
}
bytestream2_init_writer(&pbc, spkt.data, spkt.size);
bytestream2_put_be16(&pbc, 2); // 2 -> Stream Dry
bytestream2_put_be32(&pbc, rt->stream_id);
ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
&rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
ff_rtmp_packet_destroy(&spkt);
if(ret < 0){
av_log(NULL, AV_LOG_ERROR, "Stream Dry packet could not be sent");
return ret;
}
return 0;
}