rtmpdump4Android,RTMP在Android平台上的使用

由于工作中需要用到rtmp推流,参考了其他的一些博文,自己摸索着使用rtmpdump库,在此记录一下。

实现的功能

  1. 发送flv文件至流媒体服务器(读文件);
  2. 发送flv数据至流媒体服务器(编码封装后的一帧一帧的数据)。

准备工作

  • RTMPDump直接到官网下载
  • 实现MediaCodec进行H264硬编码,然后将编码后的数据封装到flv文件中

实现逻辑

  • Android.mk 文件编写
# This is the Android makefile for libyuv for both platform and NDK.
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_CPP_EXTENSION := .cc

LOCAL_SRC_FILES := \
    librtmp/amf.c           \
    librtmp/hashswf.c    \
    librtmp/log.c           \
    librtmp/parseurl.c      \
    librtmp/rtmp.c

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_MODULE := librtmp
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := rtmp-push
LOCAL_SRC_FILES := RtmpHandle.cpp  RtmpUtil.cpp
LOCAL_STATIC_LIBRARIES := librtmp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
  • Application.mk文件编写
#对标准C++ stl库的支持
#APP_STL := gnustl_static stlport_static
APP_STL := stlport_static
APP_ABI := armeabi-v7a x86 arm64-v8a
APP_PLATFORM := android-9
APP_CPPFLAGS := -std=c++14 -fexceptions
APP_CFLAGS := -DSTDC_HEADERS -DNO_CRYPTO
  • 编写RtmpHandle操作对象
/**
 * Desc :  RTMPDump调用类
 */
public class RtmpHandle {
    private static RtmpHandle mInstance;
    private RtmpHandle() {}
    public synchronized static RtmpHandle getInstance() {
        if (mInstance == null) {
            mInstance = new RtmpHandle();
        }
        return mInstance;
    }

    static {
        System.loadLibrary("rtmp");
        System.loadLibrary("rtmp-push");
    }

    public native long initRtmpHandle(String url);
    public native int pushFlvData(long rtmpHander,byte[] buf, int length);
    public native int pushFlvFile(String url, String filePath);
    public native void releaseRtmpHandle(long rtmpHander);
}
  • 推送帧数据
int RtmpUtils::pushFlvData(unsigned char *buf, jint length) {
    // TODO
    if (length < 15) {
        return -1;
    }
    //packet attributes
    uint32_t type = 0;
    uint32_t dataLength = 0;
    uint32_t timeStamp = 0;
    uint32_t streamId = 0;

    memcpy(&type, buf, 1);
    buf++;
    memcpy(&dataLength, buf, 3);
    dataLength = HTON24(dataLength);
    buf += 3;
    memcpy(&timeStamp, buf, 4);
    timeStamp = HTONTIME(timeStamp);
    buf += 4;
    memcpy(&streamId, buf, 3);
    streamId = HTON24(streamId);
    buf += 3;
    if(mCount % 50 == 0) {
        mCount = 0;
        __android_log_print(ANDROID_LOG_WARN, "WTOE PUSH_RTMP","解析包数据:%u,%u,%u,%u,%d",type, dataLength, timeStamp, streamId, length);
    }

    mCount++;

    if (type != 0x08 && type != 0x09) {
        return -2;
    }

    if (dataLength != (length - 11 - 4)) {
        return -3;
    }

    memcpy(packet->m_body, buf, dataLength+11+4);

    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nTimeStamp = timeStamp;
    packet->m_packetType = type;
    packet->m_nBodySize = dataLength;
    if (!RTMP_IsConnected(rtmp)) {
        RTMP_Log(RTMP_LOGERROR, "rtmp is not connect\n");
        return -4;
    }
    if (!RTMP_SendPacket(rtmp, packet, 0)) {
        RTMP_Log(RTMP_LOGERROR, "Send Error\n");
        return -5;
    }
    return 0;
}
  • 推送文件
/*
 * ===========================================================================
 *      使用RTMPDump进行Flv文件推流
 * ===========================================================================
 */
int RtmpUtils::pushFlvFile(const char *url, const char *path) {
    logw("pushFlvFile");
    RTMP *rtmp = NULL;
    RTMPPacket *packet = NULL;
    uint32_t start_time = 0;
    //the timestamp of the previous frame
    long pre_frame_time = 0;
    int bNextIsKey = 1;
    uint32_t preTagsize = 0;
    //packet attributes
    uint32_t type = 0;
    uint32_t datalength = 0;
    uint32_t timestamp = 0;
    uint32_t streamid = 0;

    FILE *fp = NULL;
    fp = fopen(path, "rb");
    if (!fp) {
        RTMP_LogPrintf("Open File Error.\n");
        loge("Open File Error.\n");
        return -1;
    }

    rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);
    //set connection timeout,default 30s
    rtmp->Link.timeout = 5;
    if (!RTMP_SetupURL(rtmp, const_cast<char *>(url))) {
        RTMP_Log(RTMP_LOGERROR, "SetupURL Error\n");
        loge("SetupURL Error.\n");
        RTMP_Free(rtmp);
        return -1;
    }

    //if unable,the AMF command would be 'play' instead of 'publish'
    RTMP_EnableWrite(rtmp);

    if (!RTMP_Connect(rtmp, NULL)) {
        RTMP_Log(RTMP_LOGERROR, "Connect Error\n");
        loge("Connect Error.\n");
        RTMP_Free(rtmp);
        return -1;
    }

    if (!RTMP_ConnectStream(rtmp, 0)) {
        RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
        loge("ConnectStream Error.\n");
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        return -1;
    }

    packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
    RTMPPacket_Alloc(packet, 1024 * 64);
    RTMPPacket_Reset(packet);

    packet->m_hasAbsTimestamp = 0;
    packet->m_nChannel = 0x04;
    packet->m_nInfoField2 = rtmp->m_stream_id;

    RTMP_LogPrintf("Start to send data ...\n");

    //jump over FLV Header
    fseek(fp, 9, SEEK_SET);
    //jump over previousTagSizen
    fseek(fp, 4, SEEK_CUR);
    start_time = RTMP_GetTime();
    while (1) {
        //not quite the same as FLV spec
        if (!ReadU8(&type, fp))
            break;
        if (!ReadU24(&datalength, fp))
            break;
        if (!ReadTime(&timestamp, fp))
            break;
        if (!ReadU24(&streamid, fp))
            break;

        if (type != 0x08 && type != 0x09) {
            //jump over non_audio and non_video frame,
            //jump over next previousTagSizen at the same time
            fseek(fp, datalength + 4, SEEK_CUR);
            continue;
        }

        if (fread(packet->m_body, 1, datalength, fp) != datalength)
            break;

        packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
        packet->m_nTimeStamp = timestamp;
        packet->m_packetType = type;
        packet->m_nBodySize = datalength;
        pre_frame_time = timestamp;
        long delt = RTMP_GetTime() - start_time;
        printf("%ld,%ld\n", pre_frame_time, static_cast<long>(RTMP_GetTime() - start_time));
        __android_log_print(ANDROID_LOG_WARN, " wtoe JNI ",
                            "%ld,%ld", pre_frame_time,
                            static_cast<long>(RTMP_GetTime() - start_time));
        if (delt < pre_frame_time) {
            usleep((pre_frame_time - delt) * 1000);
        }
        if (!RTMP_IsConnected(rtmp)) {
            RTMP_Log(RTMP_LOGERROR, "rtmp is not connect\n");
            loge("rtmp is not connect.\n");
            break;
        }
        if (!RTMP_SendPacket(rtmp, packet, 0)) {
            RTMP_Log(RTMP_LOGERROR, "Send Error\n");
            loge("Send Error.\n");
            break;
        }

        if (!ReadU32(&preTagsize, fp))
            break;

        if (!PeekU8(&type, fp))
            break;
        if (type == 0x09) {
            if (fseek(fp, 11, SEEK_CUR) != 0)
                break;
            if (!PeekU8(&type, fp)) {
                break;
            }
            if (type == 0x17)
                bNextIsKey = 1;
            else
                bNextIsKey = 0;

            fseek(fp, -11, SEEK_CUR);
        }
    }

    RTMP_LogPrintf("\nSend Data Over\n");
    logd("\nSend Data Over.\n");
    if (fp)
        fclose(fp);

    if (rtmp != NULL) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        rtmp = NULL;
    }
    if (packet != NULL) {
        RTMPPacket_Free(packet);
        free(packet);
        packet = NULL;
    }
    return 0;
}

具体的代码就不在这里贴出来了,如有需要,可相互学习。
详细代码已上传至GitHub,RtmpDump4Android

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值