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

相关文章

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南

《SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南》本文将基于开源项目springboot-easyexcel-batch进行解析与扩展,手把手教大家如何在SpringBo... 目录项目结构概览核心依赖百万级导出实战场景核心代码效果百万级导入实战场景监听器和Service(核心

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

SpringBoot实现RSA+AES自动接口解密的实战指南

《SpringBoot实现RSA+AES自动接口解密的实战指南》在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题,RSA+AES混合加密方案因其安全性高、性能优越而被广泛采用,本... 目录一、项目依赖与环境准备1.1 Maven依赖配置1.2 密钥生成与配置二、加密工具类实现2.1

在Java中实现线程之间的数据共享的几种方式总结

《在Java中实现线程之间的数据共享的几种方式总结》在Java中实现线程间数据共享是并发编程的核心需求,但需要谨慎处理同步问题以避免竞态条件,本文通过代码示例给大家介绍了几种主要实现方式及其最佳实践,... 目录1. 共享变量与同步机制2. 轻量级通信机制3. 线程安全容器4. 线程局部变量(ThreadL

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布