为了记录编译librtmp开源库过程遇到的问题,帮助后来人提供参考,希望本文能为大家提供参考。
The Real-Time Messaging Protocol (RTMP) is used for streaming multimedia content across a TCP/IP network. This API provides most client functions and a few server functions needed to support RTMP, RTMP tunneled in HTTP (RTMPT), encrypted RTMP (RTMPE), RTMP over SSL/TLS (RTMPS) and tunneled variants of these encrypted types (RTMPTE, RTMPTS). The basic RTMP specification has been published by Adobe but this API was reverse-engineered without use of the Adobe specification. As such, it may deviate from any published specifications but it usually duplicates the actual behavior of the original Adobe clients.
可以从以下两个仓库下载源码,目前使用master主分支
git clone https://github.com/shishuo365/librtmp.git
git clone https://github.com/LinChengChun/librtmp.git
为了开发支持android平台的库,所以需要下载google官方的NDK
https://developer.android.google.cn/ndk/downloads/index.html
cd android-ndk-r13b/build/tools/
./make-standalone-toolchain.sh --platform=android-14 --install-dir=/home/cclin/android-toolchain --force
----------------------------------------------------------------------------------------------------------------------------------------
Valid options (defaults are in brackets):
--help Print this help.
--verbose Enable verbose mode.
--dryrun Unsupported.
--toolchain= Specify toolchain name
--use-llvm No-op. Clang is always available.
--stl= Specify C++ STL [gnustl]
--arch= Specify target architecture 默认是 arm,可以指定其他指令架构
--abis= No-op. Derived from --arch or --toolchain.
--ndk-dir= Unsupported.
--package-dir= Place package file in [/tmp/ndk-cclin] 如果不指定安装目录,默认生成一个压缩包,路径在/tmp/ndk-cclin
--install-dir= Don't create package, install files to instead. 指定 工具链安装目录
--dryrun Unsupported.
--platform= Specify target Android platform/API level. [android-9] 指定哪个平台/API 版本
--force Remove existing install directory. 强制覆盖上一次生成的文件
diff --git a/Makefile b/Makefile
index 1597948..a761d8d 100644
--- a/Makefile
+++ b/Makefile
@@ -72,7 +72,7 @@ SOINST_yes=install_so
SO_DEF=$(SODEF_$(SHARED))
SO_LIB=$(SOLIB_$(SHARED))
SO_INST=$(SOINST_$(SHARED))
-
+XLDFLAGS=-llog
DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF)
OPT=-O2
CFLAGS=-Wall $(ARCH_CFLAGS) $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF)
diff --git a/tools/compile-librtmp-android.sh b/tools/compile-librtmp-android.sh
old mode 100644
new mode 100755
index 032bfab..a56faa7
--- a/tools/compile-librtmp-android.sh
+++ b/tools/compile-librtmp-android.sh
@@ -8,17 +8,19 @@ if [ -z "$RTMP_ARCH" ]; then
fi
# replace this path to your android NDK standalone toolchain ptah.
-RTMP_TOOLCHAIN_PATH=/Users/shishuo/Documents/app/Android-NDK-Toolchain
+#RTMP_TOOLCHAIN_PATH=/Users/shishuo/Documents/app/Android-NDK-Toolchain
+RTMP_TOOLCHAIN_PATH=/home/cclin/android-toolchain/bin/
RTMP_BUILD_ROOT=`pwd`
RTMP_BIN_ROOT=$RTMP_BUILD_ROOT/bin
RTMP_ARCH_DIR=
cd ..
compile_armv7a() {
- export CC=$RTMP_TOOLCHAIN_PATH/arm/bin/arm-linux-androideabi-gcc
- export LD=$RTMP_TOOLCHAIN_PATH/arm/bin/arm-linux-androideabi-ld
- export AR=$RTMP_TOOLCHAIN_PATH/arm/bin/arm-linux-androideabi-ar
- export ARCH_CFLAGS=-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb
+ export CC=$RTMP_TOOLCHAIN_PATH/arm-linux-androideabi-gcc
+ export LD=$RTMP_TOOLCHAIN_PATH/arm-linux-androideabi-ld
+ export AR=$RTMP_TOOLCHAIN_PATH/arm-linux-androideabi-ar
+ export ARCH_CFLAGS=-march=armv7-a
+ #-mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb
make clean
make all
}
@@ -94,6 +96,7 @@ release_librtmp() {
cp -f http.h $RTMP_BIN_ROOT/$RTMP_ARCH_DIR/include/librtmp
cp -f log.h $RTMP_BIN_ROOT/$RTMP_ARCH_DIR/include/librtmp
cp -f rtmp.h $RTMP_BIN_ROOT/$RTMP_ARCH_DIR/include/librtmp
+ #cp -f librtmp.so.1 $RTMP_BIN_ROOT/$RTMP_ARCH_DIR/librtmp.so
}
if [ "$RTMP_ARCH" = "armv7a" ]; then
@@ -137,4 +140,4 @@ elif [ "$RTMP_ARCH" = "all" ]; then
else
echo "unknown architecture $RTMP_ARCH";
exit 1
-fi
\ No newline at end of file
+fi
./compile-librtmp-android.sh armv7a // 由于只编译armv7a架构的,因此只修改了compile-librtmp-android.sh中对应的编译器有效路径,如果需要编译其他架构需要重新生成对应工具链,重新配置对应架构下面编译器的有效路径
librtmp/tools/bin/armeabi-v7a$cp ../../../librtmp.so.1 librtmp.so
librtmp/tools/bin/armeabi-v7a$ ls -R
.:
include lib librtmp.so
./include:
librtmp
./include/librtmp:
amf.h http.h log.h rtmp.h
./lib:
librtmp.a
# Android.mk for rtmp
LOCAL_PATH := $(call my-dir)
# rtmp library
include $(CLEAR_VARS)
LOCAL_MODULE := rtmp
LOCAL_SRC_FILES := librtmp.so
include $(PREBUILT_SHARED_LIBRARY)
#include $(PREBUILT_STATIC_LIBRARY)
# Program
include $(CLEAR_VARS)
LOCAL_MODULE := nativeRtmp
LOCAL_SRC_FILES := nativeRtmp.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := rtmp
include $(BUILD_SHARED_LIBRARY)
-----------------------------------------------------------------上面是引用librtmp.so动态库脚本,下面是引用librtmp.a静态库脚本------------
# Android.mk for rtmp
LOCAL_PATH := $(call my-dir)
# rtmp library
include $(CLEAR_VARS)
LOCAL_MODULE := rtmp
#LOCAL_SRC_FILES := librtmp.so
#include $(PREBUILT_SHARED_LIBRARY)
LOCAL_SRC_FILES := librtmp.a
include $(PREBUILT_STATIC_LIBRARY)
# Program
include $(CLEAR_VARS)
LOCAL_MODULE := nativeRtmp
LOCAL_SRC_FILES := nativeRtmp.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_STATIC_LIBRARIES := rtmp
#LOCAL_SHARED_LIBRARIES := rtmp
include $(BUILD_SHARED_LIBRARY)
#APP_ABI := all
#APP_ABI := armeabi armeabi-v7a x86
APP_ABI :=armeabi-v7a
package org.src.rtmp_get;
/**
* Created by Administrator on 2015/12/4.
*/
public class rtmpJNI {
public native void rtmpDeInit();
public native void rtmpInit(String rtmpUrl);
static {
System.loadLibrary("rtmp"); // 如果编译脚本引用librtmp.so库,需要添加该句;如果引用librtmp.a,需要注释该句;引用动态库时,native调用RTMP_Close时会出现进程崩溃;引用静态库时,运行正常,所以建议引用静态库。
System.loadLibrary("nativeRtmp");
}
}
package org.src.rtmp_get;
import android.app.Activity;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
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();
mRtmpClient.rtmpInit("rtmp://192.168.1.116");
}
@Override
protected void onDestroy() {
super.onDestroy();
mRtmpClient.rtmpDeInit();
}
}
//
// Created by Administrator on 2015/12/4.
//
#include
#include
#include
#include "include/librtmp/rtmp.h"
#include
#include
#define LOG_TAG "NativeRTMP"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
static const char *ClassName = "org/src/rtmp_get/rtmpJNI";
char* pubRtmpUrl;
RTMP *pubRtmp; //rtmp
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);
}
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);
}
void my_Native_rtmpDeInit(JNIEnv *env, jobject jobj)
{
if (RTMP_IsConnected(pubRtmp)) {
RTMP_Close(pubRtmp);
}
RTMP_Free(pubRtmp);
free(pubRtmpUrl);
}
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)
{
}
根据以上记录,librtmp库最好在linux平台编译(mingw编译不成功);编译成功后,新建一个工程测试一下librtmp库的功能,前提是具有RTMP服务器,便于通信。当然,也可以使用附件的工程来测试;本文主要是记录 librtmp编译以及在APP中的应用。后续再分析librtmp、rtmp协议相关知识点。^-^
附件rtmpDumpDemo
链接:https://pan.baidu.com/s/1nvl9inn 密码:plhb