Darwin推流存储实现介绍 之二

2024-04-29 02:08

本文主要是介绍Darwin推流存储实现介绍 之二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

根据上文确定的方案,我们会在每一个推流的会话建立时,增加一个自定义的Output对象,用来实现吧接收到的RTP包写到文件中。


首先,我们在RTSPReflectorOutput.cpp中,参考RTPSessionOutput类定义一个RTPSessionSaveOutput类:


class RTPSessionSaveOutput: public ReflectorOutput
{
public:RTPSessionSaveOutput(ReflectorSession* inReflectorSession, char* theFileName = NULL);virtual ~RTPSessionSaveOutput();//关键是重写这个方法,完成RTP包的存储。QTSS_Error  WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSec, Bool16 firstPacket );ReflectorSession* GetReflectorSession();//{ return fReflectorSession; }void        TearDown() {}Bool16      IsUDP() { return true;}Bool16      IsPlaying() { return true;}bool MkFileDirandFile();void Write_Header();private:ReflectorSession*       fReflectorSession;char        			 fFilePath[MaxPathSize];char        			 fFileName[255];SInt32      			 ffd;  // file handleBool16  bLastBlockFlg;//SInt64  lastArrivalTimeMSec;         
};


并完成实现:

#define DEFAULTPATH "/usr/local/movies/pushstream/"
RTPSessionSaveOutput::RTPSessionSaveOutput(ReflectorSession* inReflectorSession, char* theFileName):fReflectorSession(inReflectorSession),ffd(-1)
{printf("------- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ,theFileName =%s \n", theFileName);::memset(fFileName, 0, sizeof(fFileName));::memset(fFilePath, 0 , sizeof(fFilePath));bLastBlockFlg = false;lastArrivalTimeMSec = -1;strcpy(fFilePath, DEFAULTPATH);char *firstPos = ::strstr(theFileName, ".sdp");if (firstPos != NULL){int length = strlen(theFileName);int i = length - 1;int end = i;while(i > 0){if (theFileName[i] == firstPos[0]){end = i;}if (theFileName[i] == '/'){printf("----setup path:%d, i:%d-------\n", end, i);break;}i--;}if (i > 0 && end > i + 1){memcpy((char *)fFileName, ((char*)theFileName + i + 1), end - i - 1);fFileName[end - i - 1] = '\0';sprintf(fFilePath, "%s%s", DEFAULTPATH, fFileName);printf("----fFileName:%s, path:%s-------\n", fFileName, fFilePath);}}MkFileDirandFile();printf("-------  RTPSessionSaveAsMP4,  GetNumStreams=%d \n", inReflectorSession->GetNumStreams());this->InititializeBookmarks( inReflectorSession->GetNumStreams());}RTPSessionSaveOutput::~RTPSessionSaveOutput()
{printf("------- YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY \n");if(ffd != -1){close(ffd);ffd = -1;}::memset(fFileName, 0, sizeof(fFileName));::memset(fFilePath, 0 , sizeof(fFilePath));
}bool RTPSessionSaveOutput::MkFileDirandFile()
{        if(access(fFilePath, R_OK) != 0) // dir exists{int isCreate = mkdir(fFilePath , S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);if( !isCreate )    qtss_printf("create dir ok, %s\n", fFileName);else{qtss_printf("create %s failed! error code : %d\n", fFilePath, isCreate);return false;}}qtss_printf("---------- dir is ok ---------------%s\n", fFileName);    time_t	tmt =  OS::UnixTime_Secs();	 // Seconds since 1970  				char szBuf[128] = {0};        strftime(szBuf, sizeof(szBuf), "%Y-%m-%d_%H_%M_%S", localtime(&tmt));printf(" start0 =  %s\n", szBuf);if(-1 == ffd){sprintf(fFilePath, "%s/%s.m4a", fFilePath, szBuf);printf("-------open file , fFilePath=%s  \n", fFilePath);ffd = open(fFilePath, O_CREAT  | O_WRONLY  | O_APPEND, 0666);if (ffd != -1){//Write_Header();}}return true;
}ReflectorSession* RTPSessionSaveOutput::GetReflectorSession() 
{qtss_printf("################################## RTPSessionSaveAsMP4::GetReflectorSession \n");return fReflectorSession; 
}void RTPSessionSaveOutput::Write_Header()
{if (1){unsigned char header[4]; //  H264 headerheader[0] = 0;header[1] = 0;header[2] = 0;header[3] = 1;write(ffd, header, 4);}else{unsigned char header[6] = "\0"; //  AMR headerheader[0] = 0x23;header[1] = 0x21;header[2] = 0x41;header[3] = 0x4D;header[4] = 0x52;header[5] = 0x0A;write(ffd, header, sizeof(header));}
}//这个方法主要实现了AAC RTP音频流的存储,将存储的文件拷贝出来后,能正常播放
QTSS_Error  RTPSessionSaveOutput::WritePacket(StrPtrLen* inPacket, void* inStreamCookie, UInt32 inFlags, SInt64 packetLatenessInMSec, SInt64* timeToSendThisPacketAgain, UInt64* packetIDPtr, SInt64* arrivalTimeMSecPtr, Bool16 firstPacket)
{if (inPacket == NULL || inPacket->Len < 14){qtss_printf("inPacket == NULL || inPacket->Len < 14");return QTSS_NoErr;}SInt64                  currentTime = OS::Milliseconds();if(-1 != ffd){UInt8* payload = (UInt8*)inPacket->Ptr;int start = 12; //rtp header length//just save aac audio.if (inPacket->Len < 16){return QTSS_NoErr;}if (payload[start] != 0x00 || payload[start + 1] != 0x10){return QTSS_NoErr;}//check end.int rtptype = payload[start] & 0x1f;//int payload_type = payload[1]& 0x7f;bool bStartFlg = ((payload[start+1] & 0x80) == 0x80) ? true : false;//qtss_printf("#################   rtptype  = %d \n", rtptype);#if 0//这里用来做H264文件的存储if (rtptype== 28) { // FU-A//printf("################# rtptype== 28, arrivalTimeMSecPtr=%ld, len =%d\n", *arrivalTimeMSecPtr, inPacket->Len - start -2);if(bStartFlg) {Write_Header();}int ret = write(ffd, (void*)(payload + start + 2), (inPacket->Len - start -2));if(ret == -1){qtss_printf("error=%d, strerror=%s", errno, strerror(errno));}               } else if (rtptype <= 24){// single nal unit// printf("####################### rtptype rtptype =%d, arrivalTimeMSecPtr=%ld, len =%d\n", rtptype ,*arrivalTimeMSecPtr,  (inPacket->Len - start));//Write_Header();int ret = write(ffd, (void*)(payload+start), (inPacket->Len - start));// 00 00 00 01 if(ret == -1){qtss_printf("error=%d, strerror=%s", errno, strerror(errno));}}#elseif (lastArrivalTimeMSec >= *arrivalTimeMSecPtr){return QTSS_NoErr;}qtss_printf("RTPSessionSaveAsMP4::WritePacket lastArrivalTimeMSec=%d\n", lastArrivalTimeMSec);lastArrivalTimeMSec = *arrivalTimeMSecPtr;#if 1        //aac headerunsigned char adtsHeader[7] = {0};adtsHeader[0] = 0xFF;adtsHeader[1] = 0xF1;int profile = 2;int freqIdx = 11;int chanCfg = 1; int packetLen = inPacket->Len - start + 7 - 4;adtsHeader[2] = ((profile -1 )<<6) + (freqIdx << 2) + (chanCfg >> 2);adtsHeader[3] = ((chanCfg & 3) << 6) + (packetLen >> 11);adtsHeader[4] = (packetLen & 0x7ff) >> 3;//(packetLen >> 3) & 0xff;adtsHeader[5] = ((packetLen & 0x7) << 5)|0x1f;adtsHeader[6] = 0xFC;int ret = write(ffd, adtsHeader, sizeof(adtsHeader));
#endif//     //amr header//aaclatm 4headerret = write(ffd, (void*)(payload + start + 4), (inPacket->Len - start - 4));if(ret == -1){qtss_printf("error=%d, strerror=%s", errno, strerror(errno));}fLastIntervalMilliSec = currentTime - fLastPacketTransmitTime;if (fLastIntervalMilliSec > 100) {//reset interval maybe first packet or it has been blocked for awhilefLastIntervalMilliSec = 5;}fLastPacketTransmitTime = currentTime;if (!bLastBlockFlg){bLastBlockFlg = true;}else{bLastBlockFlg = false;}#endif//flush(ffd);}return QTSS_NoErr;
}



在完成文件输出的RTPSessionSaveOutput类后,我们修改QTSSReflectorModule中的,在推流的逻辑中,增加一个OutPut对象到会话中:

    // If this is an incoming data session, skip everything having to do with setting up a new// RTP Stream. if (isPush){//...//这里暂时只存储音频流if(theTrackID == 0) // qtssVideoPayloadType{RTPSessionSaveOutput* theSaveAsMP4 = NEW RTPSessionSaveOutput(theSession, theFullPath.Ptr);	         theSession->AddOutput(theSaveOutput, false); }}


这样就完成了推送音频流的存储,视频流的存储也能这样来实现,如果考虑音视频同时存储,可能要考虑其他方案来实现,比方需要通过ffmpeg来完成声音和视频融合成一个文件。


这篇关于Darwin推流存储实现介绍 之二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/944848

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo