使 Qt Player 编解码器独立
Posted
技术标签:
【中文标题】使 Qt Player 编解码器独立【英文标题】:Make Qt Player codec independent 【发布时间】:2016-03-16 05:40:15 【问题描述】:我开发的 Qt 应用程序可以使用下面的代码播放多个视频文件。
QMediaPlayer *player;
QString fileName = "C:/username/test.h264";
player->setmedia(QUrl::fromLocalFile(fileName));
在启动时我无法播放所有类型的视频文件,所以我在我的系统上安装了编解码器,现在当我的播放器启动编解码器解码器启动时,我的 CPU 使用率达到了很高的水平。(显示波纹管图像)
您可以在上图中看到外部解码器启动的右下角 LAW(红色标签)。
现在,我想让我的 Qt Player 编解码器独立,意味着我知道我的播放器只能播放 .h264 文件,所以我将只使用 h264 解码器,不需要音频,所以我会不使用音频解码器。
据我所知,QMediaPlayer 在出现图片时会启动解码器,如果我错了,请纠正我。那么我该怎么做才能停止外部解码器并在内部解码帧并成功播放?
编辑:使用 FFmpeg 进行音频解码的代码
FFmpegAudio.pro
TARGET = fooAudioFFMPEG
QT += core gui qml quick widgets
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp
HEADERS += mainwindow.h \
wrapper.h
FORMS += mainwindow.ui
QMAKE_CXXFLAGS += -D__STDC_CONSTANT_MACROS
LIBS += -pthread
LIBS += -L/usr/local/lib
LIBS += -lavdevice
LIBS += -lavfilter
LIBS += -lpostproc
LIBS += -lavformat
LIBS += -lavcodec
LIBS += -ldl
LIBS += -lXfixes
LIBS += -lXext
LIBS += -lX11
LIBS += -lasound
LIBS += -lSDL
LIBS += -lx264
LIBS += -lvpx
LIBS += -lvorbisenc
LIBS += -lvorbis
LIBS += -logg
LIBS += -lopencore-amrwb
LIBS += -lopencore-amrnb
LIBS += -lmp3lame
LIBS += -lfaac
LIBS += -lz
LIBS += -lrt
LIBS += -lswscale
LIBS += -lavutil
LIBS += -lm
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui
class MainWindow;
class MainWindow : public QMainWindow
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void changeEvent(QEvent *e);
private:
Ui::MainWindow *ui;
private slots:
void on_pushButton_clicked();
;
#endif // MAINWINDOW_H
wrapper.h
#ifndef WRAPPER_H_
#define WRAPPER_H_
#include <math.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt)
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE)
if (*p == sample_fmt)
return 1;
p++;
return 0;
/* just pick the highest supported samplerate */
static int select_sample_rate(AVCodec *codec)
const int *p;
int best_samplerate = 0;
if (!codec->supported_samplerates)
return 44100;
p = codec->supported_samplerates;
while (*p)
best_samplerate = FFMAX(*p, best_samplerate);
p++;
return best_samplerate;
/* select layout with the highest channel count */
static int select_channel_layout(AVCodec *codec)
const uint64_t *p;
uint64_t best_ch_layout = 0;
int best_nb_channells = 0;
if (!codec->channel_layouts)
return AV_CH_LAYOUT_STEREO;
p = codec->channel_layouts;
while (*p)
int nb_channels = av_get_channel_layout_nb_channels(*p);
if (nb_channels > best_nb_channells)
best_ch_layout = *p;
best_nb_channells = nb_channels;
p++;
return best_ch_layout;
/*
* Audio encoding example
*/
static void audio_encode_example(const char *filename)
AVCodec *codec;
AVCodecContext *c= NULL;
AVFrame *frame;
AVPacket pkt;
int i, j, k, ret, got_output;
int buffer_size;
FILE *f;
uint16_t *samples;
float t, tincr;
printf("Encode audio file %s\n", filename);
/* find the MP2 encoder */
codec = avcodec_find_encoder(AV_CODEC_ID_MP2);
if (!codec)
fprintf(stderr, "Codec not found\n");
exit(1);
c = avcodec_alloc_context3(codec);
if (!c)
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
/* put sample parameters */
c->bit_rate = 64000;
/* check that the encoder supports s16 pcm input */
c->sample_fmt = AV_SAMPLE_FMT_S16;
if (!check_sample_fmt(codec, c->sample_fmt))
fprintf(stderr, "Encoder does not support sample format %s",
av_get_sample_fmt_name(c->sample_fmt));
exit(1);
/* select other audio parameters supported by the encoder */
c->sample_rate = select_sample_rate(codec);
c->channel_layout = select_channel_layout(codec);
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0)
fprintf(stderr, "Could not open codec\n");
exit(1);
f = fopen(filename, "wb");
if (!f)
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
/* frame containing input raw audio */
frame = avcodec_alloc_frame();
if (!frame)
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
frame->nb_samples = c->frame_size;
frame->format = c->sample_fmt;
frame->channel_layout = c->channel_layout;
/* the codec gives us the frame size, in samples,
* we calculate the size of the samples buffer in bytes */
buffer_size = av_samples_get_buffer_size(NULL, c->channels, c->frame_size,
c->sample_fmt, 0);
samples = (uint16_t *)av_malloc(buffer_size);
if (!samples)
fprintf(stderr, "Could not allocate %d bytes for samples buffer\n",
buffer_size);
exit(1);
/* setup the data pointers in the AVFrame */
ret = avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
(const uint8_t*)samples, buffer_size, 0);
if (ret < 0)
fprintf(stderr, "Could not setup audio frame\n");
exit(1);
/* encode a single tone sound */
t = 0;
tincr = 2 * M_PI * 440.0 / c->sample_rate;
for(i=0;i<200;i++)
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
for (j = 0; j < c->frame_size; j++)
samples[2*j] = (int)(sin(t) * 10000);
for (k = 1; k < c->channels; k++)
samples[2*j + k] = samples[2*j];
t += tincr;
/* encode the samples */
ret = avcodec_encode_audio2(c, &pkt, frame, &got_output);
if (ret < 0)
fprintf(stderr, "Error encoding audio frame\n");
exit(1);
if (got_output)
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
/* get the delayed frames */
for (got_output = 1; got_output; i++)
ret = avcodec_encode_audio2(c, &pkt, NULL, &got_output);
if (ret < 0)
fprintf(stderr, "Error encoding frame\n");
exit(1);
if (got_output)
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
fclose(f);
av_freep(&samples);
avcodec_free_frame(&frame);
avcodec_close(c);
av_free(c);
/*
* Audio decoding.
*/
static void audio_decode_example(const char *outfilename, const char *filename)
AVCodec *codec;
AVCodecContext *c= NULL;
int len;
FILE *f, *outfile;
uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
AVPacket avpkt;
AVFrame *decoded_frame = NULL;
av_init_packet(&avpkt);
printf("Decode audio file %s to %s\n", filename, outfilename);
/* find the mpeg audio decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_MP2);
if (!codec)
fprintf(stderr, "Codec not found\n");
exit(1);
c = avcodec_alloc_context3(codec);
if (!c)
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0)
fprintf(stderr, "Could not open codec\n");
exit(1);
f = fopen(filename, "rb");
if (!f)
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
outfile = fopen(outfilename, "wb");
if (!outfile)
av_free(c);
exit(1);
/* decode until eof */
avpkt.data = inbuf;
avpkt.size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
while (avpkt.size > 0)
int got_frame = 0;
if (!decoded_frame)
if (!(decoded_frame = avcodec_alloc_frame()))
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
else
avcodec_get_frame_defaults(decoded_frame);
len = avcodec_decode_audio4(c, decoded_frame, &got_frame, &avpkt);
if (len < 0)
fprintf(stderr, "Error while decoding\n");
exit(1);
if (got_frame)
/* if a frame has been decoded, output it */
int data_size = av_samples_get_buffer_size(NULL, c->channels,
decoded_frame->nb_samples,
c->sample_fmt, 1);
fwrite(decoded_frame->data[0], 1, data_size, outfile);
avpkt.size -= len;
avpkt.data += len;
avpkt.dts =
avpkt.pts = AV_NOPTS_VALUE;
if (avpkt.size < AUDIO_REFILL_THRESH)
/* Refill the input buffer, to avoid trying to decode
* incomplete frames. Instead of this, one could also use
* a parser, or use a proper container format through
* libavformat. */
memmove(inbuf, avpkt.data, avpkt.size);
avpkt.data = inbuf;
len = fread(avpkt.data + avpkt.size, 1,
AUDIO_INBUF_SIZE - avpkt.size, f);
if (len > 0)
avpkt.size += len;
fclose(outfile);
fclose(f);
avcodec_close(c);
av_free(c);
avcodec_free_frame(&decoded_frame);
/*
* Main WRAPPER function
*/
void service()
/* register all the codecs */
avcodec_register_all();
audio_encode_example("test.mp2");
audio_decode_example("test.sw", "test.mp2");
#endif
main.cpp
#include <QApplication>
#include "mainwindow.h"
extern "C"
#include "wrapper.h"
int main(int argc, char *argv[])
service(); //calling the function service inside the wrapper
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
ui->setupUi(this);
MainWindow::~MainWindow()
delete ui;
void MainWindow::changeEvent(QEvent *e)
QMainWindow::changeEvent(e);
switch (e->type())
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
void MainWindow::on_pushButton_clicked()
this->close();
mainwindow.ui //没什么重要的
谢谢。
【问题讨论】:
【参考方案1】:Qt 的内置媒体播放器使用系统编解码器。如果你想颠覆它,你将需要使用其他东西。
我没用过,但周围有提到QtAV,它使用的是FFmpeg。
QtAV 网站指出:QtMultimedia 的实现依赖于平台。它在 windows 上使用 dshow,在 linux 上使用 gstream 等。支持新平台可能不是一件容易的工作。
【讨论】:
感谢您的信息,我知道 QtAV 是 Qt 和 FFmpeg 组合的好例子,但它很难理解,我想要一些简单的解决方案来在我的播放器中播放帧。我有一个使用 FFmpeg 包装器解码音频的示例,我将在我的问题区域中添加一些代码部分。【参考方案2】:仅仅为了在Qt中播放视频文件,你也可以使用libvlc,在著名的媒体播放器(http://www.videolan.org)之后
初始化:
libvlc_instance_t *vlcinstance = libvlc_new(0, NULL);
libvlc_media_player_t *player = libvlc_media_player_new(vlcinstance);
libvlc_media_t *media = libvlc_media_new_path(vlcinstance, file_path);
使用一些 qt 小部件来显示视频:
libvlc_media_player_set_drawable(player, some_widget->winId());
播放视频:
libvlc_media_player_set_media(player, media);
libvlc_media_player_play(player);
这个示例代码当然省略了错误检查。
有关更完整的示例,请参阅以下链接:https://wiki.videolan.org/LibVLC_SampleCode_Qt
【讨论】:
感谢@Philipp Ludwig,我认为这个应用程序也将依赖于平台,如果我没记错的话,意味着最后 vlc 将使用 OS 编解码器。我想要完全独立于平台,这意味着根据上面的示例音频文件使用 FFmpeg 包装器独立编码和解码,不关心哪个操作系统和编码器。 @TejasVirpariya VLC 库附带了它自己的编解码器集合,因此它可以播放各种格式,而无需任何操作系统安装的视频编解码器。 感谢@Philipp Ludwig,我会检查 CPU 性能,如果它可以实现我的目标,我肯定会使用 LibVLC。再次感谢。以上是关于使 Qt Player 编解码器独立的主要内容,如果未能解决你的问题,请参考以下文章
QT 5.8 WebEngine Html 5 视频播放器支持