赞
踩
1、将播放功能进行封装 。
- extern "C" {
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #include <libavutil/imgutils.h>
- }
-
- #include <QApplication>
- #include <QLabel>
- #include <QTimer>
- #include <QAudioOutput>
-
- // 音视频播放器类
- class MediaPlayer : public QObject {
- Q_OBJECT
-
- public:
- MediaPlayer(const QString& filename, QLabel* videoWidget) :
- pFormatContext(nullptr), videoStream(-1), audioStream(-1),
- pVideoFrame(nullptr), pFrameRGB(nullptr), swsContext(nullptr),
- videoWidget(videoWidget), timer(nullptr) {
-
- // 打开音视频文件
- if (!openFile(filename.toUtf8().constData())) {
- qDebug() << "Failed to open file: " << filename;
- }
- }
-
- ~MediaPlayer() {
- // 释放资源
- closeFile();
-
- if (timer) {
- delete timer;
- }
- }
-
- // 打开音视频文件
- bool openFile(const char* filename) {
- // 打开音视频文件
- if (avformat_open_input(&pFormatContext, filename, nullptr, nullptr) != 0) {
- return false;
- }
-
- // 获取音视频流信息
- if (avformat_find_stream_info(pFormatContext, nullptr) < 0) {
- return false;
- }
-
- // 查找音视频流
- for (int i = 0; i < pFormatContext->nb_streams; i++) {
- if (pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
- videoStream = i;
- }
- if (pFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
- audioStream = i;
- }
- }
-
- // 打开视频解码器
- AVCodec* pVideoCodec = avcodec_find_decoder(pFormatContext->streams[videoStream]->codecpar->codec_id);
- AVCodecContext* pVideoCodecContext = avcodec_alloc_context3(pVideoCodec);
- avcodec_parameters_to_context(pVideoCodecContext, pFormatContext->streams[videoStream]->codecpar);
- if (avcodec_open2(pVideoCodecContext, pVideoCodec, nullptr) < 0) {
- return false;
- }
-
- // 创建视频帧
- pVideoFrame = av_frame_alloc();
-
- // 创建RGB帧
- pFrameRGB = av_frame_alloc();
- int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pVideoCodecContext->width, pVideoCodecContext->height, 1);
- uint8_t* buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
- av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pVideoCodecContext->width,
- pVideoCodecContext->height, 1);
-
- // 创建图像转换上下文
- videoSwsContext= sws_getContext(pVideoCodecContext->width, pVideoCodecContext->height, pVideoCodecContext->pix_fmt,
- pVideoCodecContext->width, pVideoCodecContext->height, AV_PIX_FMT_RGB24,
- SWS_BILINEAR, nullptr, nullptr, nullptr);
-
- // 创建音频帧
- pAudioFrame = av_frame_alloc();
-
- //打开音频解码器
- AVCodec* pAudioCodec= avcodec_find_decoder(pFormatContext->streams[audioStream]->codecpar->codec_id);
-
- //获取音频流解码器,或者指定解码器
- AVCodecContext* pAudioCodecContext = avcodec_find_decoder(pAudioCodec->codec_id);
- if (pAudioCodecContext == NULL) {
- return false;
- }
-
- //打开音频解码器
- result = avcodec_open2(pAudioCodec, pAudioCodecContext, NULL);
- if (result < 0) {
- return false;
- }
-
- // 创建重采样上下文并设置参数
- audioSwsContext= swr_alloc();
- av_opt_set_int(audioSwsContext, "in_channel_layout", pAudioCodec->channel_layout, 0);
- av_opt_set_int(audioSwsContext, "in_sample_rate", pAudioCodec->sample_rate, 0);
- av_opt_set_sample_fmt(audioSwsContext, "in_sample_fmt", pAudioCodec->sample_fmt, 0);
- av_opt_set_int(audioSwsContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); // 目标音频格式为立体声
- av_opt_set_int(audioSwsContext, "out_sample_rate", 44100, 0); // 目标音频采样率为44.1kHz
- av_opt_set_sample_fmt(audioSwsContext, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); // 目标音频采样格式为16位有符号整数
- // audioSwsContext= swr_alloc_set_opts(nullptr,
- // AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
- // pAudioCodec->channel_layout,
- // pAudioCodec->sample_fmt,
- // pAudioCodec->sample_rate,
- // 0, nullptr);
- swr_init(audioSwsContext);
-
- // 创建QAudioFormat对象
- QAudioFormat audioFormat;
- audioFormat.setSampleRate(44100/*audioCodec->sample_rate*/);
- audioFormat.setChannelCount(2/*audioCodec->channels*/);
- audioFormat.setSampleSize(16);
- audioFormat.setCodec("audio/pcm");
- audioFormat.setByteOrder(QAudioFormat::LittleEndian);
- audioFormat.setSampleType(QAudioFormat::SignedInt);
-
- QAudioDeviceInfo audioDeviceInfo = QAudioDeviceInfo::defaultOutputDevice();
- if (!audioDeviceInfo.isFormatSupported(audioFormat)){
- audioFormat = audioDeviceInfo.nearestFormat(audioFormat);
- }
-
- // 创建QAudioOutput对象
- audioOutput = new QAudioOutput(audioFormat);
- audioOutput->setVolume(100);
-
- // 打开音频输出设备
- outputDevice = audioOutput->start();
- if (!outputDevice)
- return false;
-
- return true;
- }
-
- // 关闭音视频文件
- void closeFile() {
- if (pFormatContext) {
- avformat_close_input(&pFormatContext);
- pFormatContext = nullptr;
- }
-
- if (pVideoFrame) {
- av_frame_free(&pVideoFrame);
- pVideoFrame = nullptr;
- }
-
- if (pFrameRGB) {
- av_frame_free(&pFrameRGB);
- pFrameRGB = nullptr;
- }
-
- if (videoSwsContext) {
- sws_freeContext(videoSwsContext);
- videoSwsContext= nullptr;
- }
-
- if (pAudioFrame) {
- av_frame_free(&pAudioFrame);
- pAudioFrame= nullptr;
- }
-
- if (audioCodec != NULL) {
- avcodec_close(audioCodec);
- audioCodec = NULL;
- }
-
- if (audioSwsContext) {
- sws_freeContext(audioSwsContext);
- audioSwsContext= nullptr;
- }
-
- // 停止并释放音频输出设备
- if (outputDevice)
- outputDevice->close();
- if (audioOutput){
- audioOutput->stop();
- delete audioOutput;
- audioOutput = nullptr;
- }
- }
-
- // 播放音视频
- void play() {
- AVPacket packet;
- int frameFinished;
-
- // 循环读取音视频帧
- while (av_read_frame(pFormatContext, &packet) >= 0) {
- // 播放视频帧
- if (packet.stream_index == videoStream) {
- frameFinish = avcodec_send_packet(videoCodec, avPacket);
- if (frameFinish < 0) {
- continue;
- }
-
- frameFinished= avcodec_receive_frame(videoCodec, pVideoFrame);
- if (frameFinished< 0) {
- continue;
- }
-
- if (frameFinished>= 0) {
- //将数据转成一张图片
- sws_scale(videoSwsContext, (const uint8_t *const *)pVideoFrame->data, pVideoFrame->linesize, 0, videoHeight, pFrameRGB->data, pFrameRGB->linesize);
-
- // 显示图像
- QImage image(pFrameRGB->data[0], pVideoFrame->width, pVideoFrame->height, QImage::Format_RGB888);
- videoWidget->setPixmap(QPixmap::fromImage(image));
-
- // 计算视频帧显示时间间隔
- int64_t pts = av_frame_get_best_effort_timestamp(pVideoFrame);
- int64_t timeBase = pFormatContext->streams[videoStream]->time_base.den;
- double frameDelay = av_q2d(pFormatContext->streams[videoStream]->r_frame_rate);
- double time = (pts * frameDelay) / timeBase;
-
- // 延时显示下一帧
- QEventLoop loop;
- QTimer::singleShot(time * 1000, &loop, [&]() { loop.quit(); });
- loop.exec();
-
- // 释放内存
- av_packet_unref(&packet);
- }
- }
-
- // 播放音频帧
- if (packet.stream_index == audioStream) {
- // 解码音频帧
- AVCodecContext* pAudioCodecContext = pFormatContext->streams[audioStream]->codec;
- frameFinished= avcodec_send_packet(pAudioCodecContext , packet);
- if (frameFinished< 0) {
- continue;
- }
-
- frameFinished= avcodec_receive_frame(pAudioCodecContext , pAudioFrame);
- if (frameFinished < 0) {
- continue;
- }
-
- if (frameFinish >= 0) {
- // 播放音频帧(你可以在此处使用Qt的音频播放功能)
- // 在这里输出音频数据,你可以根据需要进行相应处理
- qDebug() << "音频帧大小:" << aacFrame->pkt_size;
-
- // 转码音频帧
- // 计算转码后的音频数据大小
- int dstNbSamples = av_rescale_rnd(swr_get_delay(swrCtx, 44100/*pAudioFrame->sample_rate*/) + aacFrame->nb_samples, 44100, 44100/*pAudioFrame->sample_rate*/, AV_ROUND_UP);
- int dstBufferSize = av_samples_get_buffer_size(nullptr, 2, dstNbSamples, AV_SAMPLE_FMT_S16, 0);
-
- // 分配转码后的音频数据缓冲区
- uint8_t *dstBuffer = static_cast<uint8_t *>(av_malloc(dstBufferSize));
-
- // 进行音频转码
- int numSamples = swr_convert(audioSwsContext, &dstBuffer, dstNbSamples, const_cast<const uint8_t **>(pAudioFrame->data), pAudioFrame->nb_samples);
- if (numSamples < 0) {
- qDebug() << "音频转码失败";
- av_freep(&dstBuffer);
- }
- else{
- // 释放资源
- // 将音频帧数据写入音频输出设备
- outputDevice->write((const char *)dstBuffer, dstBufferSize);
- }
-
- // 计算音频帧播放时长
- AVRational timeBase = pFormatContext->streams[audioStream]->time_base;
- int64_t pts = av_frame_get_best_effort_timestamp(pAudioFrame);
- double time = av_q2d(timeBase) * pts;
-
- // 延时播放下一帧
- QEventLoop loop;
- QTimer::singleShot(time * 1000, &loop, [&]() { loop.quit(); });
- loop.exec();
- }
-
- // 释放内存
- av_frame_free(&pAudioFrame);
- av_packet_unref(&packet);
- }
- }
- }
-
- private:
- AVFormatContext* pFormatContext; // 音视频格式上下文
- int videoStream; // 视频流索引
- int audioStream; // 音频流索引
- AVFrame* pVideoFrame; // 视频帧
- AVFrame* pFrameRGB; // RGB帧
- SwsContext* videoSwsContext; // 图像转换上下文
- QLabel* videoWidget; // 用于显示视频的QWidget
- QTimer* timer; // 定时器,用于延时显示
- QAudioOutput *audioOutput;
- QIODevice *outputDevice;
- SwrContext *audioSwsContext;
- };
2、调用示例。
- int main(int argc, char *argv[])
- {
- QApplication app(argc, argv);
- QLabel videoWidget;
- videoWidget.show();
-
- MediaPlayer player("path/to/video_file.mp4", &videoWidget);
- player.play();
-
- return app.exec();
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。