这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » OK3588J-C开发板在QT中使用FFmpegAPI编程

共1条 1/1 1 跳转至

OK3588J-C开发板在QT中使用FFmpegAPI编程

工程师
2025-01-25 12:24:49     打赏

我们重新将支持RKMPP的FFmpeg进行了编译,接下来我们就会在QT中使用刚刚编译好的FFmpeg进行API编程。首先我们先新建一个FFmpeg线程类,然后把FFmpeg的头文件先包含进去。

10701.png

可以看到,这次不再报红了,那就说明已经找到系统中的头文件了。
先把我们新建的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_H

源代码:

#include "ffmpegthread.h"

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);
}

// 其实这个地方最好是用emit发送信号的方式进行,后期改进
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);  //tcp方式打开

    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;

    ///打开rtmp 的网络输出IO  AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。
    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_H

源代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ffmpegProcess = new QProcess(this);
    ffmpegProcess->setProcessChannelMode(QProcess::MergedChannels);
    //connect(ffmpegProcess, &QProcess::readyRead, this, &MainWindow::onReadyReadStandardOutput);
    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;
}

还需要修改的是工程文件,我们需要添加一下库文件。

10702.png

然后我们点击编译运行,然后我们把RTSP和RTMP的链接填写好,然后点击运行就可以看到状态栏已经开始输出内容了。

10703.png

接下来我们再使用VLC打开直播来查看一下运行是否正常。
10704.png





关键词: OK3588J-C     FFmpeg     编程    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]