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

相关文章

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

MySQL更新某个字段拼接固定字符串的实现

《MySQL更新某个字段拼接固定字符串的实现》在MySQL中,我们经常需要对数据库中的某个字段进行更新操作,本文就来介绍一下MySQL更新某个字段拼接固定字符串的实现,感兴趣的可以了解一下... 目录1. 查看字段当前值2. 更新字段拼接固定字符串3. 验证更新结果mysql更新某个字段拼接固定字符串 -

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

《SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能》:本文主要介绍SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的... 目录原理解析1. mysql主从复制(Master-Slave Replication)2. 读写分离3.

Redis实现延迟任务的三种方法详解

《Redis实现延迟任务的三种方法详解》延迟任务(DelayedTask)是指在未来的某个时间点,执行相应的任务,本文为大家整理了三种常见的实现方法,感兴趣的小伙伴可以参考一下... 目录1.前言2.Redis如何实现延迟任务3.代码实现3.1. 过期键通知事件实现3.2. 使用ZSet实现延迟任务3.3

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in