编译FFMPEG库
在编译FFMPEG库前,我们还需要先进行编译x264的库。
首先需要切换到root用户下进行:
sudo -s
. /opt/aarch64/environment-setup-armv8a-poky-linux
git clone https:
sudo mkdir /opt/arm
sudo mkdir /opt/arm/x264
cd x264
./configure --prefix=/opt/arm/x264 --enable-shared --enable-static --disable-opencl --enable-pic --disable-asm --host=aarch64-linux --cross-prefix=aarch64-poky-linux-
make -j4
make install
引入环境,然后使用GIT把X264代码拉下来,创建安装的目录,然后通过configure进行配置,例如配置安装的目录,我的安装目录是/opt/arm/x264,然后开启编译动态库,静态库,关闭一些没用的,最后的--host是交叉编译必须加的,还需要把交叉编译配置一下,完成后只需要编译,编译完成后安装即可了。
然后接下来就可以编译FFMPEG库了。
wget https:
xz -d ffmpeg-6.0.tar.xz
tar -xvf ffmpeg-6.0.tar
cd ffmpeg-6.0
sudo mkdir /opt/arm/ffmpeg
export PKG_CONFIG_PATH=/opt/arm/x264/lib/pkgconfig
./configure --pkg-config="pkg-config --static" --target-os=linux --arch=arm64 --cross-prefix=aarch64-poky-linux- --prefix=/opt/arm/ffmpeg --enable-version3 --enable-libx264 --enable-nonfree --enable-gpl --disable-x86asm --extra-cflags="-I/opt/arm/x264/include" --extra-ldflags="-L/opt/arm/x264/lib" --enable-cross-compile --sysroot=/opt/aarch64/sysroots/armv8a-poky-linux/
make
make install
接下来的东西基本和上面一样,首先使用wget从ffmpeg官网把源代码下载下来,然后使用xz解压ffmpeg,解压后还是一个tar包,所以再使用tar把ffmpeg的tar包再次解压了。然后一样创建安装的目录,需要配置一下编译环境,接下来通过configure进行配置,一样配置安装的目录,然后开启编译动态库、静态库,配置x264的环境,关闭一些没用的,开启交叉编译环境,除了上面的哪些交叉编译环境,最后还要设置一下sysroot,完成后只需要编译,编译完成后安装即可了,可以直接参照一下我上面的命令即可!
代码编写
然后我们开始写程序的代码。
首先,我们修改一下显示界面,拉进去3个label,2个输入框和1个按钮,删去没用的菜单栏和状态栏,调整一下位置,改一下名称就可以看到如下的界面了。
接下来,创建一个基于QThread的类:
ffmpegthread.h
#ifndef FFMPEGTHREAD_H
#define FFMPEGTHREAD_H
#include <QThread>
#include <QDebug>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/mathematics.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/time.h"
#include "libswresample/swresample.h"
}
class FfmpegThread : public QThread {
Q_OBJECT
public:
FfmpegThread(QObject *parent = 0);
virtual ~FfmpegThread();
signals:
void main_window_signal(int);
protected:
virtual void run();
private:
const char input_link[255] = "rtsp://admin:a1234567@192.168.1.65:554/h264/ch1/sub/av_stream";
const char output_link[255] = "rtmp://192.168.1.1:8910/rtmplive/cctv";
const int width = 640, height = 480, fps = 10;
};
#endif
ffmpegthread.cpp
FfmpegThread::FfmpegThread(QObject *parent) : QThread(parent) {
}
FfmpegThread::~FfmpegThread() { }
void FfmpegThread::run() {
int i_video_output_stream = -1;
int64_t i_video_frame = 0;
avformat_network_init();
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
AVDictionary* options = NULL;
av_dict_set(&options, "max_delay", "200000", 0);
av_dict_set(&options, "stimeout", "1000000", 0);
av_dict_set(&options, "rtsp_transport", "tcp", 0);
AVFormatContext *p_video_input_format_ctx = avformat_alloc_context();
AVStream *p_video_input_stream = NULL;
if (avformat_open_input(&p_video_input_format_ctx, input_link, NULL, &options) != 0) {
qDebug() << "error: input stream open fail!";
emit main_window_signal(-1);
return;
}
if (avformat_find_stream_info(p_video_input_format_ctx, NULL) < 0) {
qDebug() << "error: couldn't find stream information.\n";
emit main_window_signal(-1);
return;
}
for (unsigned int i = 0; i < p_video_input_format_ctx->nb_streams; i++) {
if (p_video_input_format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
p_video_input_stream = p_video_input_format_ctx->streams[i];
break;
}
}
if (p_video_input_stream == NULL) {
qDebug() << "error: couldn't find video stream.\n";
emit main_window_signal(-1);
return;
}
AVFormatContext *p_output_format_ctx;
if (avformat_alloc_output_context2(&p_output_format_ctx, 0, "flv", output_link) != 0) {
qDebug() << "error: avformat_alloc_output_context2!\n";
emit main_window_signal(-1);
return;
}
AVStream *p_video_output_stream = avformat_new_stream(p_output_format_ctx, NULL);
if (!p_video_output_stream) {
qDebug() << "error: avformat_new_stream failed!\n";
emit main_window_signal(-1);
return;
}
if (avcodec_parameters_copy(p_video_output_stream->codecpar, p_video_input_stream->codecpar) < 0) {
qDebug() << "error: avformat_new_stream failed!\n";
emit main_window_signal(-1);
return;
}
p_video_output_stream->codecpar->codec_tag = 0;
if (avio_open(&p_output_format_ctx->pb, output_link, AVIO_FLAG_WRITE) != 0) {
qDebug() << "error: avio_open failed!\n";
emit main_window_signal(-1);
return;
}
if (avformat_write_header(p_output_format_ctx, NULL) != 0) {
qDebug() << "error: avformat_write_header failed!\n";
emit main_window_signal(-1);
return;
}
for (unsigned int i = 0; i < p_output_format_ctx->nb_streams; i++) {
if (p_output_format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
i_video_output_stream = i;
}
}
while (1) {
if (av_read_frame(p_video_input_format_ctx, packet) >= 0){
packet->stream_index = i_video_output_stream;
packet->pts = av_rescale_q_rnd(packet->pts, p_video_input_stream->time_base, p_video_output_stream->time_base, AV_ROUND_NEAR_INF);
packet->dts = av_rescale_q_rnd(packet->dts, p_video_input_stream->time_base, p_video_output_stream->time_base, AV_ROUND_NEAR_INF);
packet->duration = av_rescale_q(packet->duration, p_video_input_stream->time_base, p_video_output_stream->time_base);
packet->pos = -1;
if (av_interleaved_write_frame(p_output_format_ctx, packet) < 0) {
qDebug() << "error: av_interleaved_write_frame failed!\n";
}
av_packet_unref(packet);
i_video_frame++;
}
usleep(10000);
}
}
main.c函数不用动,不过我们需要修改一下mainwindow类:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "ffmpegthread.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void ffmpeg_thread_slot(int code);
private:
Ui::MainWindow *ui;
FfmpegThread *ffmpeg_thread = nullptr;
};
#endif
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
ffmpeg_thread = new FfmpegThread();
connect(ffmpeg_thread, SIGNAL(main_window_signal(int)), this, SLOT(ffmpeg_thread_slot(int)));
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::on_pushButton_clicked() {
ui->pushButton->setEnabled(false);
ffmpeg_thread->start();
}
void MainWindow::ffmpeg_thread_slot(int code) {
if (code == -1) {
ui->pushButton->setEnabled(true);
}
}
在.pro的文件中也需要修改:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
SOURCES += \
ffmpegthread.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
ffmpegthread.h \
mainwindow.h
FORMS += \
mainwindow.ui
LIBS += -lavformat \
-lavfilter \
-lavcodec \
-lavutil \
-lswresample \
-lswscale \
-lm \
-lpthread \
-lrt
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
修改的关键点是增加LIBS,把FFMPEG的内容添加进去,然后进行编译即可。
然后我们输入进去输入链接和输出链接,然后点击Start进行转换,就可以看到状态栏开始转换开了,并且显示已经转换了多少帧了,按钮也变成了Stop按钮。
然后我们打开VLC就可以看到输出链接的运行图了:
FFMPEG的另外一种方法
如果不想编译ffmpeg有没有现成的使用方法呢?
其实还有两种,第二种稍后再说,先说第一种。
打开ffmpeg的官网,点击Download,不过我们不可以选择Linux的小企鹅,而是要选择Windows的窗口,然后选择第二个By Btbn的链接,就可以打开github的release页面了。
打开以后就可以看到各种版本已经为我们编译好的各种版本的ffmpeg库了,我们要选择的是ffmpeg-n6.1.2-linuxarm64-lgpl-shared-6.1.tar.xz,或者我们也可以选择ffmpeg7.0的版本也可以,linuxarm64代表的是我们的系统和架构,LGPL代表的是我们的协议,因为我们要进行开发所以要选择shared的这种版本。