|
|
|
|
#include "savestream.h"
|
|
|
|
|
|
|
|
|
|
SaveStream::SaveStream(QObject *parent) : QObject{parent} {}
|
|
|
|
|
|
|
|
|
|
bool SaveStream::init(AVFormatContext *formatContext,
|
|
|
|
|
AVPacketQueueManager *queueManager, int videoIndex) {
|
|
|
|
|
// m_inputCodecContex = codecContex;
|
|
|
|
|
m_inputFormatContext = formatContext;
|
|
|
|
|
// m_saverQueue = queue;
|
|
|
|
|
m_queueManager = queueManager;
|
|
|
|
|
m_videoIndex = videoIndex;
|
|
|
|
|
m_start = openFile();
|
|
|
|
|
if (!m_start) {
|
|
|
|
|
free();
|
|
|
|
|
}
|
|
|
|
|
return m_start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveStream::setSaveFileDirPath(QString fileDirPath) {
|
|
|
|
|
m_outputDirPath = fileDirPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveStream::close() {
|
|
|
|
|
// QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_start = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveStream::startSaveStream() {
|
|
|
|
|
qDebug() << "SaveStreamThreadID:" << QThread::currentThreadId();
|
|
|
|
|
if (!m_start) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
while (m_start || !m_queueManager->isEmptySaveQueue()) {
|
|
|
|
|
AVPacket *inputPacket = m_queueManager->dequeueSavePacket();
|
|
|
|
|
if (inputPacket) {
|
|
|
|
|
// 由于保存的m_formatContextSave只创建了一个视频流,而读取到的图像的流索引不一定为0,可能会出现错误【Invalid
|
|
|
|
|
// packet stream index: 1】
|
|
|
|
|
// 所以这里需要将stream_index指定为和m_formatContextSave中视频流索引相同,因为就一个流,所以直接设置为0
|
|
|
|
|
inputPacket->stream_index = 0;
|
|
|
|
|
av_write_frame(m_formatContextSave,
|
|
|
|
|
inputPacket); // 将数据包写入输出媒体文件
|
|
|
|
|
av_packet_unref(inputPacket);
|
|
|
|
|
inputPacket = nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
QThread::usleep(1000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 写入文件尾
|
|
|
|
|
if (m_formatContextSave && m_writeHeader) {
|
|
|
|
|
av_write_trailer(m_formatContextSave);
|
|
|
|
|
m_writeHeader = false;
|
|
|
|
|
}
|
|
|
|
|
free();
|
|
|
|
|
qDebug() << "视频保存结束!";
|
|
|
|
|
emit sendErrorMessageSignal("视频保存结束!",
|
|
|
|
|
NotificationType::NOTIFICATION_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SaveStream::openFile() {
|
|
|
|
|
// QMutexLocker locker(&m_mutex);
|
|
|
|
|
QDir dir;
|
|
|
|
|
if (!dir.exists(m_outputDirPath)) {
|
|
|
|
|
dir.mkdir(m_outputDirPath);
|
|
|
|
|
}
|
|
|
|
|
QString strName =
|
|
|
|
|
QString("/%1.h264")
|
|
|
|
|
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH-mm-ss"));
|
|
|
|
|
strName = m_outputDirPath + strName;
|
|
|
|
|
// const AVOutputFormat *ofmt = av_guess_format("mp4", NULL, NULL);
|
|
|
|
|
int ret = avformat_alloc_output_context2(
|
|
|
|
|
&m_formatContextSave, nullptr, nullptr, strName.toStdString().data());
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
qWarning() << "avformat_alloc_output_context2 Error!";
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// m_videoStreamOut->codecpar->codec_tag = 0;
|
|
|
|
|
// if (m_formatContextSave->oformat->flags & AVFMT_GLOBALHEADER) {
|
|
|
|
|
// m_formatContextSave->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
|
|
|
// // m_videoStreamOut->codecpar->extradata = (uint8_t *)av_malloc(1024);
|
|
|
|
|
// // m_videoStreamOut->codecpar->extradata_size = 0;
|
|
|
|
|
// }
|
|
|
|
|
// 创建并初始化AVIOContext以访问url所指示的资源。
|
|
|
|
|
ret = avio_open(&m_formatContextSave->pb, strName.toStdString().data(),
|
|
|
|
|
AVIO_FLAG_WRITE);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
qWarning() << "Open file Error";
|
|
|
|
|
free();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 向媒体文件添加新流
|
|
|
|
|
m_videoStreamOut = avformat_new_stream(m_formatContextSave, nullptr);
|
|
|
|
|
if (!m_videoStreamOut) {
|
|
|
|
|
free();
|
|
|
|
|
qWarning() << "Create New Stream Error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 拷贝一些参数,给codecpar赋值(这里使用编码器上下文进行赋值)
|
|
|
|
|
// ret = avcodec_parameters_from_context(m_videoStreamOut->codecpar,
|
|
|
|
|
// m_inputCodecContex);
|
|
|
|
|
ret = avcodec_parameters_copy(
|
|
|
|
|
m_videoStreamOut->codecpar,
|
|
|
|
|
m_inputFormatContext->streams[m_videoIndex]->codecpar);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
free();
|
|
|
|
|
qWarning() << "avcodec_parameters_from_context Failed";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 写入文件头
|
|
|
|
|
ret = avformat_write_header(m_formatContextSave, nullptr);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
showError(ret);
|
|
|
|
|
free();
|
|
|
|
|
qWarning() << "avformat_write_header Error";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_writeHeader = true;
|
|
|
|
|
qDebug() << "保存视频文件初始化成功!";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveStream::free() {
|
|
|
|
|
// 关闭文件
|
|
|
|
|
if (m_formatContextSave && !(m_formatContextSave->flags & AVFMT_NOFILE)) {
|
|
|
|
|
avio_close(m_formatContextSave->pb);
|
|
|
|
|
// av_freep(m_videoStreamOut);
|
|
|
|
|
|
|
|
|
|
// avformat_free_context(m_formatContextSave);
|
|
|
|
|
// m_formatContextSave = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (m_formatContextSave) {
|
|
|
|
|
avformat_free_context(m_formatContextSave);
|
|
|
|
|
m_formatContextSave = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|