基于FFmpeg源码分析HLS拉流

本文分析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.

本文结尾底部,领取最新最全C++音视频学习提升资料,内容包括(C/C++Linux 服务器开发,FFmpeg webRTC rtmp hls rtsp ffplay srs

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125223725