#include "cffmpeg_decode.h" // Cffmpeg_decode::Cffmpeg_decode() { Cffmpeg_decode::Cffmpeg_decode(QObject *parent) : QObject(parent) { inputFormatCtx = avformat_alloc_context(); outputFormatCtx = avformat_alloc_context(); inputPacket = av_packet_alloc(); outputPacket = av_packet_alloc(); yuvFrame = av_frame_alloc(); rgbFrame = av_frame_alloc(); avformat_network_init(); } Cffmpeg_decode::~Cffmpeg_decode() { if (!inputPacket) av_packet_free(&inputPacket); if (!outputPacket) av_packet_free(&outputPacket); if (!yuvFrame) av_frame_free(&yuvFrame); if (!rgbFrame) av_frame_free(&rgbFrame); if (!encoderCtx) avcodec_free_context(&encoderCtx); if (!encoderCtx) avcodec_close(encoderCtx); if (!decoderCtx) avcodec_free_context(&decoderCtx); if (!decoderCtx) avcodec_close(decoderCtx); if (!inputFormatCtx) avformat_close_input(&inputFormatCtx); if (!outputFormatCtx) avformat_close_input(&outputFormatCtx); } void Cffmpeg_decode::setUrl(QString url) { _url = url; } bool Cffmpeg_decode::open_input_file() { if (_url.isEmpty()) return 0; //========================解码============================ AVDictionary *avdic = NULL; // 设置缓存大小,1080p可将值调大 av_dict_set(&avdic, "buffer_size", "2048000", 0); // 以udp方式打开,如果以tcp方式打开将udp替换为tcp av_dict_set(&avdic, "rtsp_transport", "udp", 0); // 设置超时断开连接时间,单位微秒//listen_timeout //av_dict_set(&avdic, "listen_timeout", "200000", 0); av_dict_set(&avdic, "stimeout", "200000", 0); av_dict_set(&avdic, "max_delay", "3", 0); // 设置最大时延 av_dict_set(&avdic, "tune", "zerolatency", 0); av_dict_set(&avdic, "preset", "ultrafast", 0); inputFormatCtx->flags |= AVFMT_FLAG_NONBLOCK; // 打开输入流 if (avformat_open_input(&inputFormatCtx, _url.toUtf8().data(), NULL, &avdic) <0) { printf("Cannot open input file.\n"); return 0; } // 查找流信息 if (avformat_find_stream_info(inputFormatCtx, NULL) < 0) { printf("Cannot find any stream in file.\n"); return 0; } // 从输入流中找到第一个视频流 for (int i = 0; i < inputFormatCtx->nb_streams; i++) { if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; continue; } } if (videoStreamIndex == -1) { printf("Cannot find video stream in file.\n"); return 0; } // 获取视频流的解码器参数 videoCodecPara = inputFormatCtx->streams[videoStreamIndex]->codecpar; decoder = avcodec_find_decoder(videoCodecPara->codec_id); if (!decoder) { printf("Cannot find valid decode codec.\n"); return 0; } // 为解码器上下文分配空间 decoderCtx = avcodec_alloc_context3(decoder); if (!decoderCtx) { printf("Cannot find valid decode codec context.\n"); return 0; } // 初始化解码器上下文 if (avcodec_parameters_to_context(decoderCtx, videoCodecPara) < 0) { printf("Cannot initialize parameters.\n"); return 0; } // 打开解码器 if (avcodec_open2(decoderCtx, decoder, NULL) < 0) { printf("Cannot open codec.\n"); return 0; } //===============================编码================================ // 1、输出文件设置 if (avformat_alloc_output_context2(&outputFormatCtx, NULL, NULL, "output_video.mp4") < 0) { qDebug() << "Could not create output context.\n"; return 0; } // 从输入流中找到第一个视频流 for (int i = 0; i < inputFormatCtx->nb_streams; i++) { if (inputFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndexOut = i; continue; } } if (videoStreamIndexOut == -1) { printf("Cannot find video stream in file.\n"); return 0; } // 获取视频流的解码器参数 videoCodecPara2 = inputFormatCtx->streams[videoStreamIndexOut]->codecpar; // 3、创建输出视频流 outStream = avformat_new_stream(outputFormatCtx, NULL); if (!outStream) { qDebug() << "Failed allocating output stream.\n"; return 0; } //4、查找编码器 encoder = avcodec_find_encoder(videoCodecPara2->codec_id); if(!encoder){ return 0; } //5、分配解码器上下文 encoderCtx = avcodec_alloc_context3(encoder); if (!encoderCtx) { return 0; } //初始化视频AVCodecContext encoderCtx->height = videoCodecPara2->height; encoderCtx->width = videoCodecPara2->width; videoCodecPara2->framerate = av_guess_frame_rate(inputFormatCtx,inputFormatCtx->streams[videoStreamIndexOut],NULL); //视频流的时间基是1 / 帧率 encoderCtx->time_base = av_inv_q(inputFormatCtx->streams[videoStreamIndexOut]->codecpar->framerate); encoderCtx->sample_aspect_ratio = inputFormatCtx->streams[videoStreamIndexOut]->codecpar->sample_aspect_ratio; if (encoder->pix_fmts) { encoderCtx->pix_fmt = encoder->pix_fmts[0]; } encoderCtx->codec_type = AVMEDIA_TYPE_VIDEO; encoderCtx->codec_id = AV_CODEC_ID_H264; encoderCtx->bit_rate = 2000000; encoderCtx->time_base.num = 1; encoderCtx->time_base.den = 20; //全局头 if (outputFormatCtx->oformat->flags & AVFMT_GLOBALHEADER) { encoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } // //设置选项 // AVDictionary *param = 0; // //H.264 // if(videoCodecPara->codec_id == AV_CODEC_ID_H264) { // av_dict_set(¶m, "preset", "slow", 0); // av_dict_set(¶m, "tune", "zerolatency", 0); // av_dict_set(¶m, "profile", "main", 0); // } // //H.265 // if(videoCodecPara->codec_id == AV_CODEC_ID_H265){ // av_dict_set(¶m, "preset", "ultrafast", 0); // av_dict_set(¶m, "tune", "zero-latency", 0); // } //6、打开编码器 if (avcodec_open2(encoderCtx, encoder, NULL) < 0) { qDebug() << "Error occurred when opening encoder.\n"; //return 0; } avcodec_parameters_from_context(outputFormatCtx->streams[0]->codecpar, encoderCtx); //avcodec_parameters_to_context(encoderCtx,outStream->codecpar); // 打开输出文件 if (!(outputFormatCtx->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&outputFormatCtx->pb, outputFormatCtx->url /* "output_video.mp4"*/, AVIO_FLAG_WRITE) < 0) { printf("Could not open output file.\n"); return 0; } } //写入输出文件的头信息 if (avformat_write_header(outputFormatCtx, NULL) < 0) { qDebug() << "Error occurred when opening output file.\n"; //return 0; } //格式化输出输出文件信息 av_dump_format(outputFormatCtx,0,outputFormatCtx->url,1); // 初始化图像转换器 swsCtx = sws_getContext(decoderCtx->width, // decoderCtx->height, // decoderCtx->pix_fmt, // decoderCtx->width, // decoderCtx->height, // AV_PIX_FMT_RGB32, // SWS_BICUBIC, NULL, NULL, NULL); numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, // decoderCtx->width, // decoderCtx->height, // 1); out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char)); int res = av_image_fill_arrays(rgbFrame->data, // rgbFrame->linesize, // out_buffer, // AV_PIX_FMT_RGB32, // decoderCtx->width, // decoderCtx->height, 1); if (res < 0) { qDebug() << "Fill arrays failed.\n"; return 0; } return true; } // 线程里持续执行 void Cffmpeg_decode::run() { if (!open_input_file()) { qDebug() << "Please open video file first."; emit sendConnectFail(1); IsstopPlay = true; return; } // 读取数据包 while (av_read_frame(inputFormatCtx, inputPacket) >= 0) { if (IsstopPlay) { qDebug() << "video play stop"; break; } if (inputPacket->stream_index == videoStreamIndex) { // 解码数据包 if (avcodec_send_packet(decoderCtx, inputPacket) >= 0) { int ret; while ((ret = avcodec_receive_frame(decoderCtx, yuvFrame)) >= 0) { if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { return; } else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); exit(1); } sws_scale(swsCtx, // yuvFrame->data, // yuvFrame->linesize, // 0, // decoderCtx->height, // rgbFrame->data, // rgbFrame->linesize); QImage img(out_buffer, // decoderCtx->width, // decoderCtx->height, // QImage::Format_RGB32); emit sendQImage(img); QThread::msleep(28); //将解码后的帧写入输出文件 if (av_write_frame(outputFormatCtx, inputPacket) < 0) { //qDebug() << "Error muxing packet.\n"; break; } } } av_packet_unref(inputPacket); } //花屏 // if (av_read_frame(inputFormatCtx, inputPacket) < 0) { // break; // 达到文件末尾 // } } } // 退出 void Cffmpeg_decode::stop() { // 写入输出文件的尾信息 // av_write_trailer(outputFormatCtx); IsstopPlay = true; if (!inputPacket) av_packet_free(&inputPacket); if (!outputPacket) av_packet_free(&outputPacket); if (!yuvFrame) av_frame_free(&yuvFrame); if (!rgbFrame) av_frame_free(&rgbFrame); if (!encoderCtx) avcodec_free_context(&encoderCtx); if (!encoderCtx) avcodec_close(encoderCtx); if (!decoderCtx) avcodec_free_context(&decoderCtx); if (!decoderCtx) avcodec_close(decoderCtx); if (!inputFormatCtx) avformat_close_input(&inputFormatCtx); if (!outputFormatCtx) avformat_close_input(&outputFormatCtx); }