282 lines
7.8 KiB
C++
282 lines
7.8 KiB
C++
|
//指定文件的编码为UTF-8
|
||
|
#pragma execution_character_set("utf-8")
|
||
|
|
||
|
#include "ReverseDecodThread.h"
|
||
|
|
||
|
ReverseDecodThread::ReverseDecodThread()
|
||
|
{
|
||
|
qDebug() << "FFMPEG版本信息:" << av_version_info();
|
||
|
}
|
||
|
|
||
|
ReverseDecodThread::~ReverseDecodThread()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
功能: 设置媒体文件
|
||
|
*/
|
||
|
int ReverseDecodThread::set_VideoFile(QString media)
|
||
|
{
|
||
|
//打开媒体文件
|
||
|
QByteArray array=media.toUtf8();
|
||
|
strncpy(m_MediaFile, array.data(), sizeof(m_MediaFile));
|
||
|
m_run = 1;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
void ReverseDecodThread::SetSate(int run)
|
||
|
{
|
||
|
m_run = run;
|
||
|
}
|
||
|
|
||
|
int ReverseDecodThread::GetSate()
|
||
|
{
|
||
|
return m_run;
|
||
|
}
|
||
|
|
||
|
|
||
|
//跳转视频帧
|
||
|
void ReverseDecodThread::SetSeekPos(qint64 pos)
|
||
|
{
|
||
|
is_CurrentSeekPos = 1;
|
||
|
m_n64CurrentSeekPos = pos;
|
||
|
m_run=1; //运行状态
|
||
|
|
||
|
//获取系统本地时间
|
||
|
play_base_time=QDateTime::currentMSecsSinceEpoch();
|
||
|
}
|
||
|
|
||
|
|
||
|
void ReverseDecodThread::PausePlay()
|
||
|
{
|
||
|
m_run = 2;
|
||
|
}
|
||
|
|
||
|
void ReverseDecodThread::StopPlay()
|
||
|
{
|
||
|
m_run = 0;
|
||
|
}
|
||
|
|
||
|
void ReverseDecodThread::LogSend(QString text)
|
||
|
{
|
||
|
//qDebug() << text;
|
||
|
}
|
||
|
|
||
|
|
||
|
//线程执行起点
|
||
|
void ReverseDecodThread::run()
|
||
|
{
|
||
|
LogSend("开始播放视频.\n");
|
||
|
StartPlay();
|
||
|
}
|
||
|
|
||
|
|
||
|
//播放视频
|
||
|
int ReverseDecodThread::StartPlay()
|
||
|
{
|
||
|
format_ctx = avformat_alloc_context();
|
||
|
if (!format_ctx) {
|
||
|
LogSend(tr("初始化失败 无法打开视频文件: %1").arg(m_MediaFile));
|
||
|
return -1;
|
||
|
}
|
||
|
//1.打开多媒体流,并且获取一些信息
|
||
|
if(avformat_open_input(&format_ctx, m_MediaFile, nullptr, nullptr) != 0)
|
||
|
{
|
||
|
LogSend(tr("无法打开视频文件: %1").arg(m_MediaFile));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//2. 读取媒体文件的数据包以获取流信息
|
||
|
if(avformat_find_stream_info(format_ctx, nullptr) < 0)
|
||
|
{
|
||
|
LogSend(tr("无法获取流信息.\n"));
|
||
|
return -1;
|
||
|
}
|
||
|
// context = avcodec_alloc_context3(codec)
|
||
|
|
||
|
AVCodecContext* avctx = avcodec_alloc_context3(NULL);
|
||
|
if (nullptr == avctx) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
st_index[AVMEDIA_TYPE_VIDEO] = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
|
||
|
st_index[AVMEDIA_TYPE_AUDIO] = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO,
|
||
|
st_index[AVMEDIA_TYPE_AUDIO],
|
||
|
st_index[AVMEDIA_TYPE_VIDEO],
|
||
|
NULL, 0);
|
||
|
|
||
|
//3.打印视频的信息
|
||
|
LogSend(tr("视频中流的数量: %1\n").arg(format_ctx->nb_streams));
|
||
|
for(int i = 0; i < format_ctx->nb_streams; ++i)
|
||
|
{
|
||
|
const AVStream* stream = format_ctx->streams[i];
|
||
|
|
||
|
if(stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
|
||
|
{
|
||
|
int ret = avcodec_parameters_to_context(avctx, stream->codecpar);
|
||
|
if (ret < 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
//查找解码器
|
||
|
const AVCodec *video_pCodec=avcodec_find_decoder(avctx->codec_id);
|
||
|
//打开解码器
|
||
|
if(avcodec_open2(avctx, video_pCodec,nullptr)!=0)
|
||
|
{
|
||
|
LogSend(tr("解码器打开失败.\n"));
|
||
|
return -1;
|
||
|
}
|
||
|
video_stream_index = i;
|
||
|
bool flag = false;
|
||
|
if (video_width != stream->codecpar->width) {
|
||
|
video_width = stream->codecpar->width;
|
||
|
flag = true;
|
||
|
}
|
||
|
|
||
|
if (video_height != stream->codecpar->height) {
|
||
|
video_height = stream->codecpar->height;
|
||
|
flag = true;
|
||
|
}
|
||
|
|
||
|
if (flag) {
|
||
|
emit VideoSizeChanged(video_width, video_height);
|
||
|
}
|
||
|
|
||
|
|
||
|
LogSend(tr("视频帧的尺寸(以像素为单位): (宽X高)%1x%2 像素格式: %3\n").arg(
|
||
|
stream->codecpar->width).arg(stream->codecpar->height).arg(stream->codecpar->format));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (video_stream_index == -1)
|
||
|
{
|
||
|
LogSend("没有检测到视频流.\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
AVRational frameRate = format_ctx->streams[video_stream_index]->avg_frame_rate;
|
||
|
|
||
|
/*设置视频转码器*/
|
||
|
SRC_VIDEO_pFrame = av_frame_alloc();
|
||
|
RGB24_pFrame = av_frame_alloc();// 存放解码后YUV数据的缓冲区
|
||
|
|
||
|
//将解码后的YUV数据转换成RGB24
|
||
|
img_convert_ctx = sws_getContext(video_width, video_height,
|
||
|
avctx->pix_fmt,video_width, video_height,
|
||
|
AV_PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr);
|
||
|
|
||
|
//计算RGB图像所占字节大小
|
||
|
int numBytes= av_image_get_buffer_size(AV_PIX_FMT_RGBA,video_width,video_height, 1);
|
||
|
|
||
|
//申请空间存放RGB图像数据
|
||
|
out_buffer_rgb = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
|
||
|
|
||
|
// avpicture_fill函数将ptr指向的数据填充到picture内,但并没有拷贝,只是将picture结构内的data指针指向了ptr的数据
|
||
|
/*avpicture_fill((AVPicture *) RGB24_pFrame, out_buffer_rgb, AV_PIX_FMT_RGB24,
|
||
|
video_width, video_height);*/
|
||
|
av_image_fill_arrays(RGB24_pFrame->data, RGB24_pFrame->linesize, out_buffer_rgb, AV_PIX_FMT_RGBA,
|
||
|
video_width, video_height, 1);
|
||
|
|
||
|
qDebug()<<"format_ctx->duration:"<<format_ctx->duration;
|
||
|
|
||
|
//获取系统本地时间
|
||
|
play_base_time=QDateTime::currentMSecsSinceEpoch();
|
||
|
// m_run = 1;
|
||
|
//表示视频加载成功
|
||
|
while(m_run)
|
||
|
{
|
||
|
if(m_run == 2)
|
||
|
{
|
||
|
msleep(100); //暂停播放
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (is_CurrentSeekPos)
|
||
|
{
|
||
|
is_CurrentSeekPos = 0;
|
||
|
//偏移到指定位置再开始解码 AVSEEK_FLAG_BACKWARD 向后找最近的关键帧
|
||
|
av_seek_frame(format_ctx, -1, m_n64CurrentSeekPos* AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);
|
||
|
qDebug()<<"跳转的位置:"<<m_n64CurrentSeekPos;
|
||
|
}
|
||
|
|
||
|
double video_clock;
|
||
|
AVPacket pkt;
|
||
|
|
||
|
//1. 读取一帧数据
|
||
|
if(av_read_frame(format_ctx, &pkt) < 0)
|
||
|
{
|
||
|
m_run=2; //设置为暂停状态
|
||
|
// qDebug()<<"数据读取完毕.";
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if(pkt.stream_index == video_stream_index)
|
||
|
{
|
||
|
//当前时间
|
||
|
video_clock = av_q2d(format_ctx->streams[video_stream_index]->time_base) * pkt.pts;
|
||
|
|
||
|
// qDebug()<<"pkt.pts:"<<pkt.pts<<"video_clock:"<<video_clock;
|
||
|
|
||
|
|
||
|
//解码视频 frame
|
||
|
//2. 发送视频帧
|
||
|
if (avcodec_send_packet(avctx,&pkt) != 0)
|
||
|
{
|
||
|
av_packet_unref(&pkt);//不成功就释放这个pkt
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//3. 接受后对视频帧进行解码
|
||
|
if (avcodec_receive_frame(avctx, SRC_VIDEO_pFrame) != 0)
|
||
|
{
|
||
|
av_packet_unref(&pkt);//不成功就释放这个pkt
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//4. 转格式
|
||
|
sws_scale(img_convert_ctx,
|
||
|
(uint8_t const **) SRC_VIDEO_pFrame->data,
|
||
|
SRC_VIDEO_pFrame->linesize, 0, video_height, RGB24_pFrame->data,
|
||
|
RGB24_pFrame->linesize);
|
||
|
|
||
|
|
||
|
|
||
|
//5. 加载图片数据
|
||
|
QImage image(out_buffer_rgb,video_width,video_height,QImage::Format_RGBA8888);
|
||
|
|
||
|
//通知界面更新
|
||
|
VideoDataOutput(image.copy());
|
||
|
|
||
|
//时间信号
|
||
|
sig_getCurrentTime(video_clock, format_ctx->duration *1.0 / AV_TIME_BASE);
|
||
|
|
||
|
QThread::msleep(40);
|
||
|
}
|
||
|
//释放包
|
||
|
av_packet_unref(&pkt);
|
||
|
|
||
|
}
|
||
|
|
||
|
LogSend("视频音频解码播放器的线程退出成功.\n");
|
||
|
|
||
|
if(SRC_VIDEO_pFrame) av_frame_free(&SRC_VIDEO_pFrame);
|
||
|
if(RGB24_pFrame) av_frame_free(&RGB24_pFrame);
|
||
|
if(img_convert_ctx)sws_freeContext(img_convert_ctx);
|
||
|
if(out_buffer_rgb)av_free(out_buffer_rgb);
|
||
|
|
||
|
SRC_VIDEO_pFrame=nullptr;
|
||
|
RGB24_pFrame=nullptr;
|
||
|
img_convert_ctx=nullptr;
|
||
|
out_buffer_rgb=nullptr;
|
||
|
|
||
|
if(format_ctx)
|
||
|
{
|
||
|
avcodec_free_context(&avctx);
|
||
|
avformat_close_input(&format_ctx);//释放解封装器的空间,以防空间被快速消耗完
|
||
|
avformat_free_context(format_ctx);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|