Qt和ffmpeg结合制作全能解码播放器

发布时间 2023-09-16 18:01:16作者: NAVYSUMMER
#include <QCoreApplication>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QVideoWidget>
#include <QAudioOutput>
#include <QDebug>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 初始化FFmpeg
    av_register_all();

    QWidget window;
    window.setWindowTitle("视频播放器");
    QVBoxLayout *layout = new QVBoxLayout(&window);

    // 创建QVideoWidget
    QVideoWidget *videoWidget = new QVideoWidget;
    layout->addWidget(videoWidget);

    // 创建QAudioOutput
    QAudioOutput *audioOutput = new QAudioOutput;
    QIODevice *audioDevice = audioOutput->start();

    window.show();

    // 打开视频文件
    AVFormatContext *formatContext = nullptr;
    if (avformat_open_input(&formatContext, "your_video_file.mp4", nullptr, nullptr) != 0)
    {
        qDebug() << "无法打开视频文件";
        return a.exec();
    }

    // 查找流信息
    if (avformat_find_stream_info(formatContext, nullptr) < 0)
    {
        qDebug() << "无法获取流信息";
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 查找视频流和音频流
    int videoStreamIndex = -1;
    int audioStreamIndex = -1;
    for (int i = 0; i < formatContext->nb_streams; i++)
    {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStreamIndex = i;
        }
        else if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            audioStreamIndex = i;
        }
    }

    if (videoStreamIndex == -1 || audioStreamIndex == -1)
    {
        qDebug() << "无法找到视频或音频流";
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 创建视频解码器上下文
    AVCodecContext *videoCodecContext = avcodec_alloc_context3(nullptr);
    if (!videoCodecContext)
    {
        qDebug() << "无法分配视频解码器上下文";
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 设置视频解码器参数
    if (avcodec_parameters_to_context(videoCodecContext, formatContext->streams[videoStreamIndex]->codecpar) < 0)
    {
        qDebug() << "无法设置视频解码器参数";
        avcodec_free_context(&videoCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 查找视频解码器
    AVCodec *videoCodec = avcodec_find_decoder(videoCodecContext->codec_id);
    if (!videoCodec)
    {
        qDebug() << "无法找到视频解码器";
        avcodec_free_context(&videoCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 打开视频解码器
    if (avcodec_open2(videoCodecContext, videoCodec, nullptr) < 0)
    {
        qDebug() << "无法打开视频解码器";
        avcodec_free_context(&videoCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 创建音频解码器上下文
    AVCodecContext *audioCodecContext = avcodec_alloc_context3(nullptr);
    if (!audioCodecContext)
    {
        qDebug() << "无法分配音频解码器上下文";
        avcodec_free_context(&videoCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 设置音频解码器参数
    if (avcodec_parameters_to_context(audioCodecContext, formatContext->streams[audioStreamIndex]->codecpar) < 0)
    {
        qDebug() << "无法设置音频解码器参数";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 查找音频解码器
    AVCodec *audioCodec = avcodec_find_decoder(audioCodecContext->codec_id);
    if (!audioCodec)
    {
        qDebug() << "无法找到音频解码器";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 打开音频解码器
    if (avcodec_open2(audioCodecContext, audioCodec, nullptr) < 0)
    {
        qDebug() << "无法打开音频解码器";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 创建解码帧
    AVFrame *videoFrame = av_frame_alloc();
    AVFrame *audioFrame = av_frame_alloc();
    if (!videoFrame || !audioFrame)
    {
        qDebug() << "无法分配解码帧";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 创建解码包
    AVPacket packet;
    av_init_packet(&packet);

    // 创建视频转换上下文
    SwsContext *swsContext = sws_getContext(
        videoCodecContext->width, videoCodecContext->height, videoCodecContext->pix_fmt,
        videoCodecContext->width, videoCodecContext->height, AV_PIX_FMT_RGB24,
        SWS_BILINEAR, nullptr, nullptr, nullptr);

    // 创建音频重采样上下文
    SwrContext *swrContext = swr_alloc();
    if (!swrContext)
    {
        qDebug() << "无法分配音频重采样上下文";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        return a.exec();
    }

    // 设置音频重采样参数
    av_opt_set_int(swrContext, "in_channel_layout", audioCodecContext->channel_layout, 0);
    av_opt_set_int(swrContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
    av_opt_set_int(swrContext, "in_sample_rate", audioCodecContext->sample_rate, 0);
    av_opt_set_int(swrContext, "out_sample_rate", 44100, 0);
    av_opt_set_sample_fmt(swrContext, "in_sample_fmt", audioCodecContext->sample_fmt, 0);
    av_opt_set_sample_fmt(swrContext, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);

    // 初始化音频重采样上下文
    if (swr_init(swrContext) < 0)
    {
        qDebug() << "无法初始化音频重采样上下文";
        avcodec_free_context(&videoCodecContext);
        avcodec_free_context(&audioCodecContext);
        avformat_close_input(&formatContext);
        swr_free(&swrContext);
        return a.exec();
    }

    // 分配音频重采样缓冲区
    uint8_t *audioBuffer = nullptr;
    int audioBufferLength = av_samples_alloc(&audioBuffer, nullptr, 2, 44100, AV_SAMPLE_FMT_S16, 0);

    // 读取并显示视频帧和播放音频
    while (av_read_frame(formatContext, &packet) >= 0)
    {
        if (packet.stream_index == videoStreamIndex)
        {
            if (avcodec_send_packet(videoCodecContext, &packet) < 0)
            {
                qDebug() << "无法发送视频解码包";
                break;
            }

            if (avcodec_receive_frame(videoCodecContext, videoFrame) == 0)
            {
                // 将视频帧转换为RGB格式
                sws_scale(swsContext, (const uint8_t *const *)videoFrame->data, videoFrame->linesize, 0, videoCodecContext->height, videoFrame->data, videoFrame->linesize);

                // 将RGB帧显示在QVideoWidget上
                QImage img(videoFrame->data[0], videoCodecContext->width, videoCodecContext->height, videoFrame->linesize[0], QImage::Format_RGB888);
                QPixmap pixmap = QPixmap::fromImage(img);
                videoWidget->setAspectRatioMode(Qt::KeepAspectRatio);
                videoWidget->setPixmap(pixmap);
                videoWidget->update();
            }
        }
        else if (packet.stream_index == audioStreamIndex)
        {
            if (avcodec_send_packet(audioCodecContext, &packet) < 0)
            {
                qDebug() << "无法发送音频解码包";
                break;
            }

            if (avcodec_receive_frame(audioCodecContext, audioFrame) == 0)
            {
                // 重采样音频帧
                swr_convert(swrContext, &audioBuffer, audioBufferLength, (const uint8_t **)audioFrame->data, audioFrame->nb_samples);

                // 将音频帧写入音频设备
                audioDevice->write((const char *)audioBuffer, audioBufferLength);
            }
        }
        av_packet_unref(&packet);
    }

    // 释放资源
    av_free(audioBuffer);
    av_frame_free(&audioFrame);
    av_frame_free(&videoFrame);
    swr_free(&swrContext);
    sws_freeContext(swsContext);
    avcodec_close(videoCodecContext);
    avcodec_free_context(&videoCodecContext);
    avcodec_close(audioCodecContext);
    avcodec_free_context(&audioCodecContext);
    avformat_close_input(&formatContext);

    return a.exec();
}