流媒体

直播问题分析总结 -- 跳帧

2017-05-15  本文已影响98人  FlyingPenguin

跳帧策略

跳帧策略是指在播放累积延迟逐渐增大时采取的 " 丢弃非实时数据 " 的策略。

跳帧目的

为了保证实时性。

跳帧的原因分析

跳帧的原因汇总
非实时数据来不及处理

由于流媒体服务器本身的处理能力的问题,累计的非实时数据超过一定阈值(比如50帧),此时流媒体服务器可能会采取直接丢弃非实时帧序列的策略,然后直接处理实时帧,此时在客户端看来就会出现跳帧的现象。
除了直播,如果录制的文件存在跳帧的现象(一般只会在大批量录制的时候出现),非常可能是服务器的硬盘读写功能跟不上,来不及写数据就被丢弃了,此时应更换性能更好的硬盘。

CPU占用过高

CPU占用过高,处理能力有限,导致收到的数据被丢弃,继而处理最新的数据,从而出现跳帧现象。

播放器缓冲过小

播放器的缓存时间过小,比如为0或着0.1,也会导致跳帧出现。
增加缓存时间可有效避免跳帧,但会影响实时性。

某次跳帧现象的分析和解决

现象描述

Windows 7下MFC开发的终端软件(下文中简称终端),在内嵌浏览器(Microsoft Web Browser控件)中打开网页。
网页中通过Flash Player播放rtmp流,存在严重的跳帧的现象。
基本上是,正常23秒,然后跳23秒,很是诡异。
处理步骤:

ping 192.168.2.222 -t -l 4096

简单分析了,视频源到流媒体服务器,还有流媒体服务器到终端的网络情况,发现时间基本上都在1~2ms左右,说明网络情况良好。

#include <stdio.h>  
#include <Windows.h>

typedef long long           int64_t;
typedef unsigned long long  uint64_t;

/// 时间转换
static uint64_t file_time_2_utc(const FILETIME* ftime)
{
    LARGE_INTEGER li;

    li.LowPart  = ftime->dwLowDateTime;
    li.HighPart = ftime->dwHighDateTime;

    return li.QuadPart;
}

/// 获得CPU的核数
static int get_processor_number()
{
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    return (int)info.dwNumberOfProcessors;
}

/// 获取指定进程占用的CPU
int get_cpu_usage(int pid)
{  
    /// cpu数量
    static int processor_count_ = -1;

    // 上一次的时间
    static uint64_t last_time_ = 0;
    static uint64_t last_system_time_ = 0;

    FILETIME now;
    FILETIME creation_time;
    FILETIME exit_time;
    FILETIME kernel_time;
    FILETIME user_time;
    uint64_t system_time;
    uint64_t time;
    uint64_t system_time_delta;
    uint64_t time_delta;

    int cpu = -1;

    if(processor_count_ == -1)
    {
        processor_count_ = get_processor_number();
    }

    GetSystemTimeAsFileTime(&now);

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
    if (!GetProcessTimes(hProcess, &creation_time, &exit_time, &kernel_time, &user_time))
    {
        return 0;
    }

    system_time = (file_time_2_utc(&kernel_time) + file_time_2_utc(&user_time)) 
                  / processor_count_;  

    time = file_time_2_utc(&now);

    if ((last_system_time_ == 0) || (last_time_ == 0))
    {
        last_system_time_ = system_time;
        last_time_ = time;

        return 0;
    }

    system_time_delta = system_time - last_system_time_;
    time_delta = time - last_time_;

    if (time_delta == 0)
    {
        return 0;
    }

    cpu = (int)((system_time_delta * 100 + time_delta / 2) / time_delta);
    last_system_time_ = system_time;
    last_time_ = time;

    return cpu;
}

/// 获取系统占用的CPU
int get_sys_cpu_usage()  
{  
    /// cpu数量  
    static int processor_count_ = -1;  

    /// 上一次的时间  
    static int64_t last_time_        = 0;  
    static int64_t last_system_time_ = 0;  

    FILETIME now;  
    FILETIME creation_time;  
    FILETIME exit_time;  
    FILETIME kernel_time;  
    FILETIME user_time;  
    int64_t system_time;  
    int64_t time;  
    int64_t system_time_delta;  
    int64_t time_delta;  

    int cpu = -1;  

    if(processor_count_ == -1)  
    {  
        processor_count_ = get_processor_number();  
    }  

    GetSystemTimeAsFileTime(&now);  

    if (!GetProcessTimes(GetCurrentProcess(), &creation_time, &exit_time,  
        &kernel_time, &user_time))  
    {  
        // We don't assert here because in some cases (such as in the Task  Manager)  
        // we may call this function on a process that has just exited but we have  
        // not yet received the notification.  
        return -1;  
    }  

    system_time = (file_time_2_utc(&kernel_time) + file_time_2_utc(&user_time))   
                  / processor_count_;  
    time = file_time_2_utc(&now);  

    if ((last_system_time_ == 0) || (last_time_ == 0))  
    {  
        // First call, just set the last values.  
        last_system_time_ = system_time;  
        last_time_ = time;  

        return -1;  
    }  

    system_time_delta = system_time - last_system_time_;  
    time_delta = time - last_time_;  

    if (time_delta == 0)  
    {
        return -1;  
    }

    // We add time_delta / 2 so the result is rounded.  
    cpu = (int)((system_time_delta * 100 + time_delta / 2) / time_delta);  

    last_system_time_ = system_time;  
    last_time_ = time; 

    return cpu;  
}  

分析发现,禁用CPU操作相关的函数后,跳帧的现象消失。

References:

https://alwaysmavs.gitbooks.io/plplayerkit/content/4%20%E7%9B%B4%E6%92%AD%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86/4.1%20%E8%B7%B3%E5%B8%A7%E7%AD%96%E7%95%A5.html
http://www.jianshu.com/p/ecf51ee32589

上一篇 下一篇

猜你喜欢

热点阅读