背景
一般的應用場景對實時點播速度要求不高的情況下,可以設置探測碼流的延時和探測數據的大小,代碼如下:
pFormatContext->probesize = 500 *1024;
pFormatContext->max_analyze_duration = 5 * AV_TIME_BASE;//AV_TIME_BASE是定義的時間標準,代表1秒
弊端
這樣設置probesize和max_analyze_duration是可以減少探測時間,但是是以犧牲成功率爲代價的,有時候探測不到流信息,就會播不出來,
出現在網絡丟包的情況下(使用UDP進行視頻數據的傳輸)或者網路特別複雜,跨越多個網段
場景
要求點播時間不超過1秒,允許指定攝像機的視頻參數如下:
當前只考慮視頻流,後續會添加音頻流,已知輸入的流格式video: H264 1920*1080 25fps
實施方案
當前採用FFmpeg 3.4版本,參考博客採用的是比較舊的版本2.2.0版本,有些函數已經被廢棄,有些函數已經發生了改變,但是基本思路是不變的:
通過手動指定解碼器參數,來取代avformat_find_stream_info函數探測流格式
AVStream* CDecoder::CreateStream(AVFormatContext* pFormatContext, int nCodecType)
{
AVStream *st = avformat_new_stream(pFormatContext, NULL);
if (!st)
return NULL;
st->codecpar->codec_type = (AVMediaType)nCodecType;
return st;
}
int CDecoder::GetVideoExtraData(AVFormatContext* pFormatContext, int nVideoIndex)
{
int type, size, flags, pos, stream_type;
int ret = -1;
int64_t dts;
bool got_extradata = false;
if (!pFormatContext || nVideoIndex < 0 || nVideoIndex > 2)
return ret;
for (;; avio_skip(pFormatContext->pb, 4)) {
pos = avio_tell(pFormatContext->pb);
type = avio_r8(pFormatContext->pb);
size = avio_rb24(pFormatContext->pb);
dts = avio_rb24(pFormatContext->pb);
dts |= avio_r8(pFormatContext->pb) << 24;
avio_skip(pFormatContext->pb, 3);
if (0 == size)
break;
if (FLV_TAG_TYPE_AUDIO == type || FLV_TAG_TYPE_META == type) {
/*if audio or meta tags, skip them.*/
avio_seek(pFormatContext->pb, size, SEEK_CUR);
}
else if (type == FLV_TAG_TYPE_VIDEO) {
/*if the first video tag, read the sps/pps info from it. then break.*/
size -= 5;
pFormatContext->streams[nVideoIndex]->codecpar->extradata = (uint8_t*)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
if (NULL == pFormatContext->streams[nVideoIndex]->codecpar->extradata)
break;
memset(pFormatContext->streams[nVideoIndex]->codecpar->extradata, 0, size + FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(pFormatContext->streams[nVideoIndex]->codecpar->extradata, pFormatContext->pb->buf_ptr + 5, size);
pFormatContext->streams[nVideoIndex]->codecpar->extradata_size = size;
ret = 0;
got_extradata = true;
}
else {
/*The type unknown,something wrong.*/
break;
}
if (got_extradata)
break;
}
return ret;
}
int CDecoder::InitDecode(AVFormatContext *pFormatContext)
{
int video_index = -1;
int audio_index = -1;
int ret = -1;
if (!pFormatContext)
return ret;
/*
Get video stream index, if no video stream then create it.
And audio so on.
*/
if (0 == pFormatContext->nb_streams) {
CreateStream(pFormatContext, AVMEDIA_TYPE_VIDEO);
CreateStream(pFormatContext, AVMEDIA_TYPE_AUDIO);
video_index = 0;
audio_index = 1;
}
else if (1 == pFormatContext->nb_streams) {
if (AVMEDIA_TYPE_VIDEO == pFormatContext->streams[0]->codecpar->codec_type) {
CreateStream(pFormatContext, AVMEDIA_TYPE_AUDIO);
video_index = 0;
audio_index = 1;
}
else if (AVMEDIA_TYPE_AUDIO == pFormatContext->streams[0]->codecpar->codec_type) {
CreateStream(pFormatContext, AVMEDIA_TYPE_VIDEO);
video_index = 1;
audio_index = 0;
}
}
else if (2 == pFormatContext->nb_streams) {
if (AVMEDIA_TYPE_VIDEO == pFormatContext->streams[0]->codecpar->codec_type) {
video_index = 0;
audio_index = 1;
}
else if (AVMEDIA_TYPE_VIDEO == pFormatContext->streams[1]->codecpar->codec_type) {
video_index = 1;
audio_index = 0;
}
}
/*Error. I can't find video stream.*/
if (video_index != 0 && video_index != 1)
return ret;
//Init the audio codec(AAC).
pFormatContext->streams[audio_index]->codecpar->codec_id = AV_CODEC_ID_AAC;
pFormatContext->streams[audio_index]->codecpar->sample_rate = 44100;
pFormatContext->streams[audio_index]->codecpar->bits_per_coded_sample = 16;
pFormatContext->streams[audio_index]->codecpar->channels = 2;
pFormatContext->streams[audio_index]->codecpar->channel_layout = 3;
pFormatContext->streams[audio_index]->pts_wrap_bits = 32;
pFormatContext->streams[audio_index]->time_base.den = 1000;
pFormatContext->streams[audio_index]->time_base.num = 1;
//Init the video codec(H264).
pFormatContext->streams[video_index]->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
pFormatContext->streams[video_index]->codecpar->codec_id = AV_CODEC_ID_H264;
pFormatContext->streams[video_index]->codecpar->format = 12;
pFormatContext->streams[video_index]->codecpar->bits_per_raw_sample = 8;
pFormatContext->streams[video_index]->codecpar->profile = 66;
pFormatContext->streams[video_index]->codecpar->level = 42;
pFormatContext->streams[video_index]->codecpar->width = 1920;
pFormatContext->streams[video_index]->codecpar->height = 1080;
pFormatContext->streams[video_index]->codecpar->sample_aspect_ratio.num = 0;
pFormatContext->streams[video_index]->codecpar->sample_aspect_ratio.den = 1;
pFormatContext->streams[video_index]->pts_wrap_bits = 64;
pFormatContext->streams[video_index]->time_base.den = 1200000;
pFormatContext->streams[video_index]->time_base.num = 1;
pFormatContext->streams[video_index]->avg_frame_rate.den = 1;
pFormatContext->streams[video_index]->avg_frame_rate.num = 25;
/*Need to change, different condition has different frame_rate. 'r_frame_rate' is new in ffmepg2.3.3*/
pFormatContext->streams[video_index]->r_frame_rate.den = 25;
pFormatContext->streams[video_index]->r_frame_rate.num = 1;
/* H264 need sps/pps for decoding, so read it from the first video tag.*/
ret = GetVideoExtraData(pFormatContext, video_index);
/*Update the AVFormatContext Info*/
pFormatContext->nb_streams = 1;
/*empty the buffer.*/
pFormatContext->pb->buf_ptr = pFormatContext->pb->buf_end;
return ret;
}
已有的方案
AVDictionary* pOptions = NULL;
pFormatCtx->probesize = 200 *1024;
pFormatCtx->max_analyze_duration = 3 * AV_TIME_BASE;
//Retrieve stream information
if (avformat_find_stream_info(pFormatCtx, &pOptions) < 0)
{
return -1; // Couldn't find stream information
}
現有方案
InitDecode(pFormatCtx);
警告
在上述的解決方案中,採用了av_malloc分配內存,但是沒有進行內存的釋放,是否會隨着pFormatCtx的釋放而釋放,不至於導致內存泄露,當然經過測試,並沒有走到這一步
優化效果
通過測試,速度優化了1200毫秒
參考
http://blog.csdn.net/STN_LCD/article/details/74935760
https://jiya.io/archives/vlc_optimize_1.html