热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

流媒体学习笔记2(live555中的Session)

毕业课题打算从最原始的地方做起。好吧,那就从live555采集转发本地摄像头视频开始吧。首先从源码开始吧,今天看了点liveMedia中的session,这里做个总结。整个源码中的继承顺序

毕业课题打算从最原始的地方做起。好吧,那就从live555采集转发本地摄像头视频开始吧。首先从源码开始吧,今天看了点liveMedia中的session,这里做个总结。

整个源码中的继承顺序为

H264VideoFileServerMediaSubsession::FileServerMediaSubsession::                                                                       OnDemandServerMediaSubsession::ServerMediaSubsession ::Medium

ServerMediaSession ::Medium

Medium:抽象了基本的接口,包括环境,task和媒体名和媒体查找函数以及一些辅助函数,几乎所有得处理单元都继承自Medium类。

ServerMediaSession:添加了子会话链表,SDP(Session Description Protocol 会话协议)描述以及一些媒体相关处理函数。其中静态成员函数lookupByName可以从session链表中找到对应的媒体

ServerMediaSubsession:定义了指向ServerMediaSession的父指针,指向下个一个对象的指针。该媒体的SDP信息,该媒体的读取定位函数等。

ServerMediaSubsessionIterator:一个迭代器类,用来遍历subsession链表。

程序为容器中的每一个流建立一个subseesion,然后通过 ServerMediaSession::addSubsession 函数,将subsession 加入到ServerMediaSession。具体代码如下

ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
  if (subsession->fParentSession != NULL) return False; // it's already used

  if (fSubsessiOnsTail== NULL) {
    fSubsessiOnsHead= subsession;
  } else {
    fSubsessionsTail->fNext = subsession;
  }
  fSubsessiOnsTail= subsession;

  subsession->fParentSession = this;
  subsession->fTrackNumber = ++fSubsessionCounter;
  return True;
}

ServerMediaSession与流名字所指定与文件是没有关系的,也就是说它不会操作文件,而文件的操作是放在 ServerMediaSubsession中的。具体应该是在ServerMediaSubsession的sdpLines()函数中打开。sdpLines()是在派生类OnDemandServerMediaSubsession中实现,代码如下

char const*
OnDemandServerMediaSubsession::sdpLines() {
  if (fSDPLines == NULL) {
    // We need to construct a set of SDP lines that describe this
    // subsession (as a unicast stream).  To do so, we first create
    // dummy (unused) source and "RTPSink" objects,
    // whose parameters we use for the SDP lines:

	//构建一套SDP行来描述这个Subsession。创建未使用的source和RTPSink对象
    unsigned estBitrate;
    FramedSource* inputSource = createNewStreamSource(0, estBitrate);
    if (inputSource == NULL) return NULL; // file not found

    struct in_addr dummyAddr;
    dummyAddr.s_addr = 0;
    Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0);
    unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
    RTPSink* dummyRTPSink
      = createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource);
	//Sink中的到SDP
    setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
    Medium::close(dummyRTPSink);
    closeStreamSource(inputSource);
  }

  return fSDPLines;
}

OnDemandServerMediaSubsession类继承 ServerMediaSubsession类,OnDemand实际上用来描述需求,Subsession表示只是一种类型媒体的会话。所以可以通过继承OnDemandServerMediaSubsession来实现不同类型的媒体转发,比如OnDemandServerMediaSubsession的两个子类

1.ProxyServerMediaSubsession:用于实现一个单播RTSP的服务器代理另一个RTSP流

2.FileServerMediaSubsession:虚基类,不同类型媒体通过继承它来实现对于需求,比如音频,视频,比如h263,h264编解码。FileServerMediaSubsession类只是添加了两个成员变量 fFileName,fFileSize用来描述转发的媒体。构造函数如下:

FileServerMediaSubsession
::FileServerMediaSubsession(UsageEnvironment& env, char const* fileName,
			    Boolean reuseFirstSource)
  : OnDemandServerMediaSubsession(env, reuseFirstSource),
    fFileSize(0) {
  fFileName = strDup(fileName);
}

而H264VideoFileServerMediaSubsession类继承了FileServerMediaSubsession,它将构造函数放入protected中,并用静态的成员方法createNew来实例对象,这是设计模式的工厂模式。createNew(env, fileName, reuseSource)是每一种类型的流媒体自己的subsession实现。如果 reuseSource == True,则多个客户端共享 StreamState 对象,也即意味着多个客户端播放同一流文件时,服务器端使用同一FileSource 对象对该流文件进行数据读取操作,使用同一RTPSink 对象对流数据进行封装发送操作,这时,多个客户端播放的内容就是同步的,这适合于实时数据播放;如果reuseSource==False,则对每个客户端,服务器端单独维护一个StreamState 对象进行播放操作,此时,多个客户端播放的内容就是独立的,这适合于数据回放。

当然,单看session看不出什么门道来,那看下面

SDP消息组装过程:
      ServerMediaSession负责产生会话公共描述信息,子会话描述由H264VideoFileServerMediaSubsession产生。 H264VideoFileServerMediaSubsession在其父类成员函数OnDemandServerMediaSubsession::sdpLines()中生成会话描述信息。在sdpLines()实现里面,创建一个FramedSource(具体实现类为H264VideoStreamFramer)和RTPSink(具体实现类为H264VideoRTPSink),最后调用OnDemandServerMediaSubsession::setSDPLinesFromRTPSink(...)成员函数生成子会话描述。

好吧,这样思路基本清楚了点,以后要写自己需要的编解码类,那就继承OnDemandServerMediaSubsession吧。对于souce-sink 机制下次讲了....


推荐阅读
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文介绍了Python函数的定义与调用的方法,以及函数的作用,包括增强代码的可读性和重用性。文章详细解释了函数的定义与调用的语法和规则,以及函数的参数和返回值的用法。同时,还介绍了函数返回值的多种情况和多个值的返回方式。通过学习本文,读者可以更好地理解和使用Python函数,提高代码的可读性和重用性。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文主要介绍了gym102222KVertex Covers(高维前缀和,meet in the middle)相关的知识,包括题意、思路和解题代码。题目给定一张n点m边的图,点带点权,定义点覆盖的权值为点权之积,要求所有点覆盖的权值之和膜qn小于等于36。文章详细介绍了解题思路,通过将图分成两个点数接近的点集L和R,并分别枚举子集S和T,判断S和T能否覆盖所有内部的边。文章还提到了使用位运算加速判断覆盖和推导T'的方法。最后给出了解题的代码。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
author-avatar
手机用户2502890731
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有