找Bug流程
1、首先看出现概率是偶现还是必现
2、如果是必现,则复现整个bug过程,看Bug是否出现
如果是偶现,则分析问题视频
3、
问题一 【欧立】【远程抓拍】安卓-远程抓拍的视频,下载到手机本地相册,声音慢放
一、额外知识学习
在复现问题过程中,刚开始没下载到本地,直接点击播放发生慢放,因为流量网速较低,15s,40Mb 平均网速要超过2.7Mb/s,因此误以为复现了bug
二、解决过程
0 问题复现
找设备抓拍,下载,发现视频没有问题,Bug无法复现
0 简单分析
分析源文件
可能在本地转码过程(转码过程可能发生在本地也可能发生在服务器端)中 数据丢失,也可能是ADTS头部,通道数自动转换
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 是指定输入文件的选项,后面跟着输入文件的路径。
-map 0:v:0
: 使用 -map 选项选择输入文件中的第一个视频流。0 表示第一个输入文件,v 表示视频流,0 表示第一个视频流。
-c:v copy
: 使用 -c:v 选项指定视频流的编码方式为 “copy”,表示直接复制视频流而不进行重新编码。这意味着输出文件将与输入文件的视频流完全相同,不会更改编码格式或参数。
2 mp4文件中提取aac
ffmpeg -i test1.mp4 -vn -acodec copy test1.aac
3 aac转码为pcm
ffmpeg -i BadAAC.aac -acodec pcm_s16le -ar 32000 -f s16le Bad.pcm
4 分析pcm
数据被置0,正常应该是较为连续的
因为是必现,估计是pcm数据拷贝,长度计算不对,只拷贝了一半数据,剩下的一半是内存中初始化的0值
三、解决方案
无法复现Bug,Bug已不存在
问题二 【欧立】【本地录像】本地视频偶现轻微花屏
一、额外知识学习
花屏出现可能原因
(1)丢帧会报错
模拟过程,在aac和h264复用为mp4的过程中,故意丢没100帧丢一帧,用ffplay播放出现报错
复用代码
#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 转化为
//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;
}
结果
ffplay C:\Users\Administrator\Desktop\VideoDemo\MissFrame.mp4
(2)无丢帧不会报错
据没有报错但是有花屏的情况,推断是动态画面采样时,码率没有上去造成数据采样不足出现马赛克。需要设备在编码动态画面时把码率动态(vbr码率,静态是cbr,均衡生abr)搞上去。