跳幀策略
跳幀策略是指在播放累積延遲逐漸增大時采取的 " 丟棄非實時數(shù)據(jù) " 的策略约郁。
跳幀目的
為了保證實時性。
跳幀的原因分析
非實時數(shù)據(jù)來不及處理
由于流媒體服務(wù)器本身的處理能力的問題但两,累計的非實時數(shù)據(jù)超過一定閾值(比如50幀)鬓梅,此時流媒體服務(wù)器可能會采取直接丟棄非實時幀序列的策略,然后直接處理實時幀谨湘,此時在客戶端看來就會出現(xiàn)跳幀的現(xiàn)象绽快。
除了直播,如果錄制的文件存在跳幀的現(xiàn)象(一般只會在大批量錄制的時候出現(xiàn))紧阔,非撤话眨可能是服務(wù)器的硬盤讀寫功能跟不上,來不及寫數(shù)據(jù)就被丟棄了擅耽,此時應(yīng)更換性能更好的硬盤活孩。
CPU占用過高
CPU占用過高,處理能力有限乖仇,導(dǎo)致收到的數(shù)據(jù)被丟棄憾儒,繼而處理最新的數(shù)據(jù),從而出現(xiàn)跳幀現(xiàn)象这敬。
播放器緩沖過小
播放器的緩存時間過小航夺,比如為0或著0.1,也會導(dǎo)致跳幀出現(xiàn)崔涂。
增加緩存時間可有效避免跳幀阳掐,但會影響實時性。
某次跳幀現(xiàn)象的分析和解決
現(xiàn)象描述
Windows 7下MFC開發(fā)的終端軟件(下文中簡稱終端)冷蚂,在內(nèi)嵌瀏覽器(Microsoft Web Browser控件)中打開網(wǎng)頁缭保。
網(wǎng)頁中通過Flash Player播放rtmp流,存在嚴(yán)重的跳幀的現(xiàn)象蝙茶。
基本上是艺骂,正常23秒,然后跳23秒隆夯,很是詭異钳恕。
處理步驟:
- Step 1: 用IE直接網(wǎng)頁直播
關(guān)閉終端,直接在IE中打開網(wǎng)頁查看直播蹄衷,發(fā)現(xiàn)不存在跳幀現(xiàn)象忧额,所以基本斷定應(yīng)該是終端本身處理的問題。 - Step 2: 排除網(wǎng)絡(luò)原因愧口。
分別通過以下ping的方式睦番,
ping 192.168.2.222 -t -l 4096
簡單分析了,視頻源到流媒體服務(wù)器,還有流媒體服務(wù)器到終端的網(wǎng)絡(luò)情況托嚣,發(fā)現(xiàn)時間基本上都在1~2ms左右巩检,說明網(wǎng)絡(luò)情況良好。
- Step 3: 分析流本身的問題示启。
獲取到rtmp流的實際地址兢哭,通過工具將rtmp流直接錄制能flv文件,將文件播放后發(fā)現(xiàn)丑搔,視頻連續(xù)厦瓢,也不存在跳幀的現(xiàn)象。說明流數(shù)據(jù)本身是好的啤月。 - Step 4: 分析終端的邏輯煮仇。
查看終端的任務(wù)瀏覽器時,發(fā)現(xiàn)了一個令人懷疑的現(xiàn)象谎仲,CPU的使用率本身并不高浙垫,一般在20%~50%左右,但CPU的頻率過高郑诺,一直在100%以上夹姥。
CPU的頻率過高
這時候,懷疑可能是終端程序中辙诞,對CPU的處理導(dǎo)致的辙售。
忽然想起前一陣,在程序中加入了CPU的處理流程飞涂,每隔5秒中旦部,獲取當(dāng)前程序占用CPU和系統(tǒng)占用的CPU,返回給服務(wù)器较店。
代碼如下:
#include <stdio.h>
#include <Windows.h>
typedef long long int64_t;
typedef unsigned long long uint64_t;
/// 時間轉(zhuǎn)換
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的核數(shù)
static int get_processor_number()
{
SYSTEM_INFO info;
GetSystemInfo(&info);
return (int)info.dwNumberOfProcessors;
}
/// 獲取指定進程占用的CPU
int get_cpu_usage(int pid)
{
/// cpu數(shù)量
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;
}
/// 獲取系統(tǒng)占用的CPU
int get_sys_cpu_usage()
{
/// cpu數(shù)量
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;
}
分析發(fā)現(xiàn)士八,禁用CPU操作相關(guān)的函數(shù)后,跳幀的現(xiàn)象消失梁呈。
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.reibang.com/p/ecf51ee32589