HLS(HTTPLiveStreaming)协议之m3u8⽂件⽣成⽅式
HLS(HTTP Live Streaming)是Apple的动态码率。主要⽤于PC和Apple终端的⾳视频服务。包括⼀个m3u(8)的索引⽂件,TS媒体分⽚⽂件和key加密串⽂件。
HLS的关键其实是⽣成m3u8索引⽂件和TS媒体分⽚,下⾯我将通过以下⼏个步骤讲述m3u8及TS媒体分⽚的⽣成:
第⼀步---获取TS⽂件:
TS(Transport Stream)既传输流,标准制定于mpeg2⽂档协议中,当时TS格式主要是为了数字电视传输⽽制定,制定的年限相当早,在⽹上能到很完备的mpeg2⽂档介绍。⼤家可以参考mpege-2⽂档标准中TS流介绍学习该格式。
现在的我们下载的⾼清电影以mkv格式居多,早期的的电影可能⼀rmvb和avi居多,更早的甚⾄还有mpg格式,现在流⾏的视频⽹站下载的视频基本都是flv格式。这些格式都是⾮TS格式,不过不要紧,现在视频转码的软件也⾮常多,我们可以通过以下两种⽅式进⾏转码。
1,通过格式⼯⼚软件,这是⼀个⽐较成熟的软件,⽹上百度下载即可,不过只有软件,不利于后期源码的直接开发;
    下载地址:
2,通过ffmpeg进⾏格式转换,该⼯程为开源项⽬,我们在实际开发的过程中可以直接集成该源码,(具体的集成⽅式该篇⽂章不讲解,后期将对怎么封装调⽤ffmpeg做出相应介绍)。⽬前我们只是想获取TS⽂件⽤于⽣产m3u8索引⽂件和TS分⽚⽽已,直接下载ffmpeg的可执⾏程序,通过转换即可:
下载地址:
通过命令⾏模式进⼊到所在的⽬录,在命令⾏中输⼊: -i XXX.flv xxx.ts即可,如下图:
图1
第⼆步--⽣成m3u8索引⽂件和TS媒体分⽚
1, m3u8 源码下,
下载地址:
该地址的源码主要是在linux系统编译,不过也能修改成在windows下编译。
windows的源码下载:
官⽹:    源码地址不过也要依赖ffmpeg库,稍微修改下即可。
其实以上两个路径的源码其实是⼀样滴,下⾯那个是德国⼈修改写的,看后缀de就知道了,可能需要FQ才能打开。
下⾯是截取segmenter.c中的代码分⽚⽚段:
do {
double segment_time = 0.0;
AVPacket packet;
fprintf格式double packetStartTime = 0.0;
double packetDuration = 0.0;
if (!decode_done)
{
decode_done = av_read_frame(ic, &packet);
if (!decode_done)
{
if (packet.stream_index != video_index &&
packet.stream_index != audio_index)
{
av_free_packet(&packet);
continue;
}
timeStamp =
(double)(packet.pts) *
(double)(ic->streams[packet.stream_index]->time_base.num) /
(double)(ic->streams[packet.stream_index]->time_base.den);
if (av_dup_packet(&packet) < 0)
{
fprintf(stderr, "Could not duplicate packet\n");
av_free_packet(&packet);
break;
}
insertPacket(streamLace, &packet, timeStamp);
}
}
if (countPackets(streamLace) < 50 && !decode_done)
{
/* allow the queue to fill up so that the packets can be sorted properly */
continue;
}
if (!removePacket(streamLace, &packet))
{
if (decode_done)
{
/* the queue is empty, we are done */
break;
}
assert(decode_done);
continue;
}
packetStartTime =
(double)(packet.pts) *
(double)(ic->streams[packet.stream_index]->time_base.num) /
(double)(ic->streams[packet.stream_index]->time_base.den);
packetDuration =
(double)(packet.duration) *
(double)(ic->streams[packet.stream_index]->time_base.num) /
(double)(ic->streams[packet.stream_index]->time_base.den);
#if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG))
if (av_log_get_level() >= AV_LOG_VERBOSE)
fprintf(stderr,
"stream %i, packet [%f, %f)\n",
packet.stream_index,
packetStartTime,
packetStartTime + packetDuration);
#endif
segment_duration = packetStartTime + packetDuration - prev_segment_time;
// NOTE: segments are supposed to start on a keyframe.
// If the keyframe interval and segment duration do not match
// forcing the segment creation for "better seeking behavior"
// will result in decoding artifacts after seeking or stream switching.
if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY || strict_segment_duration)) {            segment_time = packetStartTime;
}
else if (video_index < 0) {
segment_time = packetStartTime;
}
else {
segment_time = prev_segment_time;
}
if (segment_time - prev_segment_time + segment_duration_error_tolerance >
target_segment_duration + extra_duration_needed)
{
avio_flush(oc->pb);
avio_close(oc->pb);
// Keep track of accumulated rounding error to account for it in later chunks.
segment_duration = segment_time - prev_segment_time;
rounded_segment_duration = (int)(segment_duration + 0.5);
extra_duration_needed += (double)rounded_segment_duration - segment_duration;
updatePlaylist(playlist,
playlist_filename,
output_filename,
output_index,
rounded_segment_duration);
_snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, ++output_index);
if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
fprintf(stderr, "Could not open '%s'\n", output_filename);
break;
}
// close when we find the 'kill' file
if (kill_file) {
FILE* fp = fopen("kill", "rb");
if (fp) {
fprintf(stderr, "user abort: found kill file\n");
fclose(fp);
remove("kill");
decode_done = 1;
removeAllPackets(streamLace);
}
}
prev_segment_time = segment_time;
}
ret = av_interleaved_write_frame(oc, &packet);
if (ret < 0) {
fprintf(stderr, "Warning: Could not write frame of stream\n");
}
else if (ret > 0) {
fprintf(stderr, "End of stream requested\n");
av_free_packet(&packet);
break;
}
av_free_packet(&packet);
} while (!decode_done || countPackets(streamLace) > 0);
2, 把下载下来的源码直接在vs中编译⽣成exe即可,如我⽣成的exe为:
图2
3, 通过命令⾏进⼊该⽬录,并在命令⾏中输⼊: -d 10 -x m3u8list.m3u8 即可⽣成.m3u8⽂件和ts分⽚⽂件,如图2⽬录⽂件的m3u8list.m3u8 和-1.ts、-2.ts和-3.ts⽂件。
图3
4,如以图2的⽬录列表,直接⽤VLC播放器就可以播放m3u8list.m3u8⽂件,⽤写字板查看m3u8⽂件内容为:
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXTINF:10,
-1.ts
#EXTINF:10,
-2.ts
#EXTINF:9,
-3.ts
#EXT-X-ENDLIST
好了,⼤功告成!我们可以直接播放m3u8list.m3u8 和-1.ts、-2.ts、-3.ts⽂件,也可以直接⽤http协议传输这些⽂件,就成了hls协议了

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。