1. 程式人生 > >Android 使用librtmp推流【音視訊傳輸】

Android 使用librtmp推流【音視訊傳輸】

1.通過jni呼叫librtmp

下面是jni中提供給java呼叫的本地方法:

public final class PublishJni {
    static {
        System.loadLibrary("publish");
    }

    static native long init(String url, int w, int h, int timeOut);

    static native int sendSpsAndPps(long cptr, byte[] sps, int spsLen, byte[] pps,
                                    int ppsLen, long timestamp);

    static native int sendVideoData(long cptr, byte[] data, int len, long timestamp);

    static native int sendAacSpec(long cptr, byte[] data, int len);

    static native int sendAacData(long cptr, byte[] data, int len, long timestamp);

    static native int stop(long cptr);

}
publish.so包中匯出的呼叫介面
#include <jni.h>
#include <string>
#include "Rtmp.h"

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jlong JNICALL
Java_com_blueberry_media_PublishJni_init(JNIEnv *env, jclass type, jstring url_, jint w, jint h,
                                         jint timeOut) {

    const char *url = env->GetStringUTFChars(url_, 0);
    Rtmp *rtmp = new Rtmp();
    rtmp->init(url, w, h, timeOut);
    env->ReleaseStringUTFChars(url_, url);
    return reinterpret_cast<long> (rtmp);
}


JNIEXPORT jint JNICALL
Java_com_blueberry_media_PublishJni_sendSpsAndPps(JNIEnv *env, jclass type, jlong cptr,
                                                  jbyteArray sps_, jint spsLen, jbyteArray pps_,
                                                  jint ppsLen, jlong timestamp) {
    jbyte *sps = env->GetByteArrayElements(sps_, NULL);
    jbyte *pps = env->GetByteArrayElements(pps_, NULL);
    Rtmp *rtmp = reinterpret_cast<Rtmp *>(cptr);
    int ret = rtmp->sendSpsAndPps((BYTE *) sps, spsLen, (BYTE *) pps, ppsLen, timestamp);

    env->ReleaseByteArrayElements(sps_, sps, 0);
    env->ReleaseByteArrayElements(pps_, pps, 0);
    return ret;
}

JNIEXPORT jint JNICALL
Java_com_blueberry_media_PublishJni_sendVideoData(JNIEnv *env, jclass type, jlong cptr,
                                                  jbyteArray data_, jint len, jlong timestamp) {
    jbyte *data = env->GetByteArrayElements(data_, NULL);
    Rtmp *rtmp = reinterpret_cast<Rtmp *> (cptr);
    int ret = rtmp->sendVideoData((BYTE *) data, len, timestamp);

    env->ReleaseByteArrayElements(data_, data, 0);

    return ret;
}


JNIEXPORT jint JNICALL
Java_com_blueberry_media_PublishJni_sendAacSpec(JNIEnv *env, jclass type, jlong cptr,
                                                jbyteArray data_, jint len) {
    jbyte *data = env->GetByteArrayElements(data_, NULL);

    Rtmp *rtmp = reinterpret_cast<Rtmp *> (cptr);
    int ret = rtmp->sendAacSpec((BYTE *) data, len);

    env->ReleaseByteArrayElements(data_, data, 0);
    return ret;
}

JNIEXPORT jint JNICALL
Java_com_blueberry_media_PublishJni_sendAacData(JNIEnv *env, jclass type, jlong cptr,
                                                jbyteArray data_, jint len, jlong timestamp) {
    jbyte *data = env->GetByteArrayElements(data_, NULL);

    Rtmp *rtmp = reinterpret_cast<Rtmp *> (cptr);
    int ret = rtmp->sendAacData((BYTE *) data, len, timestamp);

    env->ReleaseByteArrayElements(data_, data, 0);
    return ret;
}

JNIEXPORT jint JNICALL
Java_com_blueberry_media_PublishJni_stop(JNIEnv *env, jclass type, jlong cptr) {
    Rtmp *rtmp = reinterpret_cast<Rtmp *> (cptr);
    delete rtmp;
    return 0;
}

#ifdef __cplusplus
}
#endif

Rtmp具體介面實現:
#include "Rtmp.h"

int Rtmp::init(std::string url, int w, int h, int timeOut) {

    RTMP_LogSetLevel(RTMP_LOGDEBUG);
    rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);
    LOGI("time out = %d",timeOut);
    rtmp->Link.timeout = timeOut;
    RTMP_SetupURL(rtmp, (char *) url.c_str());
    RTMP_EnableWrite(rtmp);

    if (!RTMP_Connect(rtmp, NULL) ) {
        LOGI("RTMP_Connect error");
        return -1;
    }
    LOGI("RTMP_Connect success.");

    if (!RTMP_ConnectStream(rtmp, 0)) {
        LOGI("RTMP_ConnectStream error");
        return -1;
    }

    LOGI("RTMP_ConnectStream success.");
    return 0;
}

int Rtmp::sendSpsAndPps(BYTE *sps, int spsLen, BYTE *pps, int ppsLen, long timestamp) {

    int i;
    RTMPPacket *packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + 1024);
    memset(packet, 0, RTMP_HEAD_SIZE);
    packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
    BYTE *body = (BYTE *) packet->m_body;

    //VideoTagHeader[TagType==9]
    //Frame Type 4bits | CodecID 4bits | AVCPacketType 8bit | CompositionTime 24bits|
    //A)Frame Type, Type of video frame. The following values are defined:
    //1 = key frame (for AVC, a seekable frame)
    //2 = inter frame (for AVC, a non-seekable frame)
    //3 = disposable inter frame (H.263 only)
    //4 = generated key frame (reserved for server use only)
    //5 = video info/command frame

    //B)CodecID, Codec Identifier. The following values are defined:
    //2 = Sorenson H.263
    //3 = Screen video
    //4 = On2 VP6
    //5 = On2 VP6 with alpha channel
    //6 = Screen video version 2
    //7 = AVC

    //C)AVCPacketType, IF CodecID == 7:
    //0 = AVC sequence header
    //1 = AVC NALU
    //2 = AVC end of sequence (lower level NALU sequence ender is not required or supported)

    //D)CompositionTime, IF CodecID == 7:
    //IF AVCPacketType == 1
    //Composition time offset
    //ELSE
    //0
    //See ISO 14496-12, 8.15.3 for an explanation of composition
    //times. The offset in an FLV file is always in milliseconds.

    i = 0;
    body[i++] = 0x17; //1:keyframe(I幀) 7:AVC
    body[i++] = 0x00; //AVC packet type:AVC sequence header 

    body[i++] = 0x00; //composition time:3位元組,AVC時無意義,全為0 
    body[i++] = 0x00;
    body[i++] = 0x00; //fill in 0

    /*AVCDecoderConfigurationRecord*/
    body[i++] = 0x01;   //configurationVersion  1(byte)    版本
    body[i++] = sps[1]; //AVCProfileIndecation  1(byte)    sps[1]
    body[i++] = sps[2]; //profile_compatibilty  1(byte)    sps[2]
    body[i++] = sps[3]; //AVCLevelIndication    1(byte)    sps[3]
    body[i++] = 0xff;   //lengthSizeMinusOne    1(byte)    FLV中NALU包長資料所使用的位元組數,包長= (lengthSizeMinusOne & 3) + 1

    /*SPS*/
    body[i++] = 0xe1;                  //numOfSequenceParameterSets    1(byte)    SPS個數,通常為0xe1 個數= numOfSequenceParameterSets & 01F
    body[i++] = (spsLen >> 8) & 0xff;  //sequenceParameterSetLength    2(byte)    SPS長度
    body[i++] = spsLen & 0xff;         
    /*sps data*/
    memcpy(&body[i], sps, spsLen);     //sequenceParameterSetNALUnits  x(byte)    SPS內容

    i += spsLen;

    /*PPS*/
    body[i++] = 0x01;                   //numOfPictureParameterSets  1(byte)         PPS個數,通常為0x01
    /*sps data length*/
    body[i++] = (ppsLen >> 8) & 0xff;   //pictureParameterSetLength  2(byte)         PPS長度
    body[i++] = ppsLen & 0xff;
    memcpy(&body[i], pps, ppsLen);      //sequenceParameterSetNALUnits ppslen(byte)  PPS內容
    i += ppsLen;

    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nBodySize = i;
    packet->m_nChannel = 0x04;
    packet->m_nTimeStamp = 0;
    packet->m_hasAbsTimestamp = 0;
    packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
    packet->m_nInfoField2 = rtmp->m_stream_id;

    /*傳送*/
    if (RTMP_IsConnected(rtmp)) {
        RTMP_SendPacket(rtmp, packet, TRUE);
    }
    free(packet);
    return 0;
}

int Rtmp::sendVideoData(BYTE *buf, int len, long timestamp) {
    int type;

    //sps 與 pps 的幀界定符都是 00 00 00 01,而普通幀可能是 00 00 00 01 也有可能 00 00 01
    /*去掉幀界定符*/
    if (buf[2] == 0x00) {//startcode:/*00 00 00 01*/ 
        buf += 4;
        len -= 4;
    } else if (buf[2] == 0x01) {//startcode:/*00 00 01*/ 
        buf += 3;
        len - 3;
    }

    type = buf[0] & 0x1f; //1bit:forbidden,2bit:priority,5bit:type

    RTMPPacket *packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + len + 9);
    memset(packet, 0, RTMP_HEAD_SIZE);
    packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
    packet->m_nBodySize = len + 9;


    /* send video packet*/
    BYTE *body = (BYTE *) packet->m_body;
    memset(body, 0, len + 9);

    /*key frame*/
    body[0] = 0x27;                 //2:innerframe(P幀) 7:AVC 
    if (type == NAL_SLICE_IDR) {
        body[0] = 0x17;             //1:keyframe(I幀) 7:AVC
    }

    body[1] = 0x01;    //AVC packet type:AVC NALU  /*nal unit*/
    body[2] = 0x00;    //composition time:3位元組,AVC時無意義,全為0 
    body[3] = 0x00;
    body[4] = 0x00;

    body[5] = (len >> 24) & 0xff;    //NALU length:(lengthSizeMinusOne & 3) + 1位元組 NALU長度 
    body[6] = (len >> 16) & 0xff;
    body[7] = (len >> 8) & 0xff;
    body[8] = (len) & 0xff;

    /*copy data*/
    memcpy(&body[9], buf, len);      //NALU Data

    packet->m_hasAbsTimestamp = 0;
    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nInfoField2 = rtmp->m_stream_id;
    packet->m_nChannel = 0x04;
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nTimeStamp = timestamp;

    if (RTMP_IsConnected(rtmp)) {
        RTMP_SendPacket(rtmp, packet, TRUE);
    }
    free(packet);

    return 0;
}

int Rtmp::sendAacSpec(BYTE *data, int spec_len) {
    RTMPPacket *packet;
    BYTE *body;
    int len = spec_len;//spec len 是2, AAC sequence header存放的是AudioSpecificConfig結構
    packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + len + 2);
    memset(packet, 0, RTMP_HEAD_SIZE);
    packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
    body = (BYTE *) packet->m_body;

    //AudioTagHeader[TagType==8]
    //SoundFormat 4bits | SoundRate 2bits | SoundSize 1bit | SoundType 1bits| AACPacketType 8bits|
    //A)SoundFormat, 音訊格式. The following values are defined:
    //0 = Linear PCM, platform endian
    //1 = ADPCM
    //2 = MP3
    //3 = Linear PCM, little endian
    //4 = Nellymoser 16 kHz mono
    //5 = Nellymoser 8 kHz mono
    //6 = Nellymoser
    //7 = G.711 A-law logarithmic PCM
    //8 = G.711 mu-law logarithmic PCM
    //9 = reserved
    //10 = AAC
    //11 = Speex
    //14 = MP3 8 kHz
    //15 = Device-specific sound
    //Formats 7, 8, 14, and 15 are reserved.
    //AAC is supported in Flash Player 9,0,115,0 and higher.
    //Speex is supported in Flash Player 10 and higher.

    //B)SoundRate, 取樣率. The following values are defined:
    //0 = 5.5 kHz
    //1 = 11 kHz
    //2 = 22 kHz
    //3 = 44 kHz

    //C)SoundSize, 取樣精度. Size of each audio sample. This parameter only pertains to uncompressed formats. Compressed formats always decode to 16 bits internally.
    //0 = 8-bit samples
    //1 = 16-bit samples

    //D)SoundType, 聲道數. Mono or stereo sound
    //0 = Mono sound
    //1 = Stereo sound

    //E)AACPacketType, AACAUDIODATA的型別. IF SoundFormat == 10
    //只有音訊格式為AAC(0x0A),AudioTagHeader中才會多出1個位元組的資料AACPacketType
    //0 = AAC sequence header
    //1 = AAC raw

    //1)第一個位元組af,a就是10代表的意思是AAC.
    //後四位f代表如下:
    //前2個bit的含義 抽樣頻率,二進位制11,代表44kHZ.
    //第3個bit,取樣精度, 1代表音訊用16位的.
    //第4個bit代表聲道 1代表Stereo.

    //2)第2個位元組00, 代表AACPacketType:
    //0 = AAC sequence header,1 = AAC raw。
    //第一個音訊包用0,後面的都用1
    /*AF 00 +AAC sequence header*/
    body[0] = 0xAF;    
    body[1] = 0x00;
    memcpy(&body[2], data, len);/*data 是AAC sequeuece header資料*/

    //AudioSpecificConfig(2byte)結構定義:
    //audioObjectType:5bit,表示編碼結構型別,AAC main編碼為1,LOW低複雜度編碼為2,SSR為3。
    //samplingFrequencyIndex:4bit,表示取樣率。
    //0x00   96000
    //0x01   88200
    //0x02   64000
    //0x03   48000
    //0x04   44100
    //0x05   32000
    //0x06   24000
    //0x07   22050
    //0x08   16000
    //0x09   12000
    //0x0A   11025
    //0x0B     8000
    //0x0C   reserved
    //0x0D   reserved
    //0x0E   reserved
    //0x0F   escape value
    // 按理說,應該是:0 ~ 96000, 1~88200, 2~64000, 3~48000, 4~44100, 5~32000, 6~24000, 7~ 22050, 8~16000...),
    //通常aac固定選中44100,即應該對應為4,但是試驗結果表明,當音訊取樣率小於等於44100時,應該選擇3,而當音訊取樣率為48000時,應該選擇2;
    //channelConfiguration:4bit,表示聲道數。
    //frameLengthFlag:1bit
    //dependsOnCoreCoder:1bit
    //extensionFlag:1bit
    //最後3bit,固定為0

    packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
    packet->m_nBodySize = len + 2;
    packet->m_nChannel = STREAM_CHANNEL_AUDIO;
    packet->m_nTimeStamp = 0;
    packet->m_hasAbsTimestamp = 0;
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nInfoField2 = rtmp->m_stream_id;

    if (RTMP_IsConnected(rtmp)) {
        RTMP_SendPacket(rtmp, packet, TRUE);
    }
    free(packet);

    return 0;
}

int Rtmp::sendAacData(BYTE *data, int len, long timeOffset) {
//    data += 5;
//    len += 5;
    if (len > 0) {
        RTMPPacket *packet;
        BYTE *body;
        packet = (RTMPPacket *) malloc(RTMP_HEAD_SIZE + len + 2);
        memset(packet, 0, RTMP_HEAD_SIZE);
        packet->m_body = (char *) packet + RTMP_HEAD_SIZE;
        body = (BYTE *) packet->m_body;

        /*AF 00 +AAC Raw data*/
        body[0] = 0xAF;
        body[1] = 0x01;
        memcpy(&body[2], data, len);

        packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
        packet->m_nBodySize = len + 2;
        packet->m_nChannel = STREAM_CHANNEL_AUDIO;
        packet->m_nTimeStamp = timeOffset;
        packet->m_hasAbsTimestamp = 0;
        packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
        packet->m_nInfoField2 = rtmp->m_stream_id;
        if (RTMP_IsConnected(rtmp)) {
            RTMP_SendPacket(rtmp, packet, TRUE);
        }
        LOGD("send packet body[0]=%x,body[1]=%x", body[0], body[1]);
        free(packet);

    }
    return 0;
}

int Rtmp::stop() const {
    RTMP_Close(rtmp);
    RTMP_Free(rtmp);
    return 0;
}

Rtmp::~Rtmp() { stop(); }