【c++】使用FFmpeg库进行视频流处理的
void MyVideoCapture::run_stream() //ffmpeg拉流获取视频帧
{
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame, *pFrameRGB;
AVPacket *packet;
uint8_t *out_buffer;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
avformat_network_init(); // 初始化网络库
av_register_all();// 初始化所有组件,调用该函数后,才能使用复用器和编解码器
pFormatCtx = avformat_alloc_context();// 分配AVFormatContext,它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体
// ffmpeg取rtsp流时av_read_frame阻塞的解决办法 设置参数优化
AVDictionary* avdic = NULL;
av_dict_set(&avdic, "buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
av_dict_set(&avdic, "rtmp_transport", "tcp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp
av_dict_set(&avdic, "stimeout", "2000000", 0); //设置超时断开连接时间,单位微秒
av_dict_set(&avdic, "max_delay", "300000", 0); //设置最大时延
///rtsp地址,可根据实际情况修改
char *url;
// QString ab = "C:/Users/goby/Desktop/Video_left.mp4";rtsp_address
url= addrest.toLocal8Bit().data();
// qDebug() << url << "url"<<addrest;
if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) { // 打开网络流或文件流
qDebug("can't open the file. \n");
return;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { // 读取流数据包并获取流的相关信息
qDebug("Could't find stream infomation.\n");
return;
}
videoStream = -1;
// 确定流格式是否为视频
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
///如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
qDebug("Didn't find a video stream.\n");
return;
}
///查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); // 根据编码器的ID号查找对应的解码器
pCodecCtx->bit_rate = 0; //码率
pCodecCtx->time_base.num = 1; //下面两行:一秒钟10帧
pCodecCtx->time_base.den = 10;
pCodecCtx->frame_number = 1; //每包一个视频帧
if (pCodec == NULL) {
qDebug("Codec not found.\n");
return;
}
// Initialize the AVCodecContext to use the given AVCodec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
qDebug("Could not open codec.\n");
return;
}
// alloc AVFrame
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
///这里我们改成了 将解码后的YUV数据转换成RGB32
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
// 将分配的内存空间给m_AVFrameRGB使用
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,
pCodecCtx->width, pCodecCtx->height);
// 为AVPacket分别内存空间
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据
int j=0;
while (1)
{
j++;
if (av_read_frame(pFormatCtx, packet) < 0)
{
qDebug() << "av_read_frame < 0";
break; //这里认为视频读取完了
}
if (packet->stream_index == videoStream)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
if (ret < 0) {
qDebug("decode error.\n");
return;
}
if (got_picture)
{// 对解码视频帧进行缩放、格式转换等操作
sws_scale(img_convert_ctx,(uint8_t const * const *) pFrame->data,pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
emit output_imag(image);
}else qDebug() << "got_picture < 0";
}else /*qDebug() << "packet->stream_index not video stream";*/
av_free_packet(packet); //释放资源,否则内存会一直上升
}
//对MyFFmpegInit接口中申请的资源进行释放操作
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}