在进行移动端视频直播项目时遇到的问题。手机端在推的流时的是没问题的,主要如今是IOS和安卓连接CRtmpServer后进行播放时checkBW过不了,出现异常:NetConnection.Call.Failed,但连FMSserver时能正常播放,因为用的是VLC的库。是封装好的。确定不了是哪个环节出了问题,下面是安卓与IOS开发报出来的异常截图。

然后,决定分析下CRtmpServer的日志及原码,发如今CrtmpServer的日志中发现了一条警告:Default implementation of ProcessInvokeGeneric: Request: _checkbw。

立即在源代码中找到了抛出该日志的方法

bool BaseRTMPAppProtocolHandler::ProcessInvokeGeneric(BaseRTMPProtocol *pFrom,
Variant & request) {
WARN("Default implementation of ProcessInvokeGeneric: Request: %s",
STR(M_INVOKE_FUNCTION(request)));
Variant response = GenericMessageFactory::GetInvokeCallFailedError(request);
return SendRTMPMessage(pFrom, response);
}

找到引用之处

bool BaseRTMPAppProtocolHandler::ProcessInvoke(BaseRTMPProtocol *pFrom,
Variant &request) {
//PROD_ACCESS(CreateLogEventInvoke(pFrom, request));
string functionName = request[RM_INVOKE][RM_INVOKE_FUNCTION];
uint32_t currentInvokeId = M_INVOKE_ID(request);
if (currentInvokeId != 0) {
if (_nextInvokeId[pFrom->GetId()] <= currentInvokeId) {
_nextInvokeId[pFrom->GetId()] = currentInvokeId + 1;
}
}
if (functionName == RM_INVOKE_FUNCTION_CONNECT) {
return ProcessInvokeConnect(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CREATESTREAM) {
return ProcessInvokeCreateStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PUBLISH) {
return ProcessInvokePublish(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PLAY) {
return ProcessInvokePlay(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PAUSERAW) {
return ProcessInvokePauseRaw(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PAUSE) {
return ProcessInvokePause(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_SEEK) {
return ProcessInvokeSeek(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CLOSESTREAM) {
return ProcessInvokeCloseStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_RELEASESTREAM) {
return ProcessInvokeReleaseStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_DELETESTREAM) {
return ProcessInvokeDeleteStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_RESULT) {
return ProcessInvokeResult(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ERROR) {
return ProcessInvokeResult(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ONSTATUS) {
return ProcessInvokeOnStatus(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_FCPUBLISH) {
return ProcessInvokeFCPublish(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_GETSTREAMLENGTH) {
return ProcessInvokeGetStreamLength(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ONBWDONE) {
return ProcessInvokeOnBWDone(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CHECKBANDWIDTH) {
return ProcessInvokeCheckBandwidth(pFrom, request);
} else {
return ProcessInvokeGeneric(pFrom, request);
}
}

同一时候也将client连接流程进行了梳理

1、client与server连接流程

   Client---->Server,  Command Message,"Connect"

   Client<----Server,  "Window Acknowledgement size"

   Client<----Server,  "Set Peer Bandwidth",这里存在Server发送"onBWDone",client回应"_checkbw"消息,Server回应"_result"

   Client---->Server,  "Window Acknowledgement size"

   Client<----Server,  User Control Message ,"StreamBegin"

   Client<----Server,  Command Message,"_result connect response"

2、client发给server的命令格式: CommandName(String) + TransactionID(Number) + CommandObject(Object,av类型。假设不存在设定为Null) + StreamName(string) + start(number) + Duration(Number) + Reset(Boolean)

   server回应给client命令格式: CommandName(String) + Description(string) 

   Client---->Server,  Command Message,"play"  ,这之后可能夹杂,server发送 "onbwcheck", client回应"_result",

   Client---->Server,  set buffertime,用户事件。设定buffertimeLength= 36000000ms

   Client<----Server,  set chunk size,server设定块大小。

   Client<----Server,  User Control Message ,"StreamIsRecoreded"

   Client<----Server,  User Control Message ,"StreamBegin"   

   Client<----Server,  Command Message,"onStatus,play.reset"

   Client<----Server,  Command Message,"onStatus,play.start"   

   Client<----Server,  发送Command Message "RtmpSampleAccess"

   Client<----Server,  Audio Message  

   Client<----Server,  Video Message

最后将问题定格在: Client---->Server,  Command Message,"play"  ,这之后可能夹杂,server发送 "onbwcheck", client回应"_result",这一环节之上。

对照了日志与源代码的处理逻辑发现,手机端发送的指令中functionName的值为"_checkbw",而源代码处理逻辑中functionName仅仅有为”checkBandwidth“,于是做了一个猜想:

1、_checkbw会不会是checkBandwidth的意思;

2、某些如Flash不须要_checkbw而某些client须要才干继续走下去。

即然怀疑问题出在此处。就决定试一试。在处理逻辑中添加对“_checkbw”的处理,处理逻辑与原有“checkBandwidth”同样,源代码例如以下:

bool BaseRTMPAppProtocolHandler::ProcessInvoke(BaseRTMPProtocol *pFrom,
Variant &request) {
//PROD_ACCESS(CreateLogEventInvoke(pFrom, request));
string functionName = request[RM_INVOKE][RM_INVOKE_FUNCTION];
uint32_t currentInvokeId = M_INVOKE_ID(request);
if (currentInvokeId != 0) {
if (_nextInvokeId[pFrom->GetId()] <= currentInvokeId) {
_nextInvokeId[pFrom->GetId()] = currentInvokeId + 1;
}
}
if (functionName == RM_INVOKE_FUNCTION_CONNECT) {
return ProcessInvokeConnect(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CREATESTREAM) {
return ProcessInvokeCreateStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PUBLISH) {
return ProcessInvokePublish(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PLAY) {
return ProcessInvokePlay(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PAUSERAW) {
return ProcessInvokePauseRaw(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_PAUSE) {
return ProcessInvokePause(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_SEEK) {
return ProcessInvokeSeek(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CLOSESTREAM) {
return ProcessInvokeCloseStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_RELEASESTREAM) {
return ProcessInvokeReleaseStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_DELETESTREAM) {
return ProcessInvokeDeleteStream(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_RESULT) {
return ProcessInvokeResult(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ERROR) {
return ProcessInvokeResult(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ONSTATUS) {
return ProcessInvokeOnStatus(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_FCPUBLISH) {
return ProcessInvokeFCPublish(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_GETSTREAMLENGTH) {
return ProcessInvokeGetStreamLength(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_ONBWDONE) {
return ProcessInvokeOnBWDone(pFrom, request);
} else if (functionName == RM_INVOKE_FUNCTION_CHECKBANDWIDTH) {
return ProcessInvokeCheckBandwidth(pFrom, request);
} else if (functionName == "_checkbw") {
return ProcessInvokeCheckBandwidth(pFrom, request);
} else {
return ProcessInvokeGeneric(pFrom, request);
}
}

改完后上传server,编译,执行,測试。还不成功。继续看日志。发现与之前有所不同,出现了还有一条警告:ProcessInvokeCheckBandwidth:checkBandwidth is disabled.

立即反映过来。原来是配置中没有将checkBandwidth设为true,立刻改动,重新启动后測试。成功。

CrtmpServer支持Android与IOS进行RTMP直播遇到的_checkbw问题的更多相关文章

  1. .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器

    .NET 开源了,Visual Studio 开始支持 Android 和 iOS 程序编写并自带 Android 模拟器 北京时间今天凌晨的 Connect(); 大会上,多少程序员的假想成为现实. ...

  2. IOS JPush 集成步骤(极光远程推送解决方案,支持android和iOS两个平台)

    ●  什么是JPush ●  一套远程推送解决方案,支持android和iOS两个平台 ●  它能够快捷地为iOS App增加推送功能,减少集成APNs需要的工作量.开发复杂 度 ●  更多的信息,可 ...

  3. [RN] React Native 图片保存到相册(支持 Android 和 ios)

    React Native 图片保存到相册(支持 Android 和 ios) 原理: IOS用 RN自带的 CameraRoll, Android 使用 不成功,需要 react-native-fs  ...

  4. 同时支持Android 和 ios 投屏到电脑的软件,Support Android and ios screen shrare to PC - 希沃授课助手

    最近学校由粉笔黑板更换了智慧电子黑板,然后发现了一个好玩的软件. 感谢希沃公司的开发: 希沃授课助手,这是一款同时支持Android 和 ios 投屏和远程控制的. 效果很流畅,非常赞

  5. EasyRTMP实现的一套简单、高效、易用的全平台(Windows/Linux/ARM/Android/iOS)RTMP直播推送库

    本文转自EasyDarwin开源团队成员Kim的博客:http://blog.csdn.net/jinlong0603/article/details/52938980 EasyRTMP介绍 Easy ...

  6. Android、iOS平台RTMP/RTSP播放器实时音量调节

    介绍移动端RTMP.RTSP播放器实时音量调节之前,我们之前也写过,为什么windows播放端加这样的接口,windows端播放器在多窗口大屏显示的场景下尤其需要,尽管我们老早就有了实时静音接口,相对 ...

  7. .NET开源了,Visual Studio开始支持 Android 和 iOS 编程并自带Android模拟器

    北京时间今天凌晨的大会上,多少程序员的假想成为现实..NET 开源,集成 Clang 和 LLVM 并且自带 Android 模拟器,这意味着 Visual Studio 这个当下最好没有之一的 ID ...

  8. 混合开发之DSBridge(同时支持Android和iOS)

    什么是 Javascript bridge 随着h5的不断普及及优化,以及移动端对动态化的需求越来越大,开发者经常需要在app中嵌入一些网页,然后会在web和native之间进行交互,如传递数据,调用 ...

  9. nginx支持android、ios、微信扫一扫

    首先做一个android下载的html页面,页面中识别微信浏览器提示在浏览器中打开,然后在nginx对ios进行识别并跳转到apple store #下载App location ^~ /appDow ...

随机推荐

  1. tiny4412 串口驱动分析三 --- log打印的几个阶段之内核自解压

    作者:彭东林 邮箱:pengdonglin137@163.com 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 ...

  2. ARP/RARP

    IP地址和MAC地址之间的转换   点对点链路不使用ARP   ARP高速缓存   ARP高效运行的关键是每个主机上都有一个ARP高速缓存. 存放了最近IP地址与MAC地址间的映射关系. 每一项生存时 ...

  3. Android ToolBar 使用完全解析

    ToolBar简介 ToolBar是Android 5.0推出的一个新的导航控件用于取代之前的ActionBar,由于其高度的可定制性.灵活性.具有Material Design风格等优点,越来越多的 ...

  4. gcc 4.8更新gcc 4.9 5.4版本等

    转载:http://www.linuxidc.com/Linux/2017-01/139976.htm 如果还在使用较旧版本的Ubuntu,或者是Ubuntu LTS,那么我们是很难体验新版gcc的. ...

  5. 通过CVE-2017-17215学习路由器漏洞分析,从入坑到放弃

    1.基本信息: 2017/11/27,Check Point 软件技术部门报告了一个华为 HG532 产品的远程命令执行漏洞(CVE-2017-17215),Mirai的升级版变种中已经使用该漏洞.看 ...

  6. Linux内核实践之工作队列

    工作队列(work queue)是另外一种将工作推后执行的形式,它和tasklet有所不同.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行.这样,通过工作 ...

  7. java GC optimization, G1GC

    引用    http://www.avricot.com/blog/?post/2010/05/03/Get-started-with-java-JVM-memory-(heap%2C-stack%2 ...

  8. JavaScript取指定时区的时间

    // 北京是getZoneTime(8),纽约是getZoneTime(-5),班加罗尔是getZoneTime(5.5). 偏移值是本时区相对于格林尼治所在时区的时区差值 function getZ ...

  9. 倒计时:CountDownLatch(火箭发射前的准备)读书笔记

     这是一个非常实用的多线程控制工具类,经典的场景就是 火箭发射,在火箭发射前,为了保证万无一失,往往还要进行各项设备,仪器的检查,只有等待所有的检查完毕后,引擎才能点火,      CountDown ...

  10. WebView简单使用

    public class MainActivity extends Activity { WebView webView; @Override protected void onCreate(Bund ...