You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PayloadAPP/Src/Video/cffmpeg_decode.cpp

327 lines
10 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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(&param, "preset", "slow", 0);
// av_dict_set(&param, "tune", "zerolatency", 0);
// av_dict_set(&param, "profile", "main", 0);
// }
// //H.265
// if(videoCodecPara->codec_id == AV_CODEC_ID_H265){
// av_dict_set(&param, "preset", "ultrafast", 0);
// av_dict_set(&param, "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);
}