技术干货:实时视频直播首屏耗时400ms内的优化实践

本文由“逆流的鱼yuiop”原创分享于“何俊林”公众号,感谢作者的无私分享。

1、引言

直播行业的竞争越来越激烈,进过2018年这波洗牌后,已经度过了蛮荒暴力期,剩下的都是在不断追求体验。最近正好在做直播首开优化工作,实践中通过多种方案并行,已经能把首开降到500ms以下,借此机会分享出来,希望能对大家有所启发。

本文内容的技术前提:

1)基于FFmpeg的ijkplayer,最新版本0.88版本;

2)拉流协议基于http-flv。

http-flv更稳定些,国内大部分直播公司基本都是使用http-flv了,从我们实际数据来看,http-flv确实稍微更快些。但是考虑到会有rtmp源,这块也加了些优化。

(本文同步发布于:http://www.52im.net/thread-2087-1-1.html

2、相关文章

浅谈开发实时视频直播平台的技术要点

实现延迟低于500毫秒的1080P实时音视频直播的实践分享

移动端实时视频直播技术实践:如何做到实时秒开、流畅不卡

技术揭秘:支持百万级粉丝互动的Facebook实时视频直播

移动端实时音视频直播技术详解(一):开篇

移动端实时音视频直播技术详解(二):采集

移动端实时音视频直播技术详解(三):处理

移动端实时音视频直播技术详解(四):编码和封装

移动端实时音视频直播技术详解(五):推流和传输

移动端实时音视频直播技术详解(六):延迟优化

理论联系实际:实现一个简单地基于HTML5的实时视频直播

浅谈实时音视频直播中直接影响用户体验的几项关键技术指标

首次披露:快手是如何做到百万观众同场看直播仍能秒开且不卡顿的?

Android直播入门实践:动手搭建一套简单的直播系统

网易云信实时视频直播在TCP数据传输层的一些优化思路

P2P技术如何将实时视频直播带宽降低75%?

近期大热的实时直播答题系统的实现思路与技术难点分享

七牛云技术分享:使用QUIC协议实现实时视频直播0卡顿!

实时视频直播客户端技术盘点:Native、HTML5、WebRTC、微信小程序

实时音频的混音在视频直播应用中的技术原理和实践总结

新浪微博技术分享:微博实时直播答题的百万高并发架构实践

3、IP直通车

简单理解就是,把域名替换成IP。比如https://www.baidu.com/,你可以直接换成14.215.177.39,这样做的目的是,省去了DNS解析的耗时,尤其在网络不好时,访问域名,域名要去解析,再给你返回。不仅仅有时间解析过长的问题,还有小运营商DNS劫持的问题。一般就是在启动应用时,就开始对拉流的域名进行预解析好,存到本地,然后在真正拉流时,直接用就行。典型的案列,就是很多人使用HTTPDNS,这个github上也有开源,可以自行去研究下。

需要注意的是:这种方案在使用 HTTPS 时,是会失败的。因为 HTTPS 在证书验证的过程,会出现 domain 不匹配导致 SSL/TLS 握手不成功。

4、服务端 GOP 缓存优化

除了客户端业务侧的优化外,我们还可以从流媒体服务器侧进行优化。

我们都知道直播流中的图像帧分为:I 帧、P 帧、B 帧,其中只有 I 帧是能不依赖其他帧独立完成解码的,这就意味着当播放器接收到 I 帧它能马上渲染出来,而接收到 P 帧、B 帧则需要等待依赖的帧而不能立即完成解码和渲染,这个期间就是「黑屏」了。

所以,在服务器端可以通过缓存 GOP(在 H.264 中,GOP 是封闭的,是以 I 帧开头的一组图像帧序列),保证播放端在接入直播时能先获取到 I 帧马上渲染出画面来,从而优化首屏加载的体验。

这里有一个 IDR 帧的概念需要讲一下,所有的 IDR 帧都是 I 帧,但是并不是所有 I 帧都是 IDR 帧,IDR 帧是 I 帧的子集。

I 帧严格定义是帧内编码帧,由于是一个全帧压缩编码帧,通常用 I 帧表示「关键帧」。IDR 是基于 I 帧的一个扩展,带了控制逻辑,IDR 图像都是 I 帧图像,当解码器解码到 IDR 图像时,会立即将参考帧队列清空,将已解码的数据全部输出或抛弃。重新查找参数集,开始一个新的序列。这样如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR 图像之后的图像永远不会使用 IDR 之前的图像的数据来解码。

在 H.264 编码中,GOP 是封闭式的,一个 GOP 的第一帧都是 IDR 帧。

5、推流端设置和优化

一般播放器需要拿到一个完整的GOP,才能记性播放。GOP是在推流端可以设置,比如下面这个图,是我dump一个流,看到的GOP情况。GOP大小是50,推流过来的fps设置是25,也就是1s内会显示25个Frame,50个Frame,刚好直播设置GOP 2S,但是直播一般fps不用设置这么高,可以随便dump任何一家直播公司的推流,设置fps在15-18之间就够了。

6、客户端播放器的相关耗时和优化

当set一个源给播放器后,播放器需要open这个流,然后和服务端建立长连接,然后demux,codec,最后渲染。

我们可以按照播放器的四大块,依次优化:

1)数据请求耗时;

2)解复用耗时;

3)解码耗时;

4)渲染出图耗时

6.1 数据请求

这里就是网络和协议相关。无论是http-flv,还是rtmp,都主要是基于tcp的,所以一定会有tcp三次握手,同时打开tcp.c分析。需要加日志在一些方法中,如下tcp_open方法。

下面是已经改动过的代码:

/* return non zero if error */

staticinttcp_open(URLContext *h, constchar*uri, intflags)

{

av_log(NULL, AV_LOG_INFO, "tcp_open begin");

...省略部分代码

if(!dns_entry) {

#ifdef HAVE_PTHREADS

av_log(h, AV_LOG_INFO, "ijk_tcp_getaddrinfo_nonblock begin.\n");

ret = ijk_tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai, s->addrinfo_timeout, &h->interrupt_callback, s->addrinfo_one_by_one);

av_log(h, AV_LOG_INFO, "ijk_tcp_getaddrinfo_nonblock end.\n");

#else

if(s->addrinfo_timeout > 0)

av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without pthreads support.\n");

av_log(h, AV_LOG_INFO, "getaddrinfo begin.\n");

if(!hostname[0])

ret = getaddrinfo(NULL, portstr, &hints, &ai);

else

ret = getaddrinfo(hostname, portstr, &hints, &ai);

av_log(h, AV_LOG_INFO, "getaddrinfo end.\n");

#endif

if(ret) {

av_log(h, AV_LOG_ERROR,

"Failed to resolve hostname %s: %s\n",

hostname, gai_strerror(ret));

returnAVERROR(EIO);

}

cur_ai = ai;

} else{

av_log(NULL, AV_LOG_INFO, "Hit DNS cache hostname = %s\n", hostname);

cur_ai = dns_entry->res;

}

restart:

#if HAVE_STRUCT_SOCKADDR_IN6

// workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number.

if(cur_ai->ai_family == AF_INET6){

structsockaddr_in6 * sockaddr_v6 = (structsockaddr_in6 *)cur_ai->ai_addr;

if(!sockaddr_v6->sin6_port){

sockaddr_v6->sin6_port = htons(port);

}

}

#endif

fd = ff_socket(cur_ai->ai_family,

cur_ai->ai_socktype,

cur_ai->ai_protocol);

if(fd < 0) {

ret = ff_neterrno();

gotofail;

}

/* Set the socket‘s send or receive buffer sizes, if specified.

If unspecified or setting fails, system default is used. */

if(s->recv_buffer_size > 0) {

setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof(s->recv_buffer_size));

}

if(s->send_buffer_size > 0) {

setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof(s->send_buffer_size));

}

if(s->listen == 2) {

// multi-client

if((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0)

gotofail1;

} elseif(s->listen == 1) {

// single client

if((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,

s->listen_timeout, h)) < 0)

gotofail1;

// Socket descriptor already closed here. Safe to overwrite to client one.

fd = ret;

} else{

ret = av_application_on_tcp_will_open(s->app_ctx);

if(ret) {

av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_WILL_TCP_OPEN");

gotofail1;

}

if((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,

s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {

if(av_application_on_tcp_did_open(s->app_ctx, ret, fd, &control))

gotofail1;

if(ret == AVERROR_EXIT)

gotofail1;

else

gotofail;

} else{

ret = av_application_on_tcp_did_open(s->app_ctx, 0, fd, &control);

if(ret) {

av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_DID_TCP_OPEN");

gotofail1;

} elseif(!dns_entry && strcmp(control.ip, hostname_bak)) {

add_dns_cache_entry(hostname_bak, cur_ai, s->dns_cache_timeout);

av_log(NULL, AV_LOG_INFO, "Add dns cache hostname = %s, ip = %s\n", hostname_bak , control.ip);

}

}

}

h->is_streamed = 1;

s->fd = fd;

if(dns_entry) {

release_dns_cache_reference(hostname_bak, &dns_entry);

} else{

freeaddrinfo(ai);

}

av_log(NULL, AV_LOG_INFO, "tcp_open end");

return0;

// 省略部分代码

}

改动地方主要是hints.ai_family = AF_INET;,原来是    hints.ai_family = AF_UNSPEC;,原来设计是一个兼容IPv4和IPv6的配置,如果修改成AF_INET,那么就不会有AAAA的查询包了。如果只有IPv4的请求,就可以改成AF_INET。当然有IPv6,这里就不要动了。这么看是否有,可以通过抓包工具看。

接着分析,我们发现tcp_read函数是个阻塞式的,会非常耗时,我们又不能设置短一点中断时间,因为短了的话,造成读取不到数据,就中断,后续播放就直接失败了,这里只能让它等。

不过还是优化的点是下面部分:

static int tcp_read(URLContext *h, uint8_t *buf, intsize)

{

av_log(NULL, AV_LOG_INFO, "tcp_read begin %d\n", size);

TCPContext *s = h->priv_data;

intret;

if(!(h->flags & AVIO_FLAG_NONBLOCK)) {

ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);

if(ret)

return ret;

}

ret = recv(s->fd, buf, size, 0);

if(ret == 0)

returnAVERROR_EOF;

//if (ret > 0)

//    av_application_did_io_tcp_read(s->app_ctx, (void*)h, ret);

av_log(NULL, AV_LOG_INFO, "tcp_read end %d\n", ret);

returnret < 0 ? ff_neterrno() : ret;

}

我们可以把上面两行注释掉,因为在ff_network_wait_fd_timeout等回来后,数据可以放到buf中,下面av_application_did_io_tcp_read就没必要去执行了。原来每次ret>0,都会执行av_application_did_io_tcp_read这个函数。

6.2 解复用耗时

在日志中发现,数据请求到后,进行音视频分离时,首先需要匹配对应demuxer,其中ffmpeg的av_find_input_format和avformat_find_stream_info非常耗时,前者简单理解就是打开某中请求到数据,后者就是探测流的一些信息,做一些样本检测,读取一定长度的码流数据,来分析码流的基本信息,为视频中各个媒体流的 AVStream 结构体填充好相应的数据。这个函数中做了查找合适的解码器、打开解码器、读取一定的音视频帧数据、尝试解码音视频帧等工作,基本上完成了解码的整个流程。这时一个同步调用,在不清楚视频数据的格式又要做到较好的兼容性时,这个过程是比较耗时的,从而会影响到播放器首屏秒开。

这两个函数调用都在ff_ffplay.c的read_thread函数中:

if(ffp->iformat_name) {

av_log(ffp, AV_LOG_INFO, "av_find_input_format noraml begin");

is->iformat = av_find_input_format(ffp->iformat_name);

av_log(ffp, AV_LOG_INFO, "av_find_input_format normal end");

}

elseif(av_stristart(is->filename, "rtmp", NULL)) {

av_log(ffp, AV_LOG_INFO, "av_find_input_format rtmp begin");

is->iformat = av_find_input_format("flv");

av_log(ffp, AV_LOG_INFO, "av_find_input_format rtmp end");

ic->probesize = 4096;

ic->max_analyze_duration = 2000000;

ic->flags |= AVFMT_FLAG_NOBUFFER;

}

av_log(ffp, AV_LOG_INFO, "avformat_open_input begin");

err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);

av_log(ffp, AV_LOG_INFO, "avformat_open_input end");

if(err < 0) {

print_error(is->filename, err);

ret = -1;

gotofail;

}

ffp_notify_msg1(ffp, FFP_MSG_OPEN_INPUT);

if(scan_all_pmts_set)

av_dict_set(&ffp->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);

if((t = av_dict_get(ffp->format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {

av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);

#ifdef FFP_MERGE

ret = AVERROR_OPTION_NOT_FOUND;

gotofail;

#endif

}

is->ic = ic;

if(ffp->genpts)

ic->flags |= AVFMT_FLAG_GENPTS;

av_format_inject_global_side_data(ic);

if(ffp->find_stream_info) {

AVDictionary **opts = setup_find_stream_info_opts(ic, ffp->codec_opts);

intorig_nb_streams = ic->nb_streams;

do{

if(av_stristart(is->filename, "data:", NULL) && orig_nb_streams > 0) {

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

if(!ic->streams[i] || !ic->streams[i]->codecpar || ic->streams[i]->codecpar->profile == FF_PROFILE_UNKNOWN) {

break;

}

}

if(i == orig_nb_streams) {

break;

}

}

ic->probesize=100*1024;

ic->max_analyze_duration=5*AV_TIME_BASE;

ic->fps_probe_size=0;

av_log(ffp, AV_LOG_INFO, "avformat_find_stream_info begin");

err = avformat_find_stream_info(ic, opts);

av_log(ffp, AV_LOG_INFO, "avformat_find_stream_info end");

} while(0);

ffp_notify_msg1(ffp, FFP_MSG_FIND_STREAM_INFO);

最终改的如上,主要是对rtmp增加了,指定format为‘flv’,以及样本大小。

同时在外部可以通过设置 probesize 和 analyzeduration 两个参数来控制该函数读取的数据量大小和分析时长为比较小的值来降低 avformat_find_stream_info的耗时,从而优化播放器首屏秒开。但是,需要注意的是这两个参数设置过小时,可能会造成预读数据不足,无法解析出码流信息,从而导致播放失败、无音频或无视频的情况。所以,在服务端对视频格式进行标准化转码,从而确定视频格式,进而再去推算 avformat_find_stream_info分析码流信息所兼容的最小的probesize和 analyzeduration,就能在保证播放成功率的情况下最大限度地区优化首屏秒开。

在 FFmpeg 中的 utils.c 文件中的函数实现中有一行代码是 int fps_analyze_framecount = 20;,这行代码的大概用处是,如果外部没有额外设置这个值,那么 avformat_find_stream_info 需要获取至少 20 帧视频数据,这对于首屏来说耗时就比较长了,一般都要 1s 左右。而且直播还有实时性的需求,所以没必要至少取 20 帧。

将这个值初始化为2,看看效果:

/* check if one codec still needs to be handled */

for(i = 0; i < ic->nb_streams; i++) {

intfps_analyze_framecount = 2;

st = ic->streams[i];

if(!has_codec_parameters(st, NULL))

break;

if(ic->metadata) {

AVDictionaryEntry *t = av_dict_get(ic->metadata, "skip-calc-frame-rate", NULL, AV_DICT_MATCH_CASE);

if(t) {

intfps_flag = (int) strtol(t->value, NULL, 10);

if(!st->r_frame_rate.num && st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0 && fps_flag > 0) {

intavg_fps = st->avg_frame_rate.num / st->avg_frame_rate.den;

if(avg_fps > 0 && avg_fps <= 120) {

st->r_frame_rate.num = st->avg_frame_rate.num;

st->r_frame_rate.den = st->avg_frame_rate.den;

}

}

}

}

这样,avformat_find_stream_info 的耗时就可以缩减到 100ms 以内。

6.3 解码耗时和渲染出图耗时

最后就是解码耗时和渲染出图耗时,这块优化空间很少,大头都在前面。

有人开始抛出问题了,你这个起播快是快,但是后面网络不好,卡顿怎么办?直播中会引起卡顿,主要是网络有抖动的时候,没有足够的数据来播放,ijkplayer会激发其缓冲机制。

主要是有几个宏控制:

DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS:网络差时首次去唤醒read_thread函数去读取数据。

DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS:第二次去唤醒read_thread函数去读取数据。

DEFAULT_LAST_HIGH_WATER_MARK_IN_MS这个宏的意思是最后的机会去唤醒read_thread函数去读取数据。

可以设置DEFAULT_LAST_HIGH_WATER_MARK_IN_MS为1 * 1000,也即缓冲1秒后开始通知缓冲完成去读取数据,默认是5秒,如果过大,会让用户等太久,那么每次读取的bytes也可以少些。可以设置DEFAULT_HIGH_WATER_MARK_IN_BYTES小一些,设置为30 * 1024,默认是256 * 1024。

把BUFFERING_CHECK_PER_MILLISECONDS设置为50,默认是500:

#define DEFAULT_HIGH_WATER_MARK_IN_BYTES        (30 * 1024)

#define DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS     (100)

#define DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS      (1 * 1000)

#define DEFAULT_LAST_HIGH_WATER_MARK_IN_MS      (1 * 1000)

#define BUFFERING_CHECK_PER_BYTES               (512)

#define BUFFERING_CHECK_PER_MILLISECONDS        (50)

可以看下这些宏使用的地方:

inline static void ffp_reset_demux_cache_control(FFDemuxCacheControl *dcc)

{

dcc->min_frames                = DEFAULT_MIN_FRAMES;

dcc->max_buffer_size           = MAX_QUEUE_SIZE;

dcc->high_water_mark_in_bytes  = DEFAULT_HIGH_WATER_MARK_IN_BYTES;

dcc->first_high_water_mark_in_ms    = DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS;

dcc->next_high_water_mark_in_ms     = DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS;

dcc->last_high_water_mark_in_ms     = DEFAULT_LAST_HIGH_WATER_MARK_IN_MS;

dcc->current_high_water_mark_in_ms  = DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS;

}

最后优化的点,是设置一些参数值,也能优化一部分,实际上很多直播用软件用低分辨率240,甚至360,来达到秒开,可以可以作为一个减少耗时点来展开的,因为分辨率越低,数据量越少,首开越快。

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer");

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "max_delay", 0);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "max-buffer-size", 4* 1024);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 50);

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probsize", "1024");

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", "100");

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);

//静音

//mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "an", 1);

//重连模式,如果中途服务器断开了连接,让它重新连接

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "reconnect", 1);

6.4 测试数据

以上完了后,就可以看下测试数据,分辨率在540p以下基本秒开,在4G网络下测试:

1)河北卫视直播源,测试10组,平均下来300ms。一组数据386ms,如下:

11-17 14:17:46.659  9896 10147 D IJKMEDIA: IjkMediaPlayer_native_setup

11-17 14:17:46.663  9896 10147 V IJKMEDIA: setDataSource: path [url=http://weblive.hebtv.com/live/hbws_bq/index.m3u8]http://weblive.hebtv.com/live/hbws_bq/index.m3u8[/url]

11-17 14:17:46.666  9896 10177 I FFMPEG  : [FFPlayer @ 0xe070d400] avformat_open_input begin

11-17 14:17:46.841  9896 10177 I FFMPEG  : [FFPlayer @ 0xe070d400] avformat_open_input end

11-17 14:17:46.841  9896 10177 I FFMPEG  : [FFPlayer @ 0xe070d400] avformat_find_stream_info begin

11-17 14:17:46.894  9896 10177 I FFMPEG  : [FFPlayer @ 0xe070d400] avformat_find_stream_info end

11-17 14:17:47.045  9896 10191 D IJKMEDIA: Video: first frame decoded

11-17 14:17:47.046  9896 10175 D IJKMEDIA: FFP_MSG_VIDEO_DECODED_START:

2)映客直播秀场源,测试10组,平均下来400ms。一组数据418ms,如下:

11-17 14:21:32.908 11464 11788 D IJKMEDIA: IjkMediaPlayer_native_setup

11-17 14:21:32.952 11464 11788 V IJKMEDIA: setDataSource: path [flash]http://14.215.100.45/hw.pull.inke.cn/live/1542433669916866_0_ud.flv[/flash]?ikDnsOp=1001&ikHost=hw&ikOp=0&codecInfo=8192&ikLog=1&ikSyncBeta=1&dpSrc=6&push_host=trans.push.cls.inke.cn&ikMinBuf=2900&ikMaxBuf=3600&ikSlowRate=0.9&ikFastRate=1.1

11-17 14:21:32.996 11464 11818 I FFMPEG  : [FFPlayer @ 0xc2575c00] avformat_open_input begin

11-17 14:21:33.161 11464 11818 I FFMPEG  : [FFPlayer @ 0xc2575c00] avformat_open_input end

11-17 14:21:33.326 11464 11829 D IJKMEDIA: Video: first frame decoded

3)熊猫直播游戏直播源,测试10组,平均下来350ms。一组数据373ms,如下:

11-17 14:29:17.615 15801 16053 D IJKMEDIA: IjkMediaPlayer_native_setup

11-17 14:29:17.645 15801 16053 V IJKMEDIA: setDataSource: path [flash]http://flv-live-qn.xingxiu.panda.tv/panda-xingxiu/dc7eb0c2e78c96646591aae3a20b0686.flv[/flash]

11-17 14:29:17.649 15801 16079 I FFMPEG  : [FFPlayer @ 0xeb5ef000] avformat_open_input begin

11-17 14:29:17.731 15801 16079 I FFMPEG  : [FFPlayer @ 0xeb5ef000] avformat_open_input end

11-17 14:29:17.988 15801 16090 D IJKMEDIA: Video: first frame decoded

附录:更多音视频技术文章

[1] 实时音视频开发的其它精华资料:

即时通讯音视频开发(一):视频编解码之理论概述

即时通讯音视频开发(二):视频编解码之数字视频介绍

即时通讯音视频开发(三):视频编解码之编码基础

即时通讯音视频开发(四):视频编解码之预测技术介绍

即时通讯音视频开发(五):认识主流视频编码技术H.264

即时通讯音视频开发(六):如何开始音频编解码技术的学习

即时通讯音视频开发(七):音频基础及编码原理入门

即时通讯音视频开发(八):常见的实时语音通讯编码标准

即时通讯音视频开发(九):实时语音通讯的回音及回音消除概述

即时通讯音视频开发(十):实时语音通讯的回音消除技术详解

即时通讯音视频开发(十一):实时语音通讯丢包补偿技术详解

即时通讯音视频开发(十二):多人实时音视频聊天架构探讨

即时通讯音视频开发(十三):实时视频编码H.264的特点与优势

即时通讯音视频开发(十四):实时音视频数据传输协议介绍

即时通讯音视频开发(十五):聊聊P2P与实时音视频的应用情况

即时通讯音视频开发(十六):移动端实时音视频开发的几个建议

即时通讯音视频开发(十七):视频编码H.264、VP8的前世今生

实时语音聊天中的音频处理与编码压缩技术简述

网易视频云技术分享:音频处理与压缩技术快速入门

学习RFC3550:RTP/RTCP实时传输协议基础知识

基于RTMP数据传输协议的实时流媒体技术研究(论文全文)

声网架构师谈实时音视频云的实现难点(视频采访)

浅谈开发实时视频直播平台的技术要点

还在靠“喂喂喂”测试实时语音通话质量?本文教你科学的评测方法!

实现延迟低于500毫秒的1080P实时音视频直播的实践分享

移动端实时视频直播技术实践:如何做到实时秒开、流畅不卡

如何用最简单的方法测试你的实时音视频方案

技术揭秘:支持百万级粉丝互动的Facebook实时视频直播

简述实时音视频聊天中端到端加密(E2EE)的工作原理

理论联系实际:实现一个简单地基于HTML5的实时视频直播

IM实时音视频聊天时的回声消除技术详解

浅谈实时音视频直播中直接影响用户体验的几项关键技术指标

如何优化传输机制来实现实时音视频的超低延迟?

首次披露:快手是如何做到百万观众同场看直播仍能秒开且不卡顿的?

Android直播入门实践:动手搭建一套简单的直播系统

网易云信实时视频直播在TCP数据传输层的一些优化思路

实时音视频聊天技术分享:面向不可靠网络的抗丢包编解码器

P2P技术如何将实时视频直播带宽降低75%?

专访微信视频技术负责人:微信实时视频聊天技术的演进

腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天

微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

近期大热的实时直播答题系统的实现思路与技术难点分享

福利贴:最全实时音视频开发要用到的开源工程汇总

七牛云技术分享:使用QUIC协议实现实时视频直播0卡顿!

实时音视频聊天中超低延迟架构的思考与技术实践

理解实时音视频聊天中的延时问题一篇就够

实时视频直播客户端技术盘点:Native、HTML5、WebRTC、微信小程序

写给小白的实时音视频技术入门提纲

微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等

腾讯技术分享:微信小程序音视频技术背后的故事

微信多媒体团队梁俊斌访谈:聊一聊我所了解的音视频技术

新浪微博技术分享:微博短视频服务的优化实践之路

实时音频的混音在视频直播应用中的技术原理和实践总结

以网游服务端的网络接入层设计为例,理解实时通信的技术挑战

腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

新浪微博技术分享:微博实时直播答题的百万高并发架构实践

技术干货:实时视频直播首屏耗时400ms内的优化实践

>> 更多同类文章 ……

[2] 开源实时音视频技术WebRTC的文章:

开源实时音视频技术WebRTC的现状

简述开源实时音视频技术WebRTC的优缺点

访谈WebRTC标准之父:WebRTC的过去、现在和未来

良心分享:WebRTC 零基础开发者教程(中文)[附件下载]

WebRTC实时音视频技术的整体架构介绍

新手入门:到底什么是WebRTC服务器,以及它是如何联接通话的?

WebRTC实时音视频技术基础:基本架构和协议栈

浅谈开发实时视频直播平台的技术要点

[观点] WebRTC应该选择H.264视频编码的四大理由

基于开源WebRTC开发实时音视频靠谱吗?第3方SDK有哪些?

开源实时音视频技术WebRTC中RTP/RTCP数据传输协议的应用

简述实时音视频聊天中端到端加密(E2EE)的工作原理

实时通信RTC技术栈之:视频编解码

开源实时音视频技术WebRTC在Windows下的简明编译教程

网页端实时音视频技术WebRTC:看起来很美,但离生产应用还有多少坑要填?

了不起的WebRTC:生态日趋完善,或将实时音视频技术白菜化

腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

>> 更多同类文章 ……

(本文同步发布于:http://www.52im.net/thread-2087-1-1.html

原文地址:https://www.cnblogs.com/imstudy/p/10000374.html

时间: 2024-10-12 04:09:36

技术干货:实时视频直播首屏耗时400ms内的优化实践的相关文章

react首屏页面加载性能优化记录

一.webpack devtool devtool的值有好几个,根据自己的情况和环境的不同,选择合适的,一般打包的时候可以用:eval .eval-source-map或者cheap-eval-source-map.我这边打包时,直接设置成了:‘eval ’.具体可以去官网查看每个值的作用~ 二.如果使用的是antd框架,通过打包分析,你会发现有个icons文件500多k,有点大~  解决办法是: 1.随意找个地方建个.js文件,名称也随意~,比如是icons.js (当然要规范哈),用来按需引

腾讯优测优分享 | 探索react native首屏渲染最佳实践

腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react native实现的界面进行持续优化.目标只有一个,在享受react native带来的新特性的同时,在体验上无限逼近原生实现.作为一名前端开发,本文会从前端角度,探索react native首屏渲染最佳实

探索react native首屏渲染最佳实践

1.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react native实现的界面进行持续优化.目标只有一个,在享受react native带来的新特性的同时,在体验上无限逼近原生实现.作为一名前端开发,本文会从前端角度,探索react native首屏渲染最佳实践. 2.首屏耗时计算方法 2.1我们关注的耗时 优化首屏渲染耗时,需要先定义首屏耗时的衡量方法.将react nat

【Bugly技术干货】那些年我们用过的显示性能指标

Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言:那些年我们用过的显示性能指标 注:Google 在自己文章中用了 Display Performance 来描述我们常说的流畅度,为了显得有文化,本文主要用“显示性能”一词来代指“流畅度”(虽然两者在概念上有细微差别). 从 Android 诞生的那一刻起,流畅度就为众人所关注.一时之间,似乎所有人都在讨论 Android 和

时间都去哪了-移动Web首屏优化实践

时间都去哪了-移动Web首屏优化实践 首屏时间 可用性的前提,后面用户是否使用你app很重要的因素之一: 我们PC上访问其实现在的带宽已经很好了,百兆光宽带,但是在移动端就不一样了,很多用户还是使用的流量,GPRS.所以,对于首屏时间的加载,压力还是蛮大的: 我们手机在出发一个操作之后,发生了什么? 下面是在返回数据前发生的动作,但是作为前端,我们好像动不了这些东西,不可能叫运营商去帮你定制一套系统: 这个才是我们真正要去做的东西-渲染(webkit) 给你两个优化的方面,优化网路与优化渲染(高

【腾讯bugly干货】QQ空间直播秒开优化实践

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址为:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1204&extra=page%3D1 2016年应该是直播元年,直播应用百团大战,QQ 空间也在6.5版本上线了直播功能,从无到有.快速搭建了直播间.“先扛住再优化”,第一个版本和竞品相比,我们进入直播间的速度比较慢.根据外网统计在6.5版本的用户端看到画面需要4.4s,因此在6.5发布之后,着手启动了优化工作,

【腾讯bugly干货分享】QQ空间直播秒开优化实践

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1204&extra=page%3D1 2016年应该是直播元年,直播应用百团大战,QQ 空间也在6.5版本上线了直播功能,从无到有.快速搭建了直播间."先扛住再优化",第一个版本和竞品相比,我们进入直播间的速度比较慢.根据外网统计在6.5版本的用户端看到画面需要4.4s,因此在6.5发布之后,着

技术创新成直播主战场,“直播+云”将成就新独角兽

(上图为腾讯集团高级执行副总裁.社交网络事业群总裁汤道生) 2016年被普遍认为是直播"风口"爆发的元年.根据中国互联网信息中心于2017年1月发布的第39次<中国互联网络发展状况统计报告>,截止到2016年12月国内直播用户达到3.44亿人,占国内网民的47.1%,网络直播业务呈出强大的营收能力,资本持续涌入. 在资本的推动下,直播独角兽开始出现.2016年,斗鱼完成由腾讯领投的B轮.C轮融资,将估值推向百亿元,创业不满3年的斗鱼也在2016年实现了10亿元的直播收入,成

基于图像识别测试手机浏览器打开网页首屏时间的方法

本文涉及性能测试技术,特别是涉及一种基于图像识别的测试手机网页首屏时间的方法,它可以应用在手机浏览器页面加载响应时间的的自动化测试或性能检测中.同时可以应用到其他android的apk的响应时间的测试中去 随着智能手机等移动终端的发展,越来越多的用户开始通过手机等设备浏览网页,以获取用户所需的信息. 用户使用手机访问网站是基于手机浏览器所获取的网页实现的.通常情况下,用户访问网站的页面全部加载完毕时,总页面高度可能有一屏到多屏不等,总的页面加载时间要比首屏加载时间长很多,但是首屏加载时间是用户对