ffmpeg 之 dash

1 简介

   在dash 协议出现以前, 各家公司都开发自己的私有流媒体协议,如微软的SS, 苹果的HLS,Adobe 公司的HDS, 3GPP组织的AHS。这给客户端开发者带来了很大的困扰,后由MPEG 组织牵头,参考前几家公司的流媒体协议,共同制定DASH 协议,也称MPEG-DASH,协议标准号为:ISO/IEC23009, 标准共计八部分,其中媒体呈现(MPD)是最重要的一部分,占据正文的70% 以上。关于DASH 协议的演进历史如下图所示。

  dash 协议一经推出,就被很多公司接入,有着一统江湖的趋势,除了由MPEG 组织背书外,还与DASH 技术优势由莫大的关系,如下图是dash 协议与其他流媒体协议的比较。

2  mpd 文件分析

1 period 字段

  一条完整的mpeg dash stream 可能由一个或者多个period 构成,同一period 内意味着可用媒体内容及其各个可用的码率不会发生变化。直播情况下,需要定期更新MPD 文件。

2 Adaptationset 字段

  一个Period 由一个或者多个Adaptationset组成, Adaptationset 由一组可供切换的不同码率的码流组成,这些码流中可能包含一个或者多个media content components。

3 media content component 字段

  一个media content component 表示一个不同音视频内容,比如不同语言的音轨属于不同的media content component ,而同一音轨的不同码率属于相同的media content component .

4 Representation 字段

  每个Adaptationset 包含一个或者多个Representation , 一个Representation 包含一个或者多个media stream,每个media stream 对应一个media content component, 为了适应不同带宽,dash 网络可能从一个Representation 切换到另一个Representation。

5 Segment 字段

  与HLS 协议的segment 一样,每个Representation 由一个或者多个segment 组成, 每个segment 由一个对应的URL 指定,也可能由相同url + 不同的byte range 指定。

MPD 的数据格式组成如下图所示: period - > adaptation -> Representation  -> segment 

  以上就是关于mpd 格式的介绍, 下面我们根据一个原始mpd 文件来分析下mpd 格式内容 

<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="urn:mpeg:dash:schema:mpd:2011"
	xmlns:xlink="http://www.w3.org/1999/xlink"
	xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
	profiles="urn:mpeg:dash:profile:isoff-live:2011"
	type="static"
	mediaPresentationDuration="PT38M57.3S"
	minBufferTime="PT27.7S">
	<ProgramInformation>
	</ProgramInformation>
	<Period id="0" start="PT0.0S">   <-- period 字段 ,最外面一层壳 -->
		<AdaptationSet id="0" contentType="video" segmentAlignment="true" bitstreamSwitching="true" lang="eng">  <-- Adaptationset 字段,video/audio 属不同的Adaptationset-->
			<Representation id="0" mimeType="video/mp4" codecs="mp4v.20" bandwidth="197991" width="320" height="240" frameRate="18/1">  <-- Representation 里面存放不同码率的stream -->
				<SegmentTemplate timescale="18432" initialization="init-stream$RepresentationID$.mp4" media="chunk-stream$RepresentationID$-$Number%05d$.mp4" startNumber="202">  <--segment , init segment (存放媒体初始化信息)+ stream segment(实际的音视频数据)-->
					<SegmentTimeline>
						<S t="42299392" d="256000" r="2" />
						<S d="14336" />
					</SegmentTimeline>
				</SegmentTemplate>
			</Representation>
		</AdaptationSet>
		<AdaptationSet id="1" contentType="audio" segmentAlignment="true" bitstreamSwitching="true" lang="eng">
			<Representation id="1" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="125579" audioSamplingRate="44100">
				<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
				<SegmentTemplate timescale="44100" initialization="init-stream$RepresentationID$.mp4" media="chunk-stream$RepresentationID$-$Number%05d$.mp4" startNumber="100">
					<SegmentTimeline>
						<S t="49667072" d="612352" r="2" />
						<S d="31744" />
					</SegmentTimeline>
				</SegmentTemplate>
			</Representation>
		</AdaptationSet>
	</Period>
</MPD>

3 ffmpeg 对dash 切片

ffmpeg.exe -re -i test.mp4 -codec copy -f dash -window_size 4 -extra_window_size 5 index.mpd

  用ffmeg 对音视频进行切片时,它会生成三类文件,mpd + init-stream0/1.mp4 + chunk-streamx-xxxx.mp4 。 其中mpd 用来记录切片的整体信息,init-stream0/1.mp4 相当于MP4文件的moov box , 它记录了当前stream 的metadata信息,还有sps,pps 等等信息。

  dash 切片成的格式为fmp4, 可以简单理解成分片化的MP4,是dash 采用的媒体文件格式。普通的MP4 文件由moov + mdat 组成, 而fmp4用 moov + N * segment 组成, segment = moof + mdat。 其中moov 描述文件层次的metadata信息,moof 描述segment 层次metadata 信息。

 

4 ffmpeg parse mpd 文件

mpd 文件是xml 格式的,具由层次结构,主题里面包含子主题,从上到下,period -> adaptationset -> representation -> segmenttimeline 。由下图可是period 字段除了具有duration 信息,还包含子主题adaptationset 信息,以此类推...., 在parse mpd 文件时,也是从上往下parse.

假设当前有一stream, 具有不同码率video/audio,那么mpd 文件又是如何描述此信息的呢?

stream 用总字段duration , video/audio 用adaptationset 描述,不同码率的video/audio 用representation 描述,在往下是segmenttimeline 代表实际的data.。 

 

5 dash data read

static int dash_read_packet(AVFormatContext *s, AVPacket *pkt)
{

    ....
    while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
        ret = av_read_frame(cur->ctx, pkt);//如果当前segment 还有data,直接调用av_read_frame
        if (ret >= 0) {
            /* If we got a packet, return it */
            cur->cur_timestamp = av_rescale(pkt->pts, (int64_t)cur->ctx->streams[0]->time_base.num * 90000, cur->ctx->streams[0]->time_base.den);
            pkt->stream_index = cur->stream_index;
            return 0;
        }
        if (cur->is_restart_needed) {//需要open 下一个segment ,调用reopen_demux_for_componet(), 重新建立url 连接
            cur->cur_seg_offset = 0;
            cur->init_sec_buf_read_offset = 0;
            if (cur->input)
                ff_format_io_close(cur->parent, &cur->input);
            ret = reopen_demux_for_component(s, cur);
            cur->is_restart_needed = 0;
        }
    }
    return AVERROR_EOF;
}

6 dash seek

dash seek 相关code 如下,总结来说就是单一fragment, 和普通片源没差异。 如果是多个fragment 组成的,则需要寻找最靠近的那个fragment ,然后重新建立连接,open 这个fragment。此处有个疑问,假如fragment duration 很长,会不会seek 不精准?

static int dash_seek(AVFormatContext *s, struct representation *pls, int64_t seek_pos_msec, int flags, int dry_run)
{

    // 如果时单一fragment , 直接call av_seek_frame()
    if (pls->n_fragments == 1) {
        pls->cur_timestamp = 0;
        pls->cur_seg_offset = 0;
        if (dry_run)
            return 0;
        ff_read_frame_flush(pls->ctx);
        return av_seek_frame(pls->ctx, -1, seek_pos_msec * 1000, flags);
    }

    // 如果不是单一fragment ,则寻找靠近的frament
    ...... //此处删除如何寻找最靠近frament 的代码逻辑
    ret = dry_run ? 0 : reopen_demux_for_component(s, pls); //找到最靠近的fragment后,重新建立url 连接

    return ret;
}

7 总结

从宏观角度看,dash 协议,会将一个stream 切成很多不同码率的segment ,我们parse 出相应的segment ,然后建立网络连接,去读取相应的segment  就好了,当然牵涉如何具体实现,不会这么简单。回看HLS 协议,其实他们两做法很相似,切片,然后去请求这些切片数据。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值