在第六篇报告中,我们重新将支持RKMPP的FFmpeg进行了编译,接下来我们就会在QT中使用刚刚编译好的FFmpeg进行API编程。首先我们先新建一个FFmpeg线程类,然后把FFmpeg的头文件先包含进去。
可以看到,这次不再报红了,那就说明已经找到系统中的头文件了。
先把我们新建的FFmpegThread类的头文件和源代码,先粘贴上来:
头文件:
#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();
void set_link(const char *rtsp, const char *rtmp);
void stop();
signals:
void main_window_signal(int);
protected:
virtual void run();
private:
bool status = true;
char input_link[255];
char output_link[255];
const int width = 640, height = 480, fps = 10;
};
#endif
源代码:
FfmpegThread::FfmpegThread(QObject *parent) : QThread(parent) {
}
FfmpegThread::~FfmpegThread() { }
void FfmpegThread::set_link(const char *rtsp, const char *rtmp) {
strcpy(this->input_link, rtsp);
strcpy(this->output_link, rtmp);
}
void FfmpegThread::stop() {
status = false;
}
void FfmpegThread::run() {
int i_video_output_stream = -1;
int64_t i_video_frame = 0;
status = true;
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 (status) {
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++;
if (i_video_frame % 30) {
emit main_window_signal(i_video_frame);
}
}
usleep(10000);
}
}
还需要修改的是MainWindow类:
头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QProcess>
#include <stdlib.h>
#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 onReadyReadStandardOutput();
void onReadyReadStandardError();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void ffmpeg_thread_slot(int index_frame);
private:
Ui::MainWindow *ui;
int type = 0;
QProcess *ffmpegProcess;
FfmpegThread *ffmpeg_thread = nullptr;
char rtsp_link[255], rtmp_link[255];
};
#endif
源代码:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ffmpegProcess = new QProcess(this);
ffmpegProcess->setProcessChannelMode(QProcess::MergedChannels);
connect(ffmpegProcess, &QProcess::readyReadStandardOutput, this, &MainWindow::onReadyReadStandardOutput);
connect(ffmpegProcess, &QProcess::readyReadStandardError, this, &MainWindow::onReadyReadStandardError);
ffmpeg_thread = new FfmpegThread();
connect(ffmpeg_thread, &FfmpegThread::main_window_signal, this, &MainWindow::ffmpeg_thread_slot);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onReadyReadStandardOutput()
{
QByteArray output = ffmpegProcess->readAllStandardOutput();
ui->label_status->setText(QString("Status: Output - ") + QString(output));
}
void MainWindow::onReadyReadStandardError()
{
QByteArray error = ffmpegProcess->readAllStandardError();
ui->label_status->setText(QString("Status: Error - ") + QString(error));
}
void MainWindow::ffmpeg_thread_slot(int index_frame) {
ui->label_status->setText(QString("Status: FPS - ") + QString::number(index_frame));
}
void MainWindow::on_pushButton_clicked()
{
if (ui->lineEdit_rtsp->text().length() == 0) {
ui->label_status->setText("Status: Error - rtsp link is empty");
return;
}
if (ui->lineEdit_rtmp->text().length() == 0) {
ui->label_status->setText("Status: Error - rtmp link is empty");
return;
}
ffmpeg_thread->set_link(ui->lineEdit_rtsp->text().toStdString().c_str(), ui->lineEdit_rtmp->text().toStdString().c_str());
ffmpeg_thread->start();
type = 1;
}
void MainWindow::on_pushButton_2_clicked()
{
if (type == 1) {
ffmpeg_thread->stop();
}
if (type == 2) {
ffmpegProcess->write("q");
}
type = 0;
}
void MainWindow::on_pushButton_3_clicked()
{
if (ui->lineEdit_rtmp->text().length() == 0) {
ui->label_status->setText("Status: Error - rtmp link is empty");
return;
}
strcpy(rtmp_link, ui->lineEdit_rtmp->text().toStdString().c_str());
QString program = "ffmpeg";
QStringList arguments;
arguments << "-f" << "v4l2" << "-pixel_format" << "nv12" << "-i" << "/dev/video73" << "-vcodec" << "h264_rkmpp" << "-an" << "-f" << "flv" << rtmp_link;
ffmpegProcess->start(program, arguments);
type = 2;
}
还需要修改的是工程文件,我们需要添加一下库文件。
具体工程文件,我们还是以附件形式上传。
*附件:test1.zip
然后我们点击编译运行,然后我们把RTSP和RTMP的链接填写好,然后点击运行就可以看到状态栏已经开始输出内容了。
接下来我们再使用VLC打开直播来查看一下运行是否正常。