博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用ffmpeg添加rtsp字幕流 (t140)
阅读量:4071 次
发布时间:2019-05-25

本文共 5300 字,大约阅读时间需要 17 分钟。

如有错误请指正,谢谢。

使用ffmpeg添加rtsp字幕流 (t140)

使用ffmpeg推送一个视频文件到rtsp非常简单:

ffmpeg -re -i subtitle.mkv -vcodec copy -acodec copy -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:10554/sVideo

但如果想要把视频中的字幕流也推送到rtsp服务器上却不行:

ffmpeg -re -i subtitle.mkv -vcodec copy -acodec copy -scodec copy -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:10554/sVideo

提示:[rtp @ 0503fd00] Unsupported codec ass

所以要使用ffmpeg推送rtsp字幕流必须要修改源码, 关于rtp载荷文本流的协议描述为RFC4103。

1.使rtp打包支持subtitle形式的编码

要使rtp打包时支持相关字幕编码器, 必须修改rtpenc.c中的 is_supported函数, 在最后加入AV_CODEC_ID_ASS(ass字幕), AV_CODEC_ID_TEXT(纯文本字幕流)

static int is_supported(enum AVCodecID id){    switch(id) {    case AV_CODEC_ID_DIRAC:    case AV_CODEC_ID_H261:    ...    case AV_CODEC_ID_TEXT:    case AV_CODEC_ID_ASS:        return 1;    default:        return 0;    }}

2.使创建sdp时生成subtitle相关字段

rtsp对可用媒体流的数量及属性判断是通过SDP, 所以有字幕流的rtsp,需要在创建rtsp时一并将字幕流的信息也创建完成。

修改sdp.c sdp_write_media_attributes函数,指定subtitle编码器对应的流媒体描述。指定AV_CODEC_ID_TEXT编码器的名称为t140, AV_CODEC_ID_ASS编码器的名称为ass。ffmpeg解析rtsp的sdp时会根据rtpmap中的编码器名称去寻找对应的编码属性,生成相关的媒体流。

sdp.c:sdp_write_media_attributes 函数修改节选

switch (p->codec_id) {        case AV_CODEC_ID_DIRAC:            av_strlcatf(buff, size, "a=rtpmap:%d VC2/90000\r\n", payload_type);            break;        case AV_CODEC_ID_H264: {            int mode = 1;            if (fmt && fmt->oformat && fmt->oformat->priv_class &&                av_opt_flag_is_set(fmt->priv_data, "rtpflags", "h264_mode0"))                mode = 0;            if (p->extradata_size) {                config = extradata2psets(fmt, p);            }            av_strlcatf(buff, size, "a=rtpmap:%d H264/90000\r\n"                                    "a=fmtp:%d packetization-mode=%d%s\r\n",                                     payload_type,                                     payload_type, mode, config ? config : "");            break;        }        ......        case AV_CODEC_ID_TEXT:            av_strlcatf(buff, size, "a=rtpmap:%d t140/1000\r\n", payload_type);            break;        case AV_CODEC_ID_ASS:            av_strlcatf(buff, size, "a=rtpmap:%d ass/1000\r\n", payload_type);            break;        default:            /* Nothing special to do here... */            break;

截止目前,使用ffmpeg推送rtsp字幕流的源码修改已经完成。

但若想使用ffmpeg播放非t140字幕编码的字幕流, 还差最后一步。

3.使sdp中的subtitle可以正确被解析

假设有以下sdp信息:

v=0o=- 0 0 IN IP4 127.0.0.1s=No Namec=IN IP4 192.168.1.180t=0 0a=tool:libavformat 56.40.101m=video 0 RTP/AVP 96a=rtpmap:96 H264/90000a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z24AMqbMpgHgCJ+XARAAGXTwBMS0CPGDGaA=,aOl4/LA=; profile-level-id=6E0032a=control:streamid=0m=audio 0 RTP/AVP 97a=rtpmap:97 MPEG4-GENERIC/48000/2a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=119056E500a=control:streamid=1m=text 0 RTP/AVP 98a=rtpmap:98 t140/1000m=text 0 RTP/AVP 99a=rtpmap:99 ass/1000

以上文本信息描述了两个subtitle流:一个文本流(t140), 一个ass字幕流(ass)。

ffmpeg在解析sdp时会创建两个AVStream, 该AVStream的codec_type为AVMEDIA_TYPE_SUBTITLE, codec_id会调用 ff_rtp_handler_find_by_name 方法进行寻找。

看下ff_rtp_handler_find_by_name的具体实现

const RTPDynamicProtocolHandler *ff_rtp_handler_find_by_name(const char *name,     enum AVMediaType codec_type)    {        void *i = 0;        const RTPDynamicProtocolHandler *handler;        while (handler = ff_rtp_handler_iterate(&i)) {            if (handler->enc_name &&                !av_strcasecmp(name, handler->enc_name) &&                codec_type == handler->codec_type)                return handler;        }        return NULL;    }

ff_rtp_handler_iterate实现:

const RTPDynamicProtocolHandler *ff_rtp_handler_iterate(void **opaque)    {        uintptr_t i = (uintptr_t)*opaque;        const RTPDynamicProtocolHandler *r = rtp_dynamic_protocol_handler_list[i];        if (r)            *opaque = (void*)(i + 1);        return r;    }

即通过遍历rtpdec.c中的rtp_dynamic_protocol_handler_list去寻找符合要求的RTPDynamicProtocolHandler,所以我们需要在rtpdec.c中添加有关ass编码器的RTPDynamicProtocolHandler,同时将对应的地址加入到list中.

注意:

          或许因为rfc标准的原因,t140对应的动态协议已经被实现了, 所以若是推送的rtsp字幕流的编码器名称为t140,则无需修改ffmpeg源码也可以进行解析。

static RTPDynamicProtocolHandler t140_dynamic_handler = { /* RFC 4103 */    .enc_name   = "t140",    .codec_type = AVMEDIA_TYPE_SUBTITLE,    .codec_id   = AV_CODEC_ID_TEXT,};static RTPDynamicProtocolHandler ass_dynamic_handler = {    .enc_name   = "ass",    .codec_type = AVMEDIA_TYPE_SUBTITLE,    .codec_id   = AV_CODEC_ID_ASS,};static const RTPDynamicProtocolHandler *rtp_dynamic_protocol_handler_list[] = {    /* rtp */    &ff_ac3_dynamic_handler,    ......    &t140_dynamic_handler,    &ass_dynamic_handler,    ......};

至此,修改完毕。

4.代码添加text字幕流

一段代码节选,添加一个字幕流到rtsp流中:

bool RtspPusher::AddInfoStream(unsigned &streamIndex)    {        if(!m_outputCtx) //AVFormatContext            return false;        AVStream * outStream = avformat_new_stream(m_outputCtx, NULL);        AVCodec * encoder = avcodec_find_encoder(AV_CODEC_ID_TEXT);        if(encoder == NULL)            return false;        AVCodecContext * encoderContext = avcodec_alloc_context3(encoder);        int ret = avcodec_parameters_from_context(outStream->codecpar, encoderContext);        avcodec_free_context(&encoderContext);        if(ret < 0)            return false;        outStream->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;        streamIndex = m_outputCtx->nb_streams - 1;        return true;    }

欢迎转载:

你可能感兴趣的文章
【leetcode】Linked List Cycle (python)
查看>>
【leetcode】Candy(python)
查看>>
【leetcode】Sum Root to leaf Numbers
查看>>
【leetcode】Pascal's Triangle II (python)
查看>>
java自定义容器排序的两种方法
查看>>
如何成为编程高手
查看>>
本科生的编程水平到底有多高
查看>>
备忘:java中的递归
查看>>
Solr及Spring-Data-Solr入门学习
查看>>
python_time模块
查看>>
python_configparser(解析ini)
查看>>
selenium学习资料
查看>>
<转>文档视图指针互获
查看>>
从mysql中 导出/导入表及数据
查看>>
HQL语句大全(转)
查看>>
几个常用的Javascript字符串处理函数 spilt(),join(),substring()和indexof()
查看>>
javascript传参字符串 与引号的嵌套调用
查看>>
swiper插件的的使用
查看>>
layui插件的使用
查看>>
JS牛客网编译环境的使用
查看>>