栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

关于Android librtmp 推流突然拔掉网线整个应用程序crash的错误

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于Android librtmp 推流突然拔掉网线整个应用程序crash的错误

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;
    }

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/274899.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号