Android Rtmp客户端搭建

来源:互联网 发布:Python如何使用 编辑:程序博客网 时间:2024/06/10 11:29

http://blog.csdn.net/hack__zsmj/article/details/50177669

@Author:明明不说话 
@Statement:博客内容纯属个人观点,欢迎转载,转载请注明,谢谢

RTMP介绍

RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议,主要用来在Flash/AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括Adobe Media Server/Ultrant Media Server/red5等。

Android的RTMP方案

Android上常用的RTMP工程可以用juv-rtmp-client,这是一款收费的软件,但是可以在网上找破解版作为学习了解使用,可以参考该示例。使用起来就是调用API,底层基本上都不要处理接触,比较方便。 
同时还有一个开源工程rmtpdump,完全开源C语言代码,我们可以自己根据需要编译和定制,例如要修改rtmp协议时(rtmp内容修改添加部分字段),则rtmpdump则体现出它的优势了。

RtmpDump使用

下载RtmpDump

可以在RtmpDump网站上下载源码编译(编译方法可以网上搜索),或者使用官网已经编译好的rtmpdump-2.3-android.zip库

rtmp native实现

native代码添加

这里写图片描述图1 
按照Android jni代码方式实现添加,不清楚百度或者参考demo,可以看到代码结构里面包含了librtmp.a和librtmp.so两个文件,其实只是链接方式的不同而已,最后JNI结构如图1所示,Android.mk根据librtmp目标文件是否静态而选在PREBUILT_SHARED_LIBRARY还是PREBUILT_STATIC_LIBRARY

Android.mk按如下编写

# Android.mk for rtmpLOCAL_PATH := $(call my-dir)# rtmp libraryinclude $(CLEAR_VARS)LOCAL_MODULE := rtmpLOCAL_SRC_FILES := librtmp.soinclude $(PREBUILT_SHARED_LIBRARY) #动态库#include $(PREBUILT_STATIC_LIBRARY)# Programinclude $(CLEAR_VARS)LOCAL_MODULE := nativeRtmpLOCAL_SRC_FILES := nativeRtmp.cLOCAL_C_INCLUDES += $(LOCAL_PATH)/includeLOCAL_LDLIBS := -llog -lzLOCAL_SHARED_LIBRARIES := rtmpinclude $(BUILD_SHARED_LIBRARY)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Application.mk编写

#APP_ABI := all#APP_ABI := armeabi armeabi-v7a x86APP_ABI :=armeabi
  • 1
  • 2
  • 3

rtmpdump jni使用

编写nativeRtmp.c,分别编写初始化,销毁和发送函数,来测试使用librtmp.so

发送函数

void send_pkt(char* buf, int buflen, int type, unsigned int timestamp){    int ret;    RTMPPacket rtmp_pakt;    RTMPPacket_Reset(&rtmp_pakt);    RTMPPacket_Alloc(&rtmp_pakt, buflen);    rtmp_pakt.m_packetType = type;    rtmp_pakt.m_nBodySize = buflen;    rtmp_pakt.m_nTimeStamp = timestamp;    rtmp_pakt.m_nChannel = 4;    rtmp_pakt.m_headerType = RTMP_PACKET_SIZE_LARGE;    rtmp_pakt.m_nInfoField2 = pubRtmp->m_stream_id;    memcpy(rtmp_pakt.m_body, buf, buflen);    ret = RTMP_SendPacket(pubRtmp, &rtmp_pakt, 0);    RTMPPacket_Free(&rtmp_pakt);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

初始化

void my_Native_rtmpInit(JNIEnv *env, jobject jobj,jstring jRtmpUrl){    const char* rtmpUrl = (*env)->GetStringUTFChars(env, jRtmpUrl, 0);    pubRtmpUrl = malloc(strlen(rtmpUrl) + 1);    memset(pubRtmpUrl, 0, strlen(rtmpUrl) + 1);    strcpy(pubRtmpUrl, rtmpUrl);    pubRtmp = RTMP_Alloc();    RTMP_Init(pubRtmp);    LOGI("RTMP_Init %s", pubRtmpUrl);    if (!RTMP_SetupURL(pubRtmp, pubRtmpUrl)) {        LOGE("RTMP_SetupURL error");        return;    }    if (!RTMP_Connect(pubRtmp, NULL) ) {        LOGE("RTMP_Connect error");        return;    }   /* if (!RTMP_ConnectStream(pubRtmp, 0)) {        LOGE("RTMP_ConnectStream  error");        return;    }*/    LOGI("RTMP Connect  ok");    send_pkt("aa", 20, RTMP_PACKET_TYPE_AUDIO,  20);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

销毁

void my_Native_rtmpDeInit(JNIEnv *env, jobject jobj){    if (RTMP_IsConnected(pubRtmp)) {        RTMP_Close(pubRtmp);    }    RTMP_Free(pubRtmp);    free(pubRtmpUrl);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

jni动态加载

这里采用JNI_OnLoad函数来加载jni的方法,供给API调用

static JNINativeMethod METHODS[]={        {"rtmpInit","(Ljava/lang/String;)V",(void *)my_Native_rtmpInit},        {"rtmpDeInit","()V",(void *)my_Native_rtmpDeInit},};JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {    int retVal = -1;    JNIEnv *env;    if ( (*vm)->GetEnv(vm,(void **) &env, JNI_VERSION_1_4) == JNI_OK  ) {    jclass clazz = (*env)->FindClass(env,ClassName);    if (clazz != NULL) {    if ((*env)->RegisterNatives(env,clazz, METHODS,sizeof(METHODS) / sizeof(METHODS[0])) >= 0) {    retVal = JNI_VERSION_1_4;    }else{    LOGI("RegisterNatives Subprocess.create method failed!");    }    (*env)->DeleteLocalRef(env,clazz);    }else{    LOGI("className not found!");    }    }    return retVal;}JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved){}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

ps:这里需要定义ClassName变量,例如demo中

static const char *ClassName = "org/src/rtmp_get/rtmpJNI";
  • 1

就是说,是org.src.rtmp_get.rtmpJNI类调用了该jni的接口,根据实际调用类的位置和名字来修改该ClassName的名字

rtmp 应用调用

java 类实现

添加需要的libray以及申明Native本地方法,这里需要和JNINativeMethod 中定义的一致,否则会报找不到该接口。

public class rtmpJNI {    public native void rtmpDeInit();    public native void rtmpInit(String rtmpUrl);    static {        System.loadLibrary("rtmp");        System.loadLibrary("nativeRtmp");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

应用实现

public class MainActivity extends Activity {    private rtmpJNI  mRtmpClient ;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mRtmpClient = new rtmpJNI(); //初始化打开链接rtmp流媒体服务器        mRtmpClient.rtmpInit("rtmp://live.hkstv.hk.lxdns.com/live/hks"); //亲测该rtmp地址可用,可以采用rtmp流播放器播放视频    }    @Override    protected void onDestroy() {        super.onDestroy();        mRtmpClient.rtmpDeInit();//销毁断开链接    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

ps一定要记得申明权限,否则会不能正常访问网络

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  • 1

测试链接

可以用简易RED5流媒体服务器测试参考Windows环境搭建Red5流媒体服务器指南,执行red5.bat,然后执行上述应用,看是否能够链接上服务器。

总结

上面就是对rtmpDump的使用,可以参考如上操作,附上demo以及相关链接,Q&A。

Demo下载地址 
red5流媒体服务器搭建下载地址

 


0 0
原创粉丝点击