ffmpeg 解决hls拖放的问题

apple 的hls方案,采用ffmpeg转码的ts流,播放时会渐渐变得音画不同步,sohu源的处理办法是每隔5分钟加一次discontinue标签,但是这个标签会导致原生播放器重启,表现得有点卡。针对这种情况,改造播放器,不让重启,直接读取下一个流是比较好的办法。但是ffmpeg处理hls的播放存在其它一些问题:1、每遇到discontinue时,显示的播放时长会清零;2、只能在第一个discontinue前进行拖放。本文针对这个问题,对ffmpeg进行改造,使对hls源更优雅的适配。

重点修改hls.c文件,可以用ffplay做前后对比,以便验证

1、添加函数

static int find_timestamp_in_seq_no( struct playlist *pls,int64_t *timestamp, int seq_no)

{

int i;

*timestamp=0;

for (i = 0; i < seq_no; i++) {

*timestamp += pls->segments[i]->duration ;

}

return 0;

}

2、修改函数:

struct segment {

int64_t duration;

int64_t url_offset;

int64_t size;

char *url;

char *key;

enum KeyType key_type;

uint8_t iv[16];

int64_t first_dts; //本文新增

};

static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)

{

HLSContext *c = s->priv_data;

int ret, i, minplaylist = -1;

int64_t timestamp=AV_NOPTS_VALUE;

static int64_t lastdts[2]={AV_NOPTS_VALUE};

static int64_t lastdts_fix[2]={AV_NOPTS_VALUE};

static int64_t interdts[2]={AV_NOPTS_VALUE};

static int64_t whole_dts[2]={AV_NOPTS_VALUE};//累加的dts

recheck_discard_flags(s, c->first_packet);

for (i = 0; i < c->n_playlists; i++) {

struct playlist *pls = c->playlists[i];

/* Make sure we‘ve got one buffered packet from each open playlist

* stream */

AVRational tb;

tb = get_timebase(pls);

find_timestamp_in_seq_no(pls,&timestamp,pls->cur_seq_no);

if (pls->needed && !pls->pkt.data) {

while (1) {

int64_t ts_diff;

ret = av_read_frame(pls->ctx, &pls->pkt);

if (ret < 0) {

if (!url_feof(&pls->pb) && ret != AVERROR_EOF)

return ret;

reset_packet(&pls->pkt);

break;

} else {

//保存每个segments的第一个时间戳,作为seek到新的discontinue使用,不要求非常精准

if(pls->segments[pls->cur_seq_no]->first_dts==AV_NOPTS_VALUE)

{

pls->segments[pls->cur_seq_no]->first_dts=pls->pkt.dts;

// av_log(s, AV_LOG_ERROR, "seq_no=%d,dts=%lld\n",pls->cur_seq_no,pls->pkt.dts);

}

/* stream_index check prevents matching picture attachments etc. */

if (pls->is_id3_timestamped && pls->pkt.stream_index == 0) {

/* audio elementary streams are id3 timestamped */

fill_timing_for_id3_timestamped_stream(pls);

}

if (c->first_timestamp == AV_NOPTS_VALUE &&

pls->pkt.dts != AV_NOPTS_VALUE)

c->first_timestamp = av_rescale_q(pls->pkt.dts,

get_timebase(pls), AV_TIME_BASE_Q);

}

if (pls->seek_timestamp == AV_NOPTS_VALUE)

{

//当前时间戳正常时,赋给lastdts

if(pls->pkt.pts > lastdts[pls->pkt.stream_index])

{

//保存最后一个时间戳,以便作为下次累加的右值,最后一帧的pts与dts一样,因此可以共用

lastdts[pls->pkt.stream_index]=pls->pkt.dts;

}

else//非正常时,即遇到discontinue时,累加至whole_dts

{

whole_dts[pls->pkt.stream_index]+=lastdts[pls->pkt.stream_index];

lastdts[pls->pkt.stream_index]=pls->pkt.dts;

av_log(s, AV_LOG_ERROR, "dts=%lld,index=%d\n",whole_dts[pls->pkt.stream_index],pls->pkt.stream_index);

}

//whole_dts有值的时候,与read packet里面的dts相加,得到递增的dts

if(whole_dts[pls->pkt.stream_index]!=AV_NOPTS_VALUE)

{

// av_log(s, AV_LOG_ERROR, "lastdts=%lld,%lld\n",lastdts,pls->pkt.dts);

pls->pkt.dts+=interdts[pls->pkt.stream_index] + whole_dts[pls->pkt.stream_index];

pls->pkt.pts+=interdts[pls->pkt.stream_index] + whole_dts[pls->pkt.stream_index];

}

//计算音视频的间隔值,以dts为准

if(interdts[pls->pkt.stream_index]==AV_NOPTS_VALUE &&

lastdts[pls->pkt.stream_index]!=AV_NOPTS_VALUE)

{

interdts[pls->pkt.stream_index]=pls->pkt.dts-lastdts[pls->pkt.stream_index];

av_log(s, AV_LOG_ERROR, "inter dts=%lld,index=%d\n",interdts,pls->pkt.stream_index);

}

break;

}

else //seek 时,要保存前一个discontinue的lastdts

{

// whole_dts[0]+=lastdts[pls->pkt.stream_index];

// whole_dts[1]+=lastdts[pls->pkt.stream_index];

}

if (pls->seek_stream_index < 0 ||

pls->seek_stream_index == pls->pkt.stream_index) {

if (pls->pkt.dts == AV_NOPTS_VALUE) {

pls->seek_timestamp = AV_NOPTS_VALUE;

break;

}

ts_diff = timestamp + av_rescale_rnd(pls->pkt.dts - pls->segments[pls->cur_seq_no]->first_dts, AV_TIME_BASE,

tb.den, AV_ROUND_DOWN) - pls->seek_timestamp;

if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY ||

pls->pkt.flags & AV_PKT_FLAG_KEY)) {

whole_dts[0]=(pls->seek_timestamp+ts_diff) * tb.den/(tb.num*AV_TIME_BASE) - pls->pkt.dts ;

whole_dts[1]=(pls->seek_timestamp+ts_diff) * tb.den/(tb.num*AV_TIME_BASE) - pls->pkt.dts ;

av_log(s, AV_LOG_ERROR, "whold dts=%lld,dts=%lld\n",whole_dts[0],pls->pkt.dts);

pls->seek_timestamp = AV_NOPTS_VALUE;

break;

}

}

av_free_packet(&pls->pkt);

reset_packet(&pls->pkt);

}

}

/* Check if this stream has the packet with the lowest dts */

if (pls->pkt.data) {

struct playlist *minpls = minplaylist < 0 ?

NULL : c->playlists[minplaylist];

if (minplaylist < 0) {

minplaylist = i;

} else {

int64_t dts = pls->pkt.dts;

int64_t mindts = minpls->pkt.dts;

if (dts == AV_NOPTS_VALUE ||

(mindts != AV_NOPTS_VALUE && compare_ts_with_wrapdetect(dts, pls, mindts, minpls) < 0))

minplaylist = i;

}

}

}

/* If we got a packet, return it */

if (minplaylist >= 0) {

struct playlist *pls = c->playlists[minplaylist];

*pkt = pls->pkt;

pkt->stream_index += pls->stream_offset;

reset_packet(&c->playlists[minplaylist]->pkt);

if (pkt->dts != AV_NOPTS_VALUE)

c->cur_timestamp = av_rescale_q(pkt->dts,

pls->ctx->streams[pls->pkt.stream_index]->time_base,

AV_TIME_BASE_Q);

return 0;

}

return AVERROR_EOF;

}

时间: 2024-10-05 05:41:53

ffmpeg 解决hls拖放的问题的相关文章

使用ffmpeg搭建HLS直播系统

[时间:2018-04] [状态:Open] [关键词:流媒体,stream,HLS, ffmpeg,live,直播,点播, nginx, ssegment] 0 引言 本文作为HLS综述的后续文章. 主要目的是使用ffmpeg搭建一个简单的HLS点播及直播系统.使用nginx作为HTTP服务器. HLS不管点播还是直播,都是基于HTTP的文件分发系统,所以本文的基本思路是: 使用nginx搭建HTTP服务器 使用ffmpeg实现ts文件的分片,并生成m3u8 ffmpeg使用本地文件模拟HLS

使用Nginx+FFMPEG搭建HLS直播转码服务器

目的:使Nginx支持Rtmp协议推流,并支持hls分发功能及FFMPEG转码多码率功能. 一.准备工作 模块:nginx-rtmp-module-master(支持rtmp协议) 下载地址: http://nginx.org https://github.com/arut/nginx-rtmp-module 1.安装依赖包: #yum -y install gcc glibc glibc-devel make nasm pkgconfig lib-devel openssl-devel exp

使用Nginx+FFMPEG搭建HLS直播服务器

 目的:使Nginx支持Rtmp协议推流,并支持hls分发功能 一.准备工作 模块:nginx-rtmp-module-master(支持rtmp协议) 下载地址: http://nginx.org https://github.com/arut/nginx-rtmp-module 1.安装依赖包: #yum -y install gcc glibc glibc-devel make nasm pkgconfig lib-devel openssl-devel expat-devel get

FFmpeg中HLS文件解析源码

不少人都在找FFmpeg中是否有hls(m3u8)解析的源码,其实是有的.就是ffmpeg/libavformat/hlsproto.c,它依赖的文件也在那个目录中. /* * Apple HTTP Live Streaming Protocol Handler * Copyright (c) 2010 Martin Storsjo * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute

使用FFmpeg生成HLS视频

准备 1.  准备高质量音视频源文件. 2. 编译安装必要的软件 a. ffmpeg  b. nginx 3. 测试环境配置 修改nginx中的nginx.conf配置文件,当前以阿里云预装的CentOS为例: 修改命令:                vim /etc/nginx/conf.d/default.conf 直接编辑该文件,加入如下配置: #音频路径                location /audios{                    alias /share/w

Nginx+ffmpeg的HLS开源server搭建配置及开发具体解释

本文概述: 至眼下为止.HLS 是移动平台上很重要并十分流行的流媒体传输协议.做移动平台的流媒体开发,不知道它不掌握它 .真是一大遗憾.而HLS的平台搭建有一定的难度,本文针对对该方向有一定了解的朋友,将方案实施中的一些细节和流程进行分享交流.本文介绍了.使用开源servernginx 搭建 HLS 服务比較具体的方案.測试使用 VLC以及JWPlayer播放. 切片器: HLS 是依照切片发送流媒体子块的,切片当然不可少. 编译和使用的命令例如以下,能够參考. 编译m3u8-segment 改

FFMPEG和HLS

Nginx本身是一个非常出色的HTTP服务器,FFMPEG是非常好的音视频解决方案.这两个东西通过一个nginx的模块nginx-rtmp-module,组合在一起即可以搭建一个功能相对比较完善的流媒体服务器. nginx配合ffmpeg做流媒体服务器的原理是: nginx通过rtmp模块提供rtmp服务, ffmpeg推送一个rtmp流到nginx, 然后客户端通过访问nginx来收看实时视频流. HLS也是差不多的原理,只是最终客户端是通过HTTP协议来访问的,但是ffmpeg推送流仍然是r

基于ffmpeg生成hls(代码)

代码在 最简单的基于FFMPEG的转码程序(雷霄骅) 基础上简单修改 参考 基于ffmpeg的转码代码(转码为hls) https://pan.baidu.com/s/1w-fF5Ojz8M1ajKsc4DKdrg 1 /* 2 *最简单的基于FFmpeg的转码器 3 *Simplest FFmpeg Transcoder 4 * 5 *雷霄骅 Lei Xiaohua 6 *[email protected] 7 *中国传媒大学/数字电视技术 8 *Communication Universit

M3U8 的简单实现 nginx+ffmpeg

一.概念 M3U8是一种格式  用于分段请求数据来实现流媒体的技术 二.nginx的安装 先下载:http://nginx.org/download/nginx-1.5.10.zip 修改config中mime 加上: application/x-mpegURL m3u8; application/vnd.apple.mpegurl m3u8; video/MP2T ts; 修改域名端口配置,双击 nginx.exe运行  具体安装参考: windows下一分钟配置ngnix实现HLS m3u8