1,最近做MP3推流中发现一拔掉网线整个程序就闪退,纠结其原因是librtmp 中 在rtmp.c中
WriteN(RTMP *r, const char *buffer, int n) 方法中
nBytes 为-1 时 会关掉socket 但是网线拔了关不掉 librtmp 会一直循环发送 直到程序崩溃为止
WriteN(RTMP *r, const char *buffer, int n) {
const char *ptr = buffer;
#ifdef CRYPTO
char *encrypted = 0;
char buf[RTMP_BUFFER_CACHE_SIZE];
if (r->link.rc4keyOut)
{
if (n > sizeof(buf))
encrypted = (char *)malloc(n);
else
encrypted = (char *)buf;
ptr = encrypted;
RC4_encrypt2(r->link.rc4keyOut, n, buffer, ptr);
}
#endif
while (n > 0) {
int nBytes;
if (r->link.protocol & RTMP_FEATURE_HTTP)
nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n);
else
nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n);
//当突然拔掉网线nBytes 为-1会执行RTMP_Close
if (nBytes < 0) {
int sockerr = GetSockError();
RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
sockerr, n);
if (sockerr == EINTR && !RTMP_ctrlC)
continue;
RTMP_Close(r);//前去关闭
n = 1;
break;
}
if (nBytes == 0)
break;
n -= nBytes;
ptr += nBytes;
}
#ifdef CRYPTO
if (encrypted && encrypted != buf)
free(encrypted);
#endif
return n == 0;
}
2,在RTMP_Close中执行SendFCUnpublish(r)
if (RTMP_IsConnected(r)) {
if (r->m_stream_id > 0) {
if ((r->link.protocol & RTMP_FEATURE_WRITE)) {
SendFCUnpublish(r);//拔掉网线会走这儿
}
__android_log_print(ANDROID_LOG_INFO, "RTMP", "RTMP_Closer-77777>start%d",
r->m_sb.sb_socket);
i = r->m_stream_id;
r->m_stream_id = 0;
SendDeleteStream(r, i);
}
if (r->m_clientID.av_val) {
HTTP_Post(r, RTMPT_CLOSE, "", 1);
free(r->m_clientID.av_val);
r->m_clientID.av_val = NULL;
r->m_clientID.av_len = 0;
}
RTMPSockBuf_Close(&r->m_sb);
}
3,在SendFCUnpublish(RTMP *r) 又会去执行 函数RTMP_SendPacket(r, &packet, FALSE)
又去执行RTMP_SendPacket 然后又执行WriteN(RTMP *r, const char *buffer, int n) 造成无限死循环
static int
SendFCUnpublish(RTMP *r) {
RTMPPacket packet;
char pbuf[1024], *pend = pbuf + sizeof(pbuf);
char *enc;
packet.m_nChannel = 0x03;
packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet.m_packetType = 0x14;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_FCUnpublish);
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &r->link.playpath);
if (!enc)
return FALSE;
packet.m_nBodySize = enc - packet.m_body;
return RTMP_SendPacket(r, &packet, FALSE);//又去执行RTMP_SendPacket 然后又执行WriteN(RTMP *r, const char *buffer, int n) 造成无限死循环
}
贴出来是希望做Android的同学 遇到此类问题有解决方案
以下是我的解决方案 ,以为本人做ndk这块写C是个二把刀 有点暴力 但是在自己的C++的代码中要执行RTMP_Close 函数 以下都是在rtmp.c 文件中添加
1,在rtmp.c
static int send_flg = 0;//自己写的
2,在rtmp.c 链接函数中 RTMP_ConnectStream 添加 send_flg = 0; 方便二次推流 能用
int
RTMP_ConnectStream(RTMP *r, int seekTime) {
RTMPPacket packet = {0};
send_flg = 0;//自己写的
if (seekTime > 0)
r->link.seekTime = seekTime;
r->m_mediaChannel = 0;
while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet)) {
if (RTMPPacket_IsReady(&packet)) {
if (!packet.m_nBodySize)
continue;
if ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) ||
(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) ||
(packet.m_packetType == RTMP_PACKET_TYPE_INFO)) {
RTMP_Log(RTMP_LOGWARNING, "Received FLV packet before play()! Ignoring.");
RTMPPacket_Free(&packet);
continue;
}
RTMP_ClientPacket(r, &packet);
RTMPPacket_Free(&packet);
}
}
return r->m_bPlaying;
}
3,在rtmp.c中的WriteN(RTMP *r, const char *buffer, int n) 函数中修改 我知道我自己写代码if有点脱了裤子放屁 多次一举 我就懒得改
static int
WriteN(RTMP *r, const char *buffer, int n) {
const char *ptr = buffer;
#ifdef CRYPTO
char *encrypted = 0;
char buf[RTMP_BUFFER_CACHE_SIZE];
if (r->link.rc4keyOut)
{
if (n > sizeof(buf))
encrypted = (char *)malloc(n);
else
encrypted = (char *)buf;
ptr = encrypted;
RC4_encrypt2(r->link.rc4keyOut, n, buffer, ptr);
}
#endif
while (n > 0) {
int nBytes;
if (r->link.protocol & RTMP_FEATURE_HTTP)
nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n);
else
nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n);
__android_log_print(ANDROID_LOG_INFO, "RTMP", "nBytes = RTMPSockBuf_Send result:%d: %d",
nBytes, send_flg);
if (nBytes < 0) {
int sockerr = GetSockError();
// RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
// sockerr, n);
if (sockerr == EINTR && !RTMP_ctrlC) {
continue;
}
__android_log_print(ANDROID_LOG_INFO, "RTMP", "nBytes<0 不是的 continue:");
if (nBytes < 0) {//自己写的 真正解决问题
send_flg = 1;
n = 1;
break;
}
RTMP_Close(r);
n = 1;
break;
}
if (nBytes == 0)
break;
n -= nBytes;
ptr += nBytes;
}
#ifdef CRYPTO
if (encrypted && encrypted != buf)
free(encrypted);
#endif
return n == 0;
}
4.在rtmp.c中 添加 别忘了在头文件件中(rtmp.h)声明
int RTM_GetSendflg(void) {
return send_flg;
}
5.在自己写的C++调用代码地方修改成
// 7. 将 RTMP 数据包发送到服务器中
// __android_log_print(ANDROID_LOG_INFO, "RTMP", "net is valid %d",netWorkIsValid);
//if(RTMP_IsConnected(rtmp)&&netWorkIsValid==1) { 这个根本没用 里面死循环 这些都是正常的 所以没用
if (RTM_GetSendflg() == 0) {
__android_log_print(ANDROID_LOG_INFO, "RTMP", "RTMP_IsConnected%d: %d",
rtmp->m_sb.sb_socket, RTM_GetSendflg());
ret = RTMP_SendPacket(rtmp, packet, 1);
} else {
break;//跳出推流
}
6 在跳出推流后 记得手动调用 RTMP_Close 因为我们把里面的自动死循环出错的的干掉了
// 8. 推流结束, 关闭与 RTMP 服务器连接, 释放资源
if (rtmp != NULL) {
RTMP_Close(rtmp);
RTMP_Free(rtmp);
}
__android_log_print(ANDROID_LOG_INFO, "RTMP", "===========push finish rtmp");
// 推流数据包 线程安全队列释放
// 防止中途退出导致没有释放资源, 造成内存泄漏
if (packet) {
RTMPPacket_Free(packet);
delete packet;
packet = 0;
}
__android_log_print(ANDROID_LOG_INFO, "RTMP", "===========push finish packet");
// 释放推流地址
if (pushPath) {
delete pushPath;
pushPath = 0;
}



