背景:

live555作为知名的流媒体开源框架,在实际项目中,经常使用到。在Android播放器中,可以使用其作为流媒体部分的拉流端,特别是对于RTSP及组播播放,live555相对还是很稳定的。
这次将其移植到Android SDK上,并完成RTSP及组播拉流小程序,权当玩乐及熟悉live555之用。
RTSP拉流小程序基本就是原来live555测试代码testRTSPClient.cpp,仅对其做了点小修改,让其能完成对电视节目RTSP流的获取,所以后面有机会再讲live555 RTSP内部实现流程吧。
这次就讲Android上移植live555及实现组播简单拉流代码。

Android移植live555

live555 Android平台移植方法CSDN上很多可以参考,并不难,我简单说下我的移植方法

首先官网下载live555源码 http://www.live555.com/liveMedia/public/
对于Android,可以删除不必要的目录,我仅保留如下目录:

编写Android.mk文件,如下,
将liveMedia目录中的CPP和C代码全部包含进来,另外其他几个目录需要用到的文件也添加进来。
指定LOCAL_CPPFLAGS 和LOCAL_LDFLAGS ,主要是对STL的支持。
这样编译即可,会在live555源码out目录下生成liblive555.so库。
编译过程中可能有些小错误,不同live555的错误可能略有不同,主要是对NDK的兼容问题,网上查查修改即可,比较简单。

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := liblive555
LOCAL_MODULE_PATH := $(LOCAL_PATH)/outLOCAL_C_INCLUDES := \$(LOCAL_PATH) \$(LOCAL_PATH)/BasicUsageEnvironment/include \$(LOCAL_PATH)/BasicUsageEnvironment \$(LOCAL_PATH)/UsageEnvironment/include \$(LOCAL_PATH)/UsageEnvironment \$(LOCAL_PATH)/groupsock/include \$(LOCAL_PATH)/groupsock \$(LOCAL_PATH)/liveMedia/include \$(LOCAL_PATH)/liveMedia \LOCAL_MODULE_TAGS := optionalprebuilt_stdcxx_PATH := prebuilts/ndk/current/sources/cxx-stl/gnu-libstdc++
SRC_LIST := $(wildcard $(LOCAL_PATH)/liveMedia/*.cpp)
SRC_LIST += $(wildcard $(LOCAL_PATH)/liveMedia/*.c)LOCAL_SRC_FILES := $(SRC_LIST:$(LOCAL_PATH)/%=%)LOCAL_SRC_FILES += \groupsock/GroupsockHelper.cpp \groupsock/GroupEId.cpp \groupsock/inet.c \groupsock/Groupsock.cpp \groupsock/NetInterface.cpp \groupsock/NetAddress.cpp \groupsock/IOHandlers.cpp \UsageEnvironment/UsageEnvironment.cpp \UsageEnvironment/HashTable.cpp \UsageEnvironment/strDup.cpp \BasicUsageEnvironment/BasicUsageEnvironment0.cpp \BasicUsageEnvironment/BasicUsageEnvironment.cpp \BasicUsageEnvironment/BasicTaskScheduler0.cpp \BasicUsageEnvironment/BasicTaskScheduler.cpp \BasicUsageEnvironment/DelayQueue.cpp \BasicUsageEnvironment/BasicHashTable.cpp \LOCAL_LDLIBS :=  -lm -llogLOCAL_CPPFLAGS := -fexceptions -DXLOCALE_NOT_USED=1 -DNULL=0 -DNO_SSTREAM=1 -UIP_ADD_SOURCE_MEMBERSHIP
LOCAL_LDFLAGS := -L$(prebuilt_stdcxx_PATH)/libs/$(TARGET_CPU_ABI) -lgnustl_static -lsupc++include $(BUILD_SHARED_LIBRARY)

组播拉流程序

代码参考测试代码testMPEG2TransportReceiver.cpp实现。
1、
Source 和 Sink 在live555中是两个非常重要的概念。
Source 发送端,流的起点, 可直观理解为生产者,负责读取文件或网络流的信息。
Sink 接收端, 流的终点, 可理解为是消费者。
可以又多级source,对上级source在进行处理,也可以成为filter(实际上也是source),数据流向简单来说如下:

2、
Source 对于RTP封装的组播和UDP组播裸流传递的有所不同。
有RTP封装的情况下,创建SimpleRTPSource;
UDP裸流传递的情况下,则是创建BasicUDPSource;
同时创建MPEG2TransportStreamFramer这个filter来处理从source接受到TS流。当然也可以不用这个filter,testMPEG2TransportReceiver.cpp中就没有使用,经过MPEG2TransportStreamFramer可以对TS进行一些解析和处理等。
代码如下:

int main(int argc, char** argv) {if (argc < 2) {return 1;}char ipStr[64] = "";unsigned short portNum = 0;Boolean isRTP = (strstr(argv[1],"rtp://") != 0)? True:False;if(isRTP){sscanf(argv[1],"rtp://%[^:]:%hu",ipStr,&portNum);}else{sscanf(argv[1],"udp://%[^:]:%hu",ipStr,&portNum);}if(portNum == 0 || ipStr[0] == 0){return 1;   }TaskScheduler* scheduler = BasicTaskScheduler::createNew();env = BasicUsageEnvironment::createNew(*scheduler);struct in_addr sessionAddress;sessionAddress.s_addr = our_inet_addr(ipStr);const unsigned char ttl = 1; // low, in case routers don't admin scope//RTP & UDP Socketconst Port port(portNum);Groupsock inputsock(*env, sessionAddress, port, ttl);//RTCP Socketconst Port rtcpPort(portNum+1);Groupsock rtcpGroupsock(*env, sessionAddress, rtcpPort, ttl);#if 1
if(isRTP){//RTP// Create the data source: a "MPEG-2 TransportStream RTP source" (which uses a 'simple' RTP payload format):sessionState.udpSource = NULL;sessionState.rtpSource = SimpleRTPSource::createNew(*env, &inputsock, 33, 90000, "video/MP2T", 0, False /*no 'M' bit*/);sessionState.readSource= MPEG2TransportStreamFramer::createNew(*env, sessionState.rtpSource);if(sessionState.readSource == NULL){*env << "create source error...\n";return 1;}// Create (and start) a 'RTCP instance' for the RTP source:const unsigned estimatedSessionBandwidth = 5000; // in kbps; for RTCP b/w shareconst unsigned maxCNAMElen = 100;unsigned char CNAME[maxCNAMElen+1];gethostname((char*)CNAME, maxCNAMElen);CNAME[maxCNAMElen] = '\0'; // just in casesessionState.rtcpInstance= RTCPInstance::createNew(*env, &rtcpGroupsock,estimatedSessionBandwidth, CNAME,NULL /* we're a client */, sessionState.rtpSource);// Note: This starts RTCP running automatically}else{//UDP // Create the data source: a "MPEG-2 TransportStream udp source"sessionState.rtpSource = NULL;sessionState.udpSource = BasicUDPSource::createNew(*env, &inputsock);sessionState.readSource = MPEG2TransportStreamFramer::createNew(*env, sessionState.udpSource);if(sessionState.readSource == NULL){*env << "create source error...\n";return 1;}}
......省略
}

3、
Sink 实现如下:
igmpSink继承MediaSink ,每次收取一帧数据,会调用到afterGettingFrame,通过continuePlaying又会处理获取下一帧数据,从而成为一个循环。所以在afterGettingFrame将流数据dump到文件mDumpFile之中。

// igmpSink //
class igmpSink: public MediaSink {
public:// "bufferSize" should be at least as large as the largest expected input frame.static igmpSink* createNew(UsageEnvironment& env, char const* fileName,unsigned bufferSize = 20000);protected:igmpSink(UsageEnvironment& env, FILE* fid, unsigned bufferSize);virtual ~igmpSink();protected: // redefined virtual functions:virtual Boolean continuePlaying();protected:static void afterGettingFrame(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds);virtual void afterGettingFrame(unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime);FILE* mDumpFile;unsigned char* fBuffer;unsigned fBufferSize;
};// igmpSink //
igmpSink::igmpSink(UsageEnvironment& env, FILE* fid, unsigned bufferSize): MediaSink(env), mDumpFile(fid), fBufferSize(bufferSize){fBuffer = new unsigned char[bufferSize];
}igmpSink::~igmpSink() {delete[] fBuffer;if (mDumpFile != NULL) fclose(mDumpFile);
}igmpSink* igmpSink::createNew(UsageEnvironment& env, char const* fileName, unsigned bufferSize) {FILE* fid;fid = fopen(fileName, "wb");if (fid == NULL) return NULL;return new igmpSink(env, fid, bufferSize);
}Boolean igmpSink::continuePlaying() {if (fSource == NULL) return False;fSource->getNextFrame(fBuffer, fBufferSize,afterGettingFrame, this,onSourceClosure, this);return True;
}void igmpSink::afterGettingFrame(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned /*durationInMicroseconds*/) {igmpSink* sink = (igmpSink*)clientData;sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}void igmpSink::afterGettingFrame(unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime) {if (numTruncatedBytes > 0) {envir() << "igmpSink::afterGettingFrame(): The input frame data was too large for our buffer size ("<< fBufferSize << ").  "<< numTruncatedBytes << " bytes of trailing data was dropped!  Correct this by increasing the \"bufferSize\" parameter in the \"createNew()\" call to at least "<< fBufferSize + numTruncatedBytes << "\n";}//envir() << "afterGettingFrame, Write to  file\n";if (mDumpFile != NULL && fBuffer != NULL) {fwrite(fBuffer, 1, frameSize, mDumpFile);}if (mDumpFile == NULL || fflush(mDumpFile) == EOF) {// The output file has closed.  Handle this the same way as if the input source had closed:if (fSource != NULL) fSource->stopGettingFrames();onSourceClosure();return;}// Then try getting the next frame:continuePlaying();
}

4、
最后只要将source和sink绑定,启动即可。
数据流为:udpSource/ rtpSource–>readSource(MPEG2TransportStreamFramer)–>igmpSink。
env->taskScheduler().doEventLoop(); 在live555内部会通过其SingleStep一直循环。(以后分析RTSP流程时,再分析live555内部流程。)组播流会被dump到指定文件。

  sessionState.sink = igmpSink::createNew(*env, "/storage/external_storage/sda1/testIGMP.ts", 5120);// Finally, start receiving the multicast stream:*env << "Beginning receiving multicast stream...\n";sessionState.sink->startPlaying(*sessionState.readSource, afterPlaying, NULL);env->taskScheduler().doEventLoop(); // does not return

5、
测试时可以使用VLC推流,RTP和UDP均可,可以看到流会被dump到我们指定的目录,拿出来播放,看是否一样即可。

Android live555组播拉流客户端相关推荐

  1. live555 android编译,Android live555组播拉流客户端

    背景: live555作为知名的流媒体开源框架,在实际项目中,经常使用到.在Android播放器中,可以使用其作为流媒体部分的拉流端,特别是对于RTSP及组播播放,live555相对还是很稳定的. 这 ...

  2. Android 直播 直播测试拉流播放器和地址

    Android 直播 直播测试拉流播放器和地址 直播拉流播放器 直播拉流测试地址 测试类抖音视频 直播拉流播放器 推荐VLC 直播拉流测试地址 香港财经 *****:rtmp://202.69.69. ...

  3. Linux环境下服务器利用组播来获取客户端IP

    Linux环境下服务器利用组播来获取客户端IP 单播是两个主机之间端对端通信(比如TCP.UDP通信),而广播用于一个主机对整个局域网中所有主机的通信.单播和广播是两个极端,要么对一个主机通信,要么对 ...

  4. android multicast(组播)发送端和接收端源代码,android multicast 多播(组播)问题

    有谁遇到过同样问题的可以探讨下,或者已经解决问题的,能够指导下我 获取组播锁 private  InetAddress   group; WifiManager  wm=(WifiManager)ge ...

  5. android wifi 组播,在Android上显示实时UDP或RTP流(多播)

    我已经尝试从播放器(Daroon播放器,PlayStore)读取它,它运行良好,所以我认为我的愚蠢问题不是由于广播 . 我看到可以通过不同的方式向用户显示视频内容: 在ACTION_VIEW中使用新的 ...

  6. java 获取组播地址_UDP客户端组播地址

    我正在开发一个在Nios处理器上使用LWIP发送UDP数据包的系统 . 我开发了一个C#应用程序,以允许可视化接收的数据 . 我遇到的问题是在发送到多播地址时接收C#应用程序上的数据 . 在运行C#应 ...

  7. CCIE知识点总结——组播

    1.组播概述 (1)组播解决了网络中用户数量不确定的问题,一份信息,多个接收者,相同的组播数据流在每一条链路上最多仅有一份.示意图如下. (2)组播的优势与劣势 优势:减轻服务器和CPU负荷,减少了链 ...

  8. 组播简介(二层组播)

    1 承载网组播模型 2 二层组播技术 2.1 组管理协议 Internet 组管理协议称为IGMP协议(Internet Group Management Protocol),是因特网协议家族中的一个 ...

  9. python socket编程 ws2tcpip_Python3组播通信编程实现教程(发送者+接收者)

    一.说明 1.1 标准组播解释 通信分为单播.多播(即组播).广播三种方式 单播指发送者发送之后,IP数据包被路由器发往目的IP指定的唯一一台设备的通信形式,比如你现在与web服务器通信就是单播形式 ...

最新文章

  1. Linus采访对Linux对git和对代码品味的理解
  2. Golang 笔记 1 基础、基本数据类型
  3. LeetCode Insert Delete GetRandom O(1)
  4. php对象的三大特征,关于php中面向对象的三大特征(封装/继承/多态)
  5. boost::sort模块实现spreadsort 字符串函子排序示例
  6. leetcode -39组合总数
  7. C++ union 公共体
  8. 一个计算两个日期间隔的算法
  9. 【STM32】各类通信接口及协议简识(IIC、SPI、RS232、RS485、CAN、USB)
  10. bzoj 1019: [SHOI2008]汉诺塔(打表)
  11. 《BUG创造队》第九次团队作业:Beta冲刺与验收准备
  12. qt超强精美绘图控件 - QCustomPlot一览
  13. android 极光推送1011,极光推送 · OpenKit使用手册 · 看云
  14. Python导入Excel名单实现随机抽取
  15. 26. 平衡二叉排序树
  16. # 慢东周记(第 1 期):【本周话题】真正的投资者应该是寻找到长期、稳定、持续的正期望收益机会且重仓
  17. 手机玩exe游戏的模拟器_安卓游戏模拟器App | 手机玩红警手机玩暗黑破坏神手机玩魔兽手机玩星际手机玩……...
  18. Aspose.Slides使用教程:使用 C# 在 PowerPoint 演示文稿中添加页眉和页脚
  19. 先卸载 nvidia-387.26驱动,再安装nvidia-384.81 驱动
  20. 华为荣耀8电信卡显示无服务器,华为荣耀手机实现双电信卡双待双通,5步告诉你真相...

热门文章

  1. myisam引擎详解
  2. 互联网产品设计第一步,账户设计之个人实名认证
  3. 2023计算机毕业设计SSM最新选题之java公租房管理系统a6gjx
  4. html5制作风车旋转,HTML5 Canvas 旋转风车绘制
  5. try_catch和__try_except区别
  6. NFT平台开发:为希望发财致富的数字藏品爱好者提供机会
  7. spring的冷门知识
  8. 在 css 中什么是好的注释?
  9. 关于Win10系统无法安装.NET Framework 3.5(包括.NET 2.0 和 3.0) 关于 错误代码:0x800F0950
  10. 一个微云计算环境的搭建过程