You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PayloadAPP/Src/VideoGL/pushstream.cpp

274 lines
10 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "pushstream.h"
PushStream::PushStream(QObject *parent) : QObject{parent} {}
void PushStream::setRemoteIP(QString url) {
m_pushStreamIP = url;
}
bool PushStream::init(AVFormatContext *inputFormatCtx,
AVPacketQueueManager *queueManager) {
// m_pusherQueue = queue;
m_queueManager = queueManager;
m_inputFormatCtx = inputFormatCtx;
m_start = openNetworkStream(inputFormatCtx);
return m_start;
}
void PushStream::close() {
// QMutexLocker locker(&m_mutex);
m_start = false;
m_end = true;
// qDebug() << "*******m_end0:" << m_end;
}
bool PushStream::openNetworkStream(AVFormatContext *inputFormatCtx) {
if (m_pushStreamIP.isEmpty()) return false;
if (!inputFormatCtx) return false;
// 初始化网络输出流
// QString m_pushStreamIP = "rtsp://182.92.130.23/app/stream999";
int ret = avformat_alloc_output_context2(
&m_outputFormatCtx, NULL, "flv", m_pushStreamIP.toUtf8().constData());
if (ret < 0) {
showError(ret);
free();
qDebug() << "Could not create output context.";
return false;
}
// 复制流信息
for (unsigned int i = 0; i < inputFormatCtx->nb_streams; ++i) {
// AVStream *stream = inputFormatCtx->streams[i];
if (inputFormatCtx->streams[i]->codecpar->codec_type ==
AVMEDIA_TYPE_VIDEO) {
m_istream = inputFormatCtx->streams[i];
m_ostream = avformat_new_stream(m_outputFormatCtx, nullptr);
if (!m_ostream) {
qDebug() << "Failed allocating output stream.\n";
free();
return false;
}
// 复制编解码器参数
ret = avcodec_parameters_copy(m_ostream->codecpar,
m_istream->codecpar);
if (ret < 0) {
showError(ret);
free();
qWarning() << "avcodec_parameters_from_context Failed";
return false;
}
// outputStream->codecpar = inputStream->codecpar;
m_ostream->codecpar->codec_tag = 0;
break;
}
}
m_inputTimeBase = m_istream->time_base;
m_inputFrameRate = m_istream->r_frame_rate;
m_outputTimeBase = m_ostream->time_base;
// 打开输出文件
if (!(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
if (ret = avio_open(&m_outputFormatCtx->pb,
m_pushStreamIP.toUtf8().constData(),
AVIO_FLAG_WRITE) < 0) {
showError(ret);
free();
qDebug() << "Could not open output file.\n";
return false;
}
}
// 写入头文件
ret = avformat_write_header(m_outputFormatCtx, NULL);
if (ret < 0) {
showError(ret);
free();
qDebug() << "Error occurred when write_header into output file.\n";
return false;
}
// sendSPS_PPS(inputFormatCtx, outputFormatCtx);
// m_InitStatus = true;
// startTime = av_gettime_relative();
m_bwriteHeader = true;
m_firstPts = AV_NOPTS_VALUE;
return true;
}
int PushStream::reconnect(int ret) {
if (ret == -10053 || ret == -10054) {
m_end = false;
// qDebug() << "m_end:" << m_end;
for (int nRetryCount = 0; nRetryCount < MAXCONNECT; ++nRetryCount) {
// 关闭输出
if (m_outputFormatCtx &&
!(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(m_outputFormatCtx->pb);
}
ret =
avio_open(&m_outputFormatCtx->pb,
m_pushStreamIP.toUtf8().constData(), AVIO_FLAG_WRITE);
if (ret < 0) {
showError(ret);
qDebug() << "Failed to reconnect"
<< QString::number(nRetryCount + 1);
QString str =
QString("网络中断,尝试重连第%1次!").arg(nRetryCount + 1);
emit sendErrorMessageSignal(str, 3);
if (m_end) break;
// av_usleep(5 * 1000000);
continue;
}
// Try to reconnect
ret = avformat_write_header(m_outputFormatCtx, nullptr);
if (ret < 0) {
showError(ret);
// free();
qDebug() << "Failed to reconnect"
<< QString::number(nRetryCount + 1);
QString str =
QString("网络中断,尝试重连第%1次!").arg(nRetryCount + 1);
emit sendErrorMessageSignal(str, 3);
if (m_end) break;
// nRetryCount++;
// av_usleep(5 * 1000000);
} else {
m_start = true;
m_firstPts = AV_NOPTS_VALUE;
m_frm_cnt = 0;
m_bwriteHeader = true;
emit sendErrorMessageSignal("重连成功!", 1);
m_queueManager->clearPushQueue();
qDebug() << "重连成功!";
return ret;
}
if (m_end) break;
}
m_start = false;
m_bwriteHeader = false;
emit sendErrorMessageSignal("重连失败,推流停止!", 2);
return -1;
}
}
void PushStream::pushStream(int64_t startTime) {
qDebug() << "PushStreamThreadID:" << QThread::currentThreadId();
// m_startTime = startTime;
while (m_start) {
AVPacket *inputPacket = m_queueManager->dequeuePushPacket();
if (inputPacket) {
if (inputPacket->data == nullptr || inputPacket->size <= 0 ||
inputPacket->pts <= 0) {
// av_packet_unref(inputPacket);
continue;
}
int outputStreamIndex = 0;
// int inputStreamIndex = inputPacket->stream_index;
// AVStream *istream = m_inputFormatCtx->streams[inputStreamIndex];
// AVStream *ostream =
// m_outputFormatCtx->streams[outputStreamIndex]; 时间基
// AVRational istream_base = m_istream->time_base;
// AVRational ostream_base = ostream->time_base;
// 没有pts的视频数据如未解码的H.264裸流需要重新计算其pts。
if (inputPacket->pts == AV_NOPTS_VALUE) {
// Duration between 2 frames (us)
int64_t calc_duration =
(double)AV_TIME_BASE / av_q2d(m_inputFrameRate);
// Reset Parameters
inputPacket->pts =
(double)(m_frm_cnt * calc_duration) /
(double)(av_q2d(m_inputTimeBase) * AV_TIME_BASE);
inputPacket->dts = inputPacket->pts;
inputPacket->duration =
(double)calc_duration /
(double)(av_q2d(m_inputTimeBase) * AV_TIME_BASE);
}
// 视频帧推送速度
if (m_firstPts == AV_NOPTS_VALUE) {
m_startTime = av_gettime();
m_firstPts = av_rescale_q(inputPacket->dts, m_inputTimeBase,
AVRational{1, AV_TIME_BASE});
}
auto pts_time = av_rescale_q(inputPacket->pts, m_inputTimeBase,
AVRational{1, AV_TIME_BASE});
int64_t pts_time_corrected = pts_time - m_firstPts;
auto now_time = av_gettime() - m_startTime;
int64_t delay = pts_time_corrected - now_time;
// qDebug() << "****************PushStream sleep time:"
// << QString::number(delay / 1000);
// delay = delay < MAXDELAY ? delay : MAXDELAY;
if (delay > 0) {
// qDebug() << "****************PushStream sleep time:"
// << QString::number(delay / 1000);
av_usleep(delay);
// sleepMsec(40);
} else {
if (delay < -50000) {
// 滞后50ms以上丢弃非重要帧
if (!(inputPacket->flags & AV_PKT_FLAG_KEY)) {
av_packet_unref(inputPacket);
continue;
}
// 调整基准时间,减少滞后
m_startTime += 50000;
}
}
// 计算延时后,重新指定时间戳
av_packet_rescale_ts(inputPacket, m_inputTimeBase,
m_outputTimeBase);
inputPacket->pos = -1;
inputPacket->stream_index = 0;
if (inputPacket->pts < inputPacket->dts) {
// qDebug() << "pts < dts";
if (!(inputPacket->flags & AV_PKT_FLAG_KEY)) {
av_packet_unref(inputPacket);
continue;
}
}
// 向推流服务器推送流数据
m_frm_cnt++;
int ret =
av_interleaved_write_frame(m_outputFormatCtx, inputPacket);
if (ret < 0) {
av_packet_unref(inputPacket);
showError(ret);
// if (ret == -10053) {
// qDebug() << "网络不稳定";
// }
if (reconnect(ret) < 0) {
break;
};
continue;
}
// 数据包写入成功现在可以释放pkt
av_packet_unref(inputPacket);
av_packet_free(&inputPacket);
} else {
QThread::usleep(1000);
}
}
if (m_bwriteHeader) av_write_trailer(m_outputFormatCtx);
free();
qDebug() << "Push Stream End!";
emit sendErrorMessageSignal("推流结束!",
NotificationType::NOTIFICATION_SUCCESS);
}
void PushStream::free() {
m_start = false;
// 关闭输出
if (m_outputFormatCtx && !(m_outputFormatCtx->flags & AVFMT_NOFILE)) {
avio_close(m_outputFormatCtx->pb);
}
if (m_outputFormatCtx) {
avformat_free_context(m_outputFormatCtx);
m_outputFormatCtx = nullptr;
}
}