提交 d05c6960 编写于 作者: mahuifa's avatar mahuifa

feat:新增AVIOReading示例

上级 058c6bee
#---------------------------------------------------------------------------------------
# @功能: API示例程序,演示如何从通过AVIOContext访问的自定义缓冲区读取数据。
# @编译器: Desktop Qt 5.12.5 MSVC2017 64bit(也支持其它编译器)
# @Qt IDE D:/Qt/Qt5.12.5/Tools/QtCreator/share/qtcreator
#
# @开发者 mhf
# @邮箱 1603291350@qq.com
# @时间 2022-10-23 19:27:48
# @备注
#---------------------------------------------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
widget.cpp
HEADERS += \
widget.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
# 定义程序版本号
VERSION = 1.0.0
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
TARGET = AVIOReading
contains(QT_ARCH, i386){ # 使用32位编译器
DESTDIR = $$PWD/../bin # 程序输出路径
}else{
DESTDIR = $$PWD/../bin64 # 使用64位编译器
}
# msvc 编译器使用utf-8编码
msvc {
QMAKE_CFLAGS += /utf-8
QMAKE_CXXFLAGS += /utf-8
}
# 加载库,ffmpeg n5.1.2版本
win32{
LIBS += -LE:/lib/ffmpeg5-1-2/lib/ -lavcodec -lavfilter -lavformat -lswscale -lavutil -lswresample -lavdevice
INCLUDEPATH += E:/lib/ffmpeg5-1-2/include
DEPENDPATH += E:/lib/ffmpeg5-1-2/include
}
unix:!macx{
LIBS += -L/home/mhf/lib/ffmpeg/ffmpeg-5-1-2/lib -lavcodec -lavfilter -lavformat -lswscale -lavutil -lswresample -lavdevice
INCLUDEPATH += /home/mhf/lib/ffmpeg/ffmpeg-5-1-2/include
DEPENDPATH += /home/mhf/lib/ffmpeg/ffmpeg-5-1-2/include
}
#include "widget.h"
#include <QApplication>
#include <QTextCodec>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "widget.h"
#include "ui_widget.h"
#include <qfiledialog.h>
#include <QDebug>
#include <qthread.h>
#include <qtimer.h>
extern "C" { // 用C规则编译指定的代码
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavutil/file.h"
}
typedef struct BufferData {
uchar* ptr;
quint64 size; // 缓冲区中剩余的大小
}BufferData;
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle(QString("AVIOContext访问的自定义缓冲区读取数据 V%1").arg(APP_VERSION));
}
Widget::~Widget()
{
delete ui;
}
/**
* @brief 选择文件
*/
void Widget::on_pushButton_clicked()
{
QString strName = QFileDialog::getOpenFileName(this, "选择播放视频~!", "/", "视频 (*.mp4 *.m4v *.mov *.avi *.flv);; 其它(*)");
if(strName.isEmpty())
{
return;
}
ui->line_file->setText(strName);
}
void Widget::on_pushButton_2_clicked()
{
int ret = openAV();
if(ret < 0)
{
showError(ret);
}
av_file_unmap(m_buffer, m_bufSize); // 释放m_buffer
avformat_free_context(m_formatContext); // 释放m_formatContext
if(m_avioContext)
{
av_freep(&m_avioContext->buffer); // 释放m_avioBuffer
}
avio_context_free(&m_avioContext); // 释放m_avioContext并置NULL
m_avioBuffer = nullptr;
m_buffer = nullptr;
m_bufSize = 0;
m_formatContext = nullptr;
}
/**
* @brief 自定义非阻塞延时
* @param ms
*/
void msleep(int ms)
{
QEventLoop loop;
QTimer::singleShot(ms, &loop, SLOT(quit()));
loop.exec();
}
/**
* @brief 回调读取数据
* @param opaque
* @param buf
* @param buf_size
* @return
*/
int Widget::read_packet(void *opaque, uint8_t *buf, int buf_size)
{
BufferData *bd = static_cast<BufferData *>(opaque);
buf_size = FFMIN(buf_size, int(bd->size));
if (!buf_size)
{
return AVERROR_EOF; // 文件结尾
}
qDebug() << QString("当前指向缓冲区位置ptr:0x%1 剩余数据长度size:%2").arg(quint64(bd->ptr), 0, 16).arg(bd->size);
/* 将内部缓冲区数据复制到buf */
memcpy(buf, bd->ptr, quint64(buf_size));
bd->ptr += buf_size;
bd->size -= quint64(buf_size);
msleep(1); // 加上延时,否则回调函数执行很快,不能用QThread延时
return buf_size;
}
int Widget::openAV()
{
QString strName = ui->line_file->text();
if(strName.isEmpty())
{
return AVERROR(ENOENT); // 返回文件不存在的错误码
}
// 打开strName文件,将文件中所有数据读取到m_buffer中,读取的数据长度为m_bufSize,最后两个参数与日志相关,基本用不到
int ret = av_file_map(strName.toStdString().data(), &m_buffer, &m_bufSize, 0, nullptr);
if(ret < 0)
{
return ret;
}
showLog(QString("文件总buf:0x%1 文件总长度:%2").arg(quint64(m_buffer), 0, 16).arg(m_bufSize));
m_formatContext = avformat_alloc_context(); // 分配一个解封装上下文,包含了媒体流的格式信息(.mp4 .avi)
if(!m_formatContext)
{
return AVERROR(ENOMEM); // 返回无法分配内存的错误码
}
m_avioBuffer = static_cast<uchar*>(av_malloc(quint64(m_avioBufSize))) ; // 分配一个空间
if(!m_avioBuffer)
{
return AVERROR(ENOMEM); // 返回无法分配内存的错误码
}
showLog(QString("avioBuffer:0x%1 avioBufSize长度:%2").arg(quint64(m_avioBuffer), 0, 16).arg(m_avioBufSize));
BufferData bufData;
bufData.ptr = m_buffer;
bufData.size = m_bufSize;
m_avioContext = avio_alloc_context(m_avioBuffer,
m_avioBufSize,
0,
&bufData,
&read_packet,
nullptr,
nullptr);
if(!m_avioContext)
{
return AVERROR(ENOMEM); // 返回无法分配内存的错误码
}
m_formatContext->pb = m_avioContext;
showLog(QString("缓冲区的开始:0x%1 缓冲区大小:%3").arg(quint64(m_avioContext->buffer), 0, 16).arg(m_avioContext->buffer_size));
ret = avformat_open_input(&m_formatContext, nullptr, nullptr, nullptr);
if(ret < 0)
{
return ret;
}
showLog("回调函数执行完成!");
// 读取媒体文件的数据包以获取流信息。
ret = avformat_find_stream_info(m_formatContext, nullptr);
if(ret < 0)
{
return ret;
}
// 打印关于输入或输出格式的详细信息
av_dump_format(m_formatContext,
0, // 要转储信息的流的索引
strName.toStdString().data(), // 要打印的URL,例如源文件或目标文件
0); // 选择指定的上下文是输入(0)还是输出(1)
return 0;
}
void Widget::showLog(const QString &log)
{
ui->textEdit->append(log);
}
/**
* @brief 显示ffmpeg函数调用异常信息
* @param err
*/
void Widget::showError(int err)
{
static char m_error[1024];
memset(m_error, 0, sizeof (m_error)); // 将数组置零
av_strerror(err, m_error, sizeof (m_error));
showLog(QString("Error:%1 %2").arg(err).arg(m_error));
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
struct AVFormatContext;
struct AVIOContext;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
static int read_packet(void *opaque, uint8_t *buf, int buf_size);
private:
void showError(int err);
int openAV();
void showLog(const QString& log);
private:
Ui::Widget *ui;
AVFormatContext* m_formatContext = nullptr;
AVIOContext * m_avioContext = nullptr;
uchar * m_buffer = nullptr; // 保存打开的媒体文件的所有数据
quint64 m_bufSize = 0; // 打开的文件的总大小
uchar * m_avioBuffer = nullptr; // 从m_buffer中一次读取的数据
int m_avioBufSize = 4096; // 从m_buffer中一次读取的数据长度
};
#endif // WIDGET_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>429</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>1</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>1</number>
</property>
<item row="1" column="0">
<widget class="QTextEdit" name="textEdit">
<property name="styleSheet">
<string notr="true">font: 12pt &quot;黑体&quot;;</string>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="line_file"/>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>选择</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>开始</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
......@@ -15,3 +15,4 @@ SUBDIRS += VideoPlayGL1 # 使用软解码实现的视频播放器 使用OpenG
SUBDIRS += VideoPlayGL2 # 使用软解码实现的视频播放器 使用OpenGL显示YUV图像
SUBDIRS += VideoPlayHW # 使用硬件解码实现的视频播放器
SUBDIRS += VideoPlayHWGL # 使用硬件解码实现的视频播放器 使用OpenGL显示YUV/NV12图像
SUBDIRS += AVIOReading
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册