找Bug流程
1、首先看出現(xiàn)概率是偶現(xiàn)還是必現(xiàn)
2、如果是必現(xiàn),則復(fù)現(xiàn)整個bug過程,看Bug是否出現(xiàn)
如果是偶現(xiàn),則分析問題視頻
問題一 【歐立】【遠(yuǎn)程抓拍】安卓-遠(yuǎn)程抓拍的視頻,下載到手機(jī)本地相冊,聲音慢放
一、額外知識學(xué)習(xí)
在復(fù)現(xiàn)問題過程中,剛開始沒下載到本地,直接點(diǎn)擊播放發(fā)生慢放,因?yàn)榱髁烤W(wǎng)速較低,15s,40Mb 平均網(wǎng)速要超過2.7Mb/s,因此誤以為復(fù)現(xiàn)了bug
二、解決過程
0 問題復(fù)現(xiàn)
找設(shè)備抓拍,下載,發(fā)現(xiàn)視頻沒有問題,Bug無法復(fù)現(xiàn)
0 簡單分析
分析源文件
可能在本地轉(zhuǎn)碼過程(轉(zhuǎn)碼過程可能發(fā)生在本地也可能發(fā)生在服務(wù)器端)中 數(shù)據(jù)丟失,也可能是ADTS頭部,通道數(shù)自動轉(zhuǎn)換
1 mp4文件提取出h264/h265
播放沒問題
ffmpeg -i test.mp4 -map 0:v:1 -c:v copy video.h264
ffmpeg -i test.mp4 -map 0:v:0 -c:v copy video.h265
-i test.mp4
: 指定輸入文件為 test.mp4。-i 是指定輸入文件的選項(xiàng),后面跟著輸入文件的路徑。-map 0:v:0
: 使用 -map 選項(xiàng)選擇輸入文件中的第一個視頻流。0 表示第一個輸入文件,v 表示視頻流,0 表示第一個視頻流。-c:v copy
: 使用 -c:v 選項(xiàng)指定視頻流的編碼方式為 “copy”,表示直接復(fù)制視頻流而不進(jìn)行重新編碼。這意味著輸出文件將與輸入文件的視頻流完全相同,不會更改編碼格式或參數(shù)。
2 mp4文件中提取aac
ffmpeg -i test1.mp4 -vn -acodec copy test1.aac
3 aac轉(zhuǎn)碼為pcm
ffmpeg -i BadAAC.aac -acodec pcm_s16le -ar 32000 -f s16le Bad.pcm
4 分析pcm
數(shù)據(jù)被置0,正常應(yīng)該是較為連續(xù)的
因?yàn)槭潜噩F(xiàn),估計是pcm數(shù)據(jù)拷貝,長度計算不對,只拷貝了一半數(shù)據(jù),剩下的一半是內(nèi)存中初始化的0值
三、解決方案
無法復(fù)現(xiàn)Bug,Bug已不存在
問題二 【歐立】【本地錄像】本地視頻偶現(xiàn)輕微花屏
一、額外知識學(xué)習(xí)
花屏出現(xiàn)可能原因
(1)丟幀會報錯
模擬過程,在aac和h264復(fù)用為mp4的過程中,故意丟沒100幀丟一幀,用ffplay播放出現(xiàn)報錯
復(fù)用代碼
#include <stdio.h>
#include <iostream>
extern "C"
{
#include "libavformat/avformat.h"
};
int main(int argc, char* argv[])
{
AVFormatContext* ifmtCtxVideo = NULL, * ifmtCtxAudio = NULL, * ofmtCtx = NULL;
AVCodecContext* video_ctx = NULL;
AVPacket packet;
AVCodec* video_codec = NULL;
//AVBSFContext* bsf_ctx = nullptr;
const AVBitStreamFilter* pfilter = av_bsf_get_by_name("h264_mp4toannexb");
AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
//av_bsf_alloc(pfilter, &bsf_ctx);
int inVideoIndex = -1, inAudioIndex = -1;
int outVideoIndex = -1, outAudioIndex = -1;
int audioindex = 0;
int videoindex = 0;
int idx = 0;
int64_t curPstVideo = 0, curPstAudio = 0;
int ret = 0;
unsigned int i = 0;
const char* inFilenameVideo = "Titanic.h264";
const char* inFilenameAudio = "Titanic.aac";
const char* outFilename = "MissFrame.mp4";
//打開輸入視頻文件
ret = avformat_open_input(&ifmtCtxVideo, inFilenameVideo, 0, 0);
if (ret < 0)
{
printf("can't open input video file\n");
goto end;
}
//查找輸入流
ret = avformat_find_stream_info(ifmtCtxVideo, 0);
if (ret < 0)
{
printf("failed to retrieve input video stream information\n");
goto end;
}
//打開輸入音頻文件
ret = avformat_open_input(&ifmtCtxAudio, inFilenameAudio, 0, 0);
if (ret < 0)
{
printf("can't open input audio file\n");
goto end;
}
//查找輸入流
ret = avformat_find_stream_info(ifmtCtxAudio, 0);
if (ret < 0)
{
printf("failed to retrieve input audio stream information\n");
goto end;
}
printf("===========Input Information==========\n");
av_dump_format(ifmtCtxVideo, 0, inFilenameVideo, 0);
av_dump_format(ifmtCtxAudio, 0, inFilenameAudio, 0);
printf("======================================\n");
//新建輸出上下文
avformat_alloc_output_context2(&ofmtCtx, NULL, NULL, outFilename);
if (!ofmtCtx)
{
printf("can't create output context\n");
goto end;
}
//視頻輸入流
for (i = 0; i < ifmtCtxVideo->nb_streams; ++i)
{
if (ifmtCtxVideo->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
AVStream* inStream = ifmtCtxVideo->streams[i];
AVStream* outStream = avformat_new_stream(ofmtCtx, NULL);
//av_dump_format(ofmtCtx, 0, outFilename, 1);
inVideoIndex = i;
if (!outStream)
{
printf("failed to allocate output stream\n");
goto end;
}
outVideoIndex = outStream->index;
if (avcodec_parameters_copy(outStream->codecpar, inStream->codecpar) < 0)
{
printf("faild to copy context from input to output stream");
goto end;
}
outStream->codecpar->codec_tag = 0;
//av_dump_format(ofmtCtx, 0, outFilename, 1);
break;
}
}
video_ctx = avcodec_alloc_context3(video_codec);
video_codec = avcodec_find_decoder(ifmtCtxVideo->streams[0]->codecpar->codec_id);
video_ctx = ifmtCtxVideo->streams[0]->codec;
//音頻輸入流
for (i = 0; i < ifmtCtxAudio->nb_streams; ++i)
{
if (ifmtCtxAudio->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
AVStream* inStream = ifmtCtxAudio->streams[i];
AVStream* outStream = avformat_new_stream(ofmtCtx, NULL);
inAudioIndex = i;
if (!outStream)
{
printf("failed to allocate output stream\n");
goto end;
}
if (avcodec_parameters_copy(outStream->codecpar, inStream->codecpar) < 0)
{
printf("faild to copy context from input to output stream");
goto end;
}
outAudioIndex = outStream->index;
break;
}
}
printf("==========Output Information==========\n");
av_dump_format(ofmtCtx, 0, outFilename, 1);
printf("======================================\n");
//打開輸入文件
if (!(ofmtCtx->oformat->flags & AVFMT_NOFILE))
{
if (avio_open(&ofmtCtx->pb, outFilename, AVIO_FLAG_WRITE) < 0)
{
printf("can't open out file\n");
goto end;
}
}
//寫文件頭
if (avformat_write_header(ofmtCtx, NULL) < 0)
{
printf("Error occurred when opening output file\n");
goto end;
}
while (1)
{
AVFormatContext* ifmtCtx = NULL;
AVStream* inStream, * outStream;
int streamIndex = 0;
if (av_compare_ts(curPstVideo, ifmtCtxVideo->streams[inVideoIndex]->time_base, curPstAudio, ifmtCtxAudio->streams[inAudioIndex]->time_base) < 0)
{
ifmtCtx = ifmtCtxVideo;
streamIndex = outVideoIndex;
if (av_read_frame(ifmtCtx, &packet) >= 0)
{
//printf("Video start packet.pts = %d \n\n", packet.pts);
inStream = ifmtCtx->streams[packet.stream_index];
//printf("Video sample_rate = %\n", inStream->codecpar->sample_rate);
outStream = ofmtCtx->streams[streamIndex];
if (packet.stream_index == inVideoIndex)
{
av_bitstream_filter_filter(h264bsfc, ifmtCtxVideo->streams[0]->codec, NULL, &packet.data, &packet.size, packet.data, packet.size, 0);
// Fix: No PTS(Example: Raw H.264
// Simple Write PTS
//printf("Video PTS: %ld\n", packet.pts);
//printf("Video DTS: %ld\n", packet.dts);
if (packet.pts == AV_NOPTS_VALUE)
{
//write PTS
AVRational timeBase1 = inStream->time_base;
//Duration between 2 frames
double calcDuration = (double)1.0 / av_q2d(inStream->r_frame_rate);
//Parameters 轉(zhuǎn)化為
//printf("Video calcDuration = %lf\n", calcDuration);
packet.pts = (double)(videoindex * calcDuration) / (double)(av_q2d(timeBase1));
packet.dts = packet.pts;
packet.duration = (double)calcDuration / (double)(av_q2d(timeBase1));
videoindex++;
//printf("Video PTS: %ld\n", packet.pts);
//printf("Video DTS: %ld\n", packet.dts);
}
curPstVideo = packet.pts;
}
}
else
{
break;
}
}
else
{
ifmtCtx = ifmtCtxAudio;
streamIndex = outAudioIndex;
if (av_read_frame(ifmtCtx, &packet) >= 0)
{
//printf("Audio start packet.pts = %d \n\n", packet.pts);
inStream = ifmtCtx->streams[packet.stream_index];
outStream = ofmtCtx->streams[streamIndex];
//printf("Audio PTS: %ld\n", packet.pts);
//printf("Audio DTS: %ld\n", packet.dts);
if (packet.stream_index == inAudioIndex)
{
//Fix: No PTS(Example: Raw H.264
//Simple Write PTS
AVRational timeBase1 = inStream->time_base;
//printf("timeBase = %.15lf\n", (double)(av_q2d(timeBase1)));
//Duration between 2 frames
double calcDuration = (double)1024.0 / inStream->codecpar->sample_rate;
//printf("Audio calcDuration = %lf\n", calcDuration);
packet.pts = (double)(audioindex * calcDuration) / (double)(av_q2d(timeBase1));
packet.dts = packet.pts;
packet.duration = (double)calcDuration / (double)(av_q2d(timeBase1));
audioindex ++;
//printf("Audio PTS: %ld\n", packet.pts);
//printf("Audio DTS: %ld\n", packet.dts);
curPstAudio = packet.pts;
}
}
else
{
break;
}
}
//FIX:Bitstream Filter
//Convert PTS/DTS
packet.pts = av_rescale_q_rnd(packet.pts, inStream->time_base, outStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.dts = av_rescale_q_rnd(packet.dts, inStream->time_base, outStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
packet.duration = av_rescale_q(packet.duration, inStream->time_base, outStream->time_base);
packet.pos = -1;
packet.stream_index = streamIndex;
//write
// printf("Audio sample_rate = %d \n\n", inStream->codecpar->sample_rate);
if (streamIndex == outVideoIndex)++idx;
if(streamIndex == outVideoIndex && (idx % 100 == 0)) continue;
if (av_interleaved_write_frame(ofmtCtx, &packet) < 0)
{
printf("error muxing packet");
break;
}
av_packet_unref(&packet);
}
printf("idx = %d\n", idx);
av_write_trailer(ofmtCtx);//寫文件尾
end:
avformat_close_input(&ifmtCtxVideo);
avformat_close_input(&ifmtCtxAudio);
if (ofmtCtx && !(ofmtCtx->oformat->flags & AVFMT_NOFILE))
avio_close(ofmtCtx->pb);
avformat_free_context(ofmtCtx);
return 0;
}
結(jié)果
ffplay C:\Users\Administrator\Desktop\VideoDemo\MissFrame.mp4
(2)無丟幀不會報錯
據(jù)沒有報錯但是有花屏的情況,推斷是動態(tài)畫面采樣時,碼率沒有上去造成數(shù)據(jù)采樣不足出現(xiàn)馬賽克。需要設(shè)備在編碼動態(tài)畫面時把碼率動態(tài)(vbr碼率,靜態(tài)是cbr,均衡生abr)搞上去。
二、解決過程
0 簡單查看視頻信息
1 把MP4中的h265提取出來
ffmpeg -i test.mp4 -map 0:v:0 -c:v copy video.h265
2 把h265中的yuv提取出來
ffmpeg -i video.h265 -c:v rawvideo -pix_fmt yuv420p output.yuv
3 分析h265和YUV數(shù)據(jù)
h265
YUV 第69幀出現(xiàn)數(shù)據(jù)錯誤
三、解決方案
錄像不存在丟幀,但在第69幀(視頻中14:01:08:3處)數(shù)據(jù)出錯
問題三 【歐立】【遠(yuǎn)程直播】遠(yuǎn)程直播晃動攝像頭,馬賽克嚴(yán)重
一、額外知識學(xué)習(xí)
0 參考文章
Elecard Stream Eye 學(xué)習(xí)鏈接
直播疑難雜癥排查(6)— 馬賽克嚴(yán)重
直播常見問題:馬賽克嚴(yán)重
抖音直播畫面出現(xiàn)馬賽克怎么回事?
1 碼率
碼率(Bit Rate)是指在數(shù)字音視頻中,每秒傳輸?shù)谋忍財?shù)。它表示以每秒的速度傳輸?shù)臄?shù)據(jù)量,通常以比特每秒(bps)或千比特每秒(kbps)為單位。
在音視頻中,碼率是描述音頻或視頻數(shù)據(jù)傳輸速率和質(zhì)量的關(guān)鍵參數(shù)。較高的碼率通常意味著更高的數(shù)據(jù)傳輸速率和更好的音視頻質(zhì)量,而較低的碼率可能導(dǎo)致較低的傳輸速率和較差的音視頻質(zhì)量。
對于音頻,較高的碼率通常表示更多的數(shù)據(jù)被分配給每秒鐘的音頻采樣,從而提供更高的音頻質(zhì)量和更準(zhǔn)確的聲音還原。較低的碼率可能導(dǎo)致音頻失真、噪聲或低質(zhì)量的聲音。
對于視頻,較高的碼率通常表示更多的數(shù)據(jù)被分配給每秒的視頻幀,從而提供更高的圖像質(zhì)量、更多的細(xì)節(jié)和更平滑的動畫。較低的碼率可能導(dǎo)致視頻圖像模糊、馬賽克、圖像壓縮偽影以及運(yùn)動模糊。
選擇適當(dāng)?shù)拇a率需要平衡帶寬限制、儲存空間限制和期望的音視頻質(zhì)量。較高的碼率可以提供更好的質(zhì)量,但會占用更多的帶寬和存儲空間。較低的碼率可以節(jié)省帶寬和存儲空間,但可能犧牲音視頻質(zhì)量。
在音視頻編碼過程中,可以根據(jù)具體需求選擇合適的碼率。通常會根據(jù)目標(biāo)應(yīng)用或平臺的要求,選擇適當(dāng)?shù)拇a率以滿足預(yù)期的音視頻質(zhì)量和資源限制。
二、解決過程
0 簡單分析
分析源文件
可能在本地轉(zhuǎn)碼過程(轉(zhuǎn)碼過程可能發(fā)生在本地也可能發(fā)生在服務(wù)器端)中 數(shù)據(jù)丟失,也可能是ADTS頭部,通道數(shù)自動轉(zhuǎn)換
1 mp4文件提取出h264
播放沒問題
ffmpeg -i test.mp4 -map 0:v:0 -c:v copy video.h264
2 分析h264
ffplay 不報錯
軟件分析原視頻無問題
3 將h264解碼為YUV
ffmpeg -i video.h264 -c:v rawvideo -pix_fmt yuv420p output.yuv
4 分析YUV
三、解決方案
視頻本身無丟幀,數(shù)據(jù)正常,可能運(yùn)動過程中采集視頻碼率較低或者網(wǎng)速不穩(wěn)定,建議方案商將碼率調(diào)高
本視頻碼率為821kb/s 幀率27.49fps
問題四 【歐立】【wifi回看】安卓-wifi回看,播放歷史視頻很卡頓
一、額外知識學(xué)習(xí)
記路者版本過低會出現(xiàn)視頻打不開現(xiàn)象,下載最新版即可
二、解決過程
0 簡單分析
需要分析 下載視頻,下載視頻如果有問題證明是視頻本身的問題
然后 復(fù)現(xiàn)問題 找問題所在位置
1 mp4文件提取出h264
播放沒問題
ffmpeg -i test.mp4 -map 0:v:0 -c:v copy video.h264
2 分析h264
ffplay 不報錯
軟件分析,原視頻無問題
3 mp4文件提取aac、aac解碼為pcm
pcm無錯誤文章來源:http://www.zghlxwxcb.cn/news/detail-510717.html
三、解決方案
下載同一段歷史視頻分析正常,應(yīng)該是 設(shè)備端 數(shù)據(jù)傳輸速度不夠
視頻卡頓為偶現(xiàn),在600k/s以上網(wǎng)速無發(fā)生卡頓現(xiàn)象
前四張網(wǎng)速高,播放正常,最后一張286k發(fā)生卡頓文章來源地址http://www.zghlxwxcb.cn/news/detail-510717.html
到了這里,關(guān)于音視頻BUG學(xué)習(xí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!