Android RTMP录频直播四(rtmpdump集成)

1. rtmpdump库,下载源码

rtmpdump库github下载地址

librtmp.png

2. 将librtmp文件夹下的源码集成到Android
  1. 下载rtmpdump源码,将librtmp文件夹拷贝到android cpp目录下。
  2. 删除掉没用的文件,只保留.c .h文件。
  3. 在librtmp目录下,创建并编写CMakeLists.txt。
  4. 在外层CMakeLists.txt配置rtmp。
    如下图所示:


    rtmp_cpp.png

    librtmp目录下的CMakeLists.txt配置

cmake_minimum_required(VERSION 3.10.2)

project("librtmp")

# 只要有一个C++文件,就是属于C++ == CXX CMAKE_CXX_FLAGS
# 不修改源码的情况下,解决报错,添加宏  -D
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_CRYPTO")

file(GLOB SRC_FILE *.c)

add_library(rtmp SHARED ${SRC_FILE})

项目CMakeLists.txt配置

cmake_minimum_required(VERSION 3.10.2)

project("rtmppush")

file(GLOB SRC_FILE *.cpp *.h)

add_library(rtmppush SHARED ${SRC_FILE})

## 引入头文件目录
include_directories(librtmp)
## 配置librtmp
add_subdirectory(librtmp)
## rtmp链接到rtmppush总库
target_link_libraries(
        rtmppush
        rtmp
        log)
3.rtmp使用流程
RTMP使用流程.png
4. rtmp使用ndk代码
#include 
#include 
#include 
#include 
#include "safe_queue.h"

bool isStart = false;
bool isConnect = false;
char *url = nullptr;
pthread_t pid;
// RTMP包的队列
SafeQueue packets;

// 连接RTMP
void *start_task(void *args) {
    // 连接RTMP服务器 , 要做好多事
    RTMP *rtmp = nullptr;
    do {
        // 1. 申请RTMP内存
        rtmp = RTMP_Alloc();
        if (!rtmp) {
            LOGE("rtmp申请存在失败");
            break;
        }
        // 2. 初始化
        RTMP_Init(rtmp);
        // 3. 设置地址
        int r = RTMP_SetupURL(rtmp, url);
        // r 0代表失败
        if (!r) {
            LOGE("rtmp设置url失败,url:%s", url);
            break;
        }
        // 4. 开启输出模式
        RTMP_EnableWrite(rtmp);
        // 5. 连接
        r = RTMP_Connect(rtmp, nullptr);
        if (!r) {
            LOGE("rtmp连接失败:%d url=%s", r, url);
            break;
        }
        r = RTMP_ConnectStream(rtmp, 0);
        if (!r) {
            LOGE("rtmp连接流失败:%d url=%s", r, url);
            break;
        }
        LOGE(" ====== rtmp连接成功 ======");
        isConnect = true;

        // 这里要回调Java层
        if (javaCallback) {
            javaCallback->onStart(THREAD_CHILD);
        }
        // 队列开始工作
        packets.setWork(1);

        RTMPPacket *packet = nullptr;
        while (isConnect) {
            packet = nullptr;
            // 去取队列里面的包,如果没有拿到会阻塞
            packets.pop(packet);
            if (!isConnect) {
                break;
            }
            if (!packet) {
                continue;
            }
            // 给RTMP推流的ID
            packet->m_nInfoField2 = rtmp->m_stream_id;

            // 成功取出数据包,发送
            r = 0;
            if (rtmp) {
                r = RTMP_SendPacket(rtmp, packet, 1); // 1==true 开启内部缓冲
            }
            // packet 你都发给服务器了,可以大胆释放
            releasePacket(&packet);
            packet = nullptr;
            if (!r) { // ret == 0 和 ffmpeg不同,0代表失败
                LOGE("rtmp 发送包失败 自动断开服务器");
                break;
            }
        }

        // 如果跳出上面的循环,就释放包
        if (packet) {
            releasePacket(&packet);
        }
    } while (false);

    LOGE(" ====== rtmp连接退出 ======");

    isStart = false;
    isConnect = false;
    startTime = 0;
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        rtmp = nullptr;
    }
    if (url) {
        delete url;
        url = nullptr;
    }
    packets.setWork(0);
    packets.clear();
    return 0;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_boardour_toupin_push_RtmpPush_nStart(JNIEnv *env, jobject thiz, jstring url_) {
    if (isStart) {
        return;
    }
    isStart = true;
    const char *path = env->GetStringUTFChars(url_, 0);
    url = new char[strlen(path) + 1];
    strcpy(url, path);
    pthread_create(&pid, 0, start_task, 0);
    env->ReleaseStringUTFChars(url_, path);
}

5. SafeQueue(阻塞队列,生产消费者模式) ndk代码
#ifndef DERRY_SAFE_QUEUE_H
#define DERRY_SAFE_QUEUE_H

#include 
#include 

using namespace std;

template
class SafeQueue {
    typedef void (*ReleaseCallback)(T *);

    typedef void (*SyncHandle)(queue &);

private:
    queue q;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int work = 0; // 标记队列是否工作
    ReleaseCallback releaseCallback;
    SyncHandle syncHandle;
    int maxSize = 0x0fffffff;
public:
    SafeQueue() {
        pthread_mutex_init(&mutex, 0); // 动态初始化互斥锁
        pthread_cond_init(&cond, 0);
    }

    ~SafeQueue() {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
    }

    /**
     * 入队
     * @param value
     */
    void push(T value) {
        pthread_mutex_lock(&mutex); // 先锁起来
        if (work) {
            // 工作状态需要push
            if (size() < maxSize) {
                q.push(value);
            }
            // 广播通知
            pthread_cond_signal(&cond);
        } else {
            // 非工作状态
            if (releaseCallback) {
                releaseCallback(&value); // T无法释放, 让外界释放
            }
        }
        pthread_mutex_unlock(&mutex); // 解锁
    }

    /**
     * 出队
     * @param value
     * @return
     */
    int pop(T &value) {
        int ret = 0;
        pthread_mutex_lock(&mutex); // 先锁起来
        while (work && q.empty()) {
            // 工作状态,说明确实需要pop,但是队列为空,需要等待
            pthread_cond_wait(&cond, &mutex);
        }
        if (!q.empty()) {
            value = q.front();
            //弹出
            q.pop();
            ret = 1;
        }
        pthread_mutex_unlock(&mutex); // 解锁
        return ret;
    }

    /**
     * 设置队列的工作状态
     * @param work
     */
    void setWork(int work) {
        pthread_mutex_lock(&mutex); // 先锁起来
        this->work = work;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex); // 解锁
    }

    /**
     * 设置队列缓存最大数量
     * @param maxSize
     */
    void setMaxSize(int maxSize) {
        this->maxSize = maxSize;
    }

    /**
     * 判断队列是否为空
     * @return
     */
    int empty() {
        return q.empty();
    }

    /**
     * 获取队列大小
     * @return
     */
    int size() {
        return q.size();
    }

    /**
     * 清空队列 队列中的元素如何释放? 让外界释放
     */
    void clear() {
        pthread_mutex_lock(&mutex); // 先锁起来
        unsigned int size = q.size();
        for (int i = 0; i < size; ++i) {
            //取出队首元素
            T value = q.front();
            if (releaseCallback) {
                releaseCallback(&value);
            }
            q.pop();
        }
        pthread_mutex_unlock(&mutex); // 解锁
    }

    void setReleaseCallback(ReleaseCallback releaseCallback) {
        this->releaseCallback = releaseCallback;
    }

    void setSyncHandle(SyncHandle syncHandle) {
        this->syncHandle = syncHandle;
    }

    /**
     * 同步操作
     */
    void sync() {
        pthread_mutex_lock(&mutex); // 先锁起来
        syncHandle(q);

        pthread_mutex_unlock(&mutex); // 再解锁
    }


};

#endif //DERRY_SAFE_QUEUE_H

RTMP链接服务器成功后,就会开启一个死循环,阻塞等待音视频包加入队列,如果有音视频包加入队列就会拿出来发送到RTMP服务器。

你可能感兴趣的:(Android RTMP录频直播四(rtmpdump集成))