看下直播m3u8结构:
-
#EXTM3U
-
#EXT-X-MEDIA-SEQUENCE:3918
-
#EXT-X-TARGETDURATION:10
-
#EXTINF:10,
-
2017071806/1500358480.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358490.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358500.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358510.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358520.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358535.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358545.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358555.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358565.ts?type=hls_live_slice
-
#EXTINF:10,
-
2017071806/1500358575.ts?type=hls_live_slice
看下点播m3u8结构:
-
#EXTM3U
-
#EXT-X-VERSION:3
-
#EXT-X-MEDIA-SEQUENCE:0
-
#EXT-X-TARGETDURATION:19
-
#EXT-X-PLAYLIST-TYPE:VOD
-
#EXTINF:9.800,
-
00_d0024036a7j.320086.1.ts?index=0&start=0&end=9800&brs=0&bre=1509827&ver=4
-
#EXTINF:10.480,
-
01_d0024036a7j.320086.1.ts?index=1&start=9800&end=20280&brs=1509828&bre=3828055&ver=4
-
#EXTINF:9.960,
-
02_d0024036a7j.320086.1.ts?index=2&start=20280&end=30240&brs=3828056&bre=6248743&ver=4
-
#EXTINF:9.520,
-
03_d0024036a7j.320086.1.ts?index=3&start=30240&end=39760&brs=6248744&bre=8234399&ver=4
-
#EXTINF:9.920,
-
04_d0024036a7j.320086.1.ts?index=4&start=39760&end=49680&brs=8234400&bre=10619931&ver=4
-
#EXTINF:9.960,
-
05_d0024036a7j.320086.1.ts?index=5&start=49680&end=59640&brs=10619932&bre=13400639&ver=4
-
#EXTINF:9.720,
-
06_d0024036a7j.320086.1.ts?index=6&start=59640&end=69360&brs=13400640&bre=16777683&ver=4
-
#EXTINF:10.880,
-
07_d0024036a7j.320086.1.ts?index=7&start=69360&end=80240&brs=16777684&bre=19713867&ver=4
-
...省略
-
#EXTINF:9.960,
-
029_d0024036a7j.320086.2.ts?index=29&start=295520&end=305480&brs=0&bre=1287611&ver=4
-
#EXTINF:9.040,
-
...省略
-
#EXTINF:7.240,
-
0315_d0024036a7j.320086.11.ts?index=315&start=3181360&end=3188600&brs=30080752&bre=31204615&ver=4
-
#EXT-X-ENDLIST
区别:
-
1、 每个TS分片时间,通过标签EXTINF,后面有的是float型,有的是int型,一般为10s(也有不会10s的),最后如果没有10s,就取对应时间。
-
2、不是所有点播m3u8中都有EXT-X-PLAYLIST-TYPE,表明类型。最直接区分就是直播m3u8没有EXT-X-ENDLIST标签,因为是实时流,自然不会有结束,否则就是点播流了。
FFmpeg是如何解析直播,点播的HLS?在\libavformat\hlsproto.c中,就是实现步骤,先打开m3u8文件,然后parse。
-
static int hls_open(URLContext *h, const char *uri, int flags)
-
{
-
HLSContext *s = h->priv_data;
-
int ret, i;
-
const char *nested_url;
-
-
if (flags & AVIO_FLAG_WRITE)
-
return AVERROR(ENOSYS);
-
-
h->is_streamed = 1;
-
-
if (av_strstart(uri, "hls+", &nested_url)) {
-
av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));
-
} else if (av_strstart(uri, "hls://", &nested_url)) {
-
av_log(h, AV_LOG_ERROR,
-
"No nested protocol specified. Specify e.g. hls+http://%s\n",
-
nested_url);
-
ret = AVERROR(EINVAL);
-
goto fail;
-
} else {
-
av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
-
ret = AVERROR(EINVAL);
-
goto fail;
-
}
-
av_log(h, AV_LOG_WARNING,
-
"Using the hls protocol is discouraged, please try using the "
-
"hls demuxer instead. The hls demuxer should be more complete "
-
"and work as well as the protocol implementation. (If not, "
-
"please report it.) To use the demuxer, simply use %s as url.\n",
-
s->playlisturl);
-
-
if ((ret = parse_playlist(h, s->playlisturl)) < 0)
-
goto fail;
-
-
if (s->n_segments == 0 && s->n_variants > 0) {
-
int max_bandwidth = 0, maxvar = -1;
-
for (i = 0; i < s->n_variants; i++) {
-
if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {
-
max_bandwidth = s->variants[i]->bandwidth;
-
maxvar = i;
-
}
-
}
-
av_strlcpy(s->playlisturl, s->variants[maxvar]->url,
-
sizeof(s->playlisturl));
-
if ((ret = parse_playlist(h, s->playlisturl)) < 0)
-
goto fail;
-
}
-
-
if (s->n_segments == 0) {
-
av_log(h, AV_LOG_WARNING, "Empty playlist\n");
-
ret = AVERROR(EIO);
-
goto fail;
-
}
-
s->cur_seq_no = s->start_seq_no;
-
if (!s->finished && s->n_segments >= 3)
-
s->cur_seq_no = s->start_seq_no + s->n_segments - 3;
-
-
return 0;
-
-
fail:
-
hls_close(h);
-
return ret;
-
}
解析playlist中的ts流
-
static int parse_playlist(URLContext *h, const char *url)
-
{
-
HLSContext *s = h->priv_data;
-
AVIOContext *in;
-
int ret = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
-
int64_t duration = 0;
-
char line[1024];
-
const char *ptr;
-
-
if ((ret = ffio_open_whitelist(&in, url, AVIO_FLAG_READ,
-
&h->interrupt_callback, NULL,
-
h->protocol_whitelist, h->protocol_blacklist)) < 0)
-
return ret;
-
-
read_chomp_line(in, line, sizeof(line));
-
if (strcmp(line, "#EXTM3U")) {
-
ret = AVERROR_INVALIDDATA;
-
goto fail;
-
}
-
-
free_segment_list(s);
-
s->finished = 0;
-
while (!avio_feof(in)) {
-
read_chomp_line(in, line, sizeof(line));
-
if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
-
struct variant_info info = {{0}};
-
is_variant = 1;
-
ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
-
&info);
-
bandwidth = atoi(info.bandwidth);
-
} else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
-
s->target_duration = atoi(ptr) * AV_TIME_BASE;
-
} else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
-
s->start_seq_no = atoi(ptr);
-
} else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
-
s->finished = 1;
-
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
-
is_segment = 1;
-
duration = atof(ptr) * AV_TIME_BASE;
-
} else if (av_strstart(line, "#", NULL)) {
-
continue;
-
} else if (line[0]) {
-
if (is_segment) {
-
struct segment *seg = av_malloc(sizeof(struct segment));
-
if (!seg) {
-
ret = AVERROR(ENOMEM);
-
goto fail;
-
}
-
seg->duration = duration;
-
ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
-
dynarray_add(&s->segments, &s->n_segments, seg);
-
is_segment = 0;
-
} else if (is_variant) {
-
struct variant *var = av_malloc(sizeof(struct variant));
-
if (!var) {
-
ret = AVERROR(ENOMEM);
-
goto fail;
-
}
-
var->bandwidth = bandwidth;
-
ff_make_absolute_url(var->url, sizeof(var->url), url, line);
-
dynarray_add(&s->variants, &s->n_variants, var);
-
is_variant = 0;
-
}
-
}
-
}
-
s->last_load_time = av_gettime_relative();
-
-
fail:
-
avio_close(in);
-
return ret;
-
}
- https://mp.weixin.qq.com/s/xCMXp46VXZ2jRGuRGAKu8Q