基于FFmpeg源码分析HLS拉流

原因:上个博客介绍了HLS的服务器搭建以及ffplay拉流播放,接下来分析ffmpeg针对hls处理的源码

概述:首先通过http请求获取m3u8文件,通过解析m3u8文件获取切片最早的的ts文件获取常规的AVFormatContext信息。代码主要实现在hls.c文件.

伪代码如下:可以看出针对hls的协议处理主要实现是在avformat_open_input中,具体细节接下来分析。

AVFormatContext *pFormatCtx = nullptr;
char *purl = "http://192.168.27.161:8080/hls/q.m3u8";

int ir = avformat_open_input(&pFormatCtx, purl, nullptr, nullptr);
if (ir != 0)
{
	printf("avformat_open_input Failed.\n");
}

首先进行http的连通伪代码如下:首先通过init_input进行m3u8的数据请求。首先通过ffurl_open_whitelist进行tcp连接,然后通过http_connect方法进行get请求。请求成功后接下来则然后通过av_probe_input_buffer2方法进行数据获取。

utils.c
/* Open input file and probe the format if necessary. */
static int init_input(AVFormatContext *s, const char *filename,
                      AVDictionary **options)
{
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
        return ret;

eturn av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                 s, 0, s->format_probesize);
}

http.c
static int http_open(URLContext *h, const char *uri, int flags,
                     AVDictionary **options)
{

     ret = http_open_cnx(h, options);
}

static int http_open_cnx(URLContext *h, AVDictionary **options)
{
      location_changed = http_open_cnx_internal(h, options);
}

static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
{
    //buf=="tcp://192.168.27.161:8080"
     err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
                                   &h->interrupt_callback, options,
                                   h->protocol_whitelist, h->protocol_blacklist, h);

    //local_path= "/hls/q.m3u8"
     err = http_connect(h, path, local_path, hoststr,
                       auth, proxyauth, &location_changed);
}

static int http_connect(URLContext *h, const char *path, const char *local_path,
                        const char *hoststr, const char *auth,
                        const char *proxyauth, int *new_location)
{
//"GET /hls/q.m3u8 HTTP/1.1\r\nUser-Agent: Lavf/57.72.101\r\nAccept: */*\r\nRange: bytes=0-\r\nConnection: close\r\nHost: 192.168.27.161:8080\r\nIcy-MetaData: 1\r\n\r\n"
    
 if ((err = ffurl_write(s->hd, s->buffer, strlen(s->buffer))) < 0)
        goto done;

}

利用av_probe_input_buffer2方法进行m3u8文件获取伪代码如下:通过av_probe_input_buffer2获取对应的m3u8文件信息.

int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,
                          const char *filename, void *logctx,
                          unsigned int offset, unsigned int max_probe_size)
{

 if ((ret = avio_read(pb, buf + buf_offset,
                             probe_size - buf_offset)) < 0) {
    }
}

static int http_read(URLContext *h, uint8_t *buf, int size)
{
     size = http_read_stream(h, buf, size);
}

接下来则通过m3u8文件进行解析和读取第一个ts文件流伪代码如下:通过parse_playlist将m3u8文件中ts信息进行解析和存储。然后通过av_probe_input_buffer获取第一个ts文件信息.然后获取对应的AVFormatContext.通过第一个信息用于初始化媒体信息。

int avformat_open_input(AVFormatContext **ps, const char *filename,
                        AVInputFormat *fmt, AVDictionary **options)
{

if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
        if ((ret = s->iformat->read_header(s)) < 0)
            goto fail;

}

static int hls_read_header(AVFormatContext *s)
{
 if ((ret = parse_playlist(c, s->filename, NULL, s->pb)) < 0)
        goto fail;

   for (i = 0; i < c->n_playlists; i++) {
        ret = av_probe_input_buffer(&pls->pb, &in_fmt, pls->segments[0]->url,
                                    NULL, 0, 0);

        ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, NULL);
        if (ret < 0)
            goto fail;

    }
}

 

总结:通过FFmpeg针对hls的简单分析可知,首先进行http连接,然后获取m3u8文件,然后解析m3u8文件并获取第一个ts流数据信息用于初始化AVFormatContext.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值