librtmp發送AVC,AAC數據包

1. libRTMP的包結構

  typedef struct RTMPPacket
  {
    uint8_t  m_headerType;   
    uint8_t  m_packetType;
    uint8_t  m_hasAbsTimestamp;	
    int      m_nChannel;
    uint32_t m_nTimeStamp;	
    int32_t  m_nInfoField2;	
    uint32_t m_nBodySize;
    uint32_t m_nBytesRead;
    RTMPChunk *m_chunk;
    char    *m_body;
  } RTMPPacket;

packet->m_headerType: 可以定義如下:
#define RTMP_PACKET_SIZE_LARGE    0
#define RTMP_PACKET_SIZE_MEDIUM   1
#define RTMP_PACKET_SIZE_SMALL    2
#define RTMP_PACKET_SIZE_MINIMUM  3
一般定位爲 RTMP_PACKET_SIZE_MEDIUM

packet->m_packetType: 音頻、視頻包類型
#define RTMP_PACKET_TYPE_AUDIO    0x08
#define RTMP_PACKET_TYPE_VIDEO    0x09
#define RTMP_PACKET_TYPE_INFO     0x12 
還有其他更多類型,但一般都是 音頻或視頻

packet->m_hasAbsTimestamp: 是否使用絕對時間戳,一般定義爲0。

packet->m_nChannel:音視頻通道號碼,音視頻不要寫錯,
#define STREAM_CHANNEL_METADATA  0x03
#define STREAM_CHANNEL_VIDEO     0x04
#define STREAM_CHANNEL_AUDIO     0x05

packet->m_nTimeStamp:時間戳
一般視頻時間戳可以從0開始計算,每幀時間戳 + 1000/fps (25fps每幀遞增25;30fps遞增33)
音頻時間戳也可以從0開始計算,48K採樣每幀遞增21;44.1K採樣每幀遞增23。

packet->m_nInfoField2 = rtmp->m_stream_id

packet->m_nBodySize:數據包長度 = NALU包長度 + 包頭長度

packet->m_nBytesRead:不用管
packet->m_chunk: 不用管
packet->m_body:包頭數據 + NALU數據,其長度爲packet->m_nBodySize。


2. 發送視頻SPS,PPS包

int rtmp_write_video_header(RTMP *rtmp) {
    int size = 10 + 3 + sps_pps.sps_length + 3 + sps_pps.pps_length;

    RTMPPacket packet;
    RTMPPacket_Reset(&packet);
    RTMPPacket_Alloc(&packet, size);
    unsigned char *body = packet.m_body;

    int i = 0;
    body[i++] = 0x17;
    body[i++] = 0x00;

    body[i++] = 0x00;
    body[i++] = 0x00;
    body[i++] = 0x00;

    //AVCDecoderConfigurationRecord
    body[i++] = 0x01;
    body[i++] = sps_pps.sps_data[1];
    body[i++] = sps_pps.sps_data[2];
    body[i++] = sps_pps.sps_data[3];
    body[i++] = 0xFF;

    //sps
    body[i++] = 0xE1;
    body[i++] = (sps_pps.sps_length >> 8) & 0xFF;
    body[i++] =  sps_pps.sps_length       & 0xFF;
    memcpy(&body[i], sps_pps.sps_data, sps_pps.sps_length);  // 複製sps數據
    i +=  sps_pps.sps_length;

    // pps
    body[i++] = 0x01;
    body[i++] = (sps_pps.pps_length >> 8) & 0xFF;
    body[i++] =  sps_pps.pps_length       & 0xFF;
    memcpy(&body[i], sps_pps.pps_data, sps_pps.pps_length);  // 複製pps數據
    i +=  sps_pps.pps_length;

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

    //調用發送接口
    int nRet = RTMP_SendPacket(rtmp, packet, TRUE);
    RTMPPacket_Free(&packet);
    return nRet;
}

在最開始發送一次即可。

3. 發送視頻數據包

<pre name="code" class="cpp">int rtmp_write_video_frame(RTMP *rtmp, const BYTE *nalu_data, int slice_count, int nTimestamp){
    int n;
    int nRet =0;
    int size = 0;
    for(n=0; n<slice_count; n++)
        size += (slice[n].slice_length + 4);  // 每個slice數據前要加4字節的slice長度

    size += 5;                                // 5字節頭
    RTMPPacket packet;
    RTMPPacket_Reset(&packet);
    RTMPPacket_Alloc(&packet, size);
    unsigned char *body = packet.m_body;

    // NALU size
    int i=0;
    body[0] = ((slice[0].slice_type&0x1F) == 0x5) ? 0x17 : 0x27;
    body[1] = 0x01;  // AVC NALU
    body[2] = 0x00;
    body[3] = 0x00;
    body[4] = 0x00;
    i=5;

    //包體內存
    packet.m_headerType  = RTMP_PACKET_SIZE_LARGE;
    packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet.m_hasAbsTimestamp = 0;
    packet.m_nChannel   = STREAM_CHANNEL_VIDEO;
    packet.m_nTimeStamp = nTimestamp;
    packet.m_nInfoField2 = rtmp->m_stream_id;
    packet.m_nBodySize  = size;

    for(n=0; n<slice_count; n++) {
        int len = slice[n].slice_length;
        int_to_bytes(len, &body[i], 4);
        i+=4;
        memcpy(&body[i], nalu_data + slice[n].start_address, len);
        i += len;
    }

    //發送
    if (RTMP_IsConnected(rtmp)){
        nRet = RTMP_SendPacket(rtmp, &packet, TRUE); //TRUE爲放進發送隊列,FALSE是不放進發送隊列,直接發送
        if(nRet <= 0) {
            printf("RTMP_SendPacket Error\n");
        }
    }

    //釋放內存
    RTMPPacket_Free(&packet);
    return nRet;
}


4. 發送音頻頭包

int rtmp_write_audio_header(RTMP *rtmp){
    RTMPPacket packet;
    RTMPPacket_Reset(&packet);
    RTMPPacket_Alloc(&packet, 4);

    packet.m_body[0] = 0xAF;  // MP3 AAC format 48000Hz
    packet.m_body[1] = 0x00;
    packet.m_body[2] = 0x11;
    packet.m_body[3] = 0x90;//0x10修改爲0x90,2016-1-19

    packet.m_headerType  = RTMP_PACKET_SIZE_MEDIUM;
    packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;
    packet.m_hasAbsTimestamp = 0;
    packet.m_nChannel   = STREAM_CHANNEL_AUDIO;
    packet.m_nTimeStamp = 0;
    packet.m_nInfoField2 = rtmp->m_stream_id;
    packet.m_nBodySize  = 4;

    //調用發送接口
    int nRet = RTMP_SendPacket(rtmp, &packet, TRUE);
    RTMPPacket_Free(&packet);//釋放內存
    return nRet;
}
在最開始發送一次即可。


5. 發送音頻包

int rtmp_write_audio_data(RTMP *rtmp, const BYTE *nalu_data, int nalu_size, int audio_TimeStamp){
    //rtmp包結構
    int size = nalu_size + 2;
    RTMPPacket packet;
    RTMPPacket_Reset(&packet);
    RTMPPacket_Alloc(&packet, size);

    int i=0;
    // MP3 AAC format 48000Hz
    packet.m_body[i++] = 0xAF;
    packet.m_body[i++] = 0x01;
    memcpy(&packet.m_body[i], nalu_data, nalu_size);

    packet.m_headerType  = RTMP_PACKET_SIZE_MEDIUM;
    packet.m_packetType = RTMP_PACKET_TYPE_AUDIO;
    packet.m_hasAbsTimestamp = 0;
    packet.m_nChannel   = STREAM_CHANNEL_AUDIO;
    packet.m_nTimeStamp = audio_TimeStamp;
    packet.m_nInfoField2 = rtmp->m_stream_id;
    packet.m_nBodySize  = size;

    //調用發送接口
    int nRet = RTMP_SendPacket(rtmp, &packet, TRUE);
    RTMPPacket_Free(&packet);//釋放內存
    return nRet;
}

6. 本地存儲packet

int RTMP_SendPacket_wrap(RTMP_ *rtmp, RTMPPacket *packet, int queue){
#if OUTPUT_FLV
    uint8_t data[] = {0x09,
        0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00 };
    if(packet->m_packetType == RTMP_PACKET_TYPE_VIDEO){
        data[0] = 0x09;
    } else if(packet->m_packetType == RTMP_PACKET_TYPE_AUDIO){
        data[0] = 0x08;
    }
    int len = 0;
    int_to_bytes(packet->m_nBodySize,  &data[1], 3);  // 3字節 包內容長度
    int_to_bytes(packet->m_nTimeStamp, &data[4], 3);  // 3字節 時間戳,沒用第4字節,太長的文件會溢出
    len += fwrite(data, 1, 11, flv_fp);               // 寫入包頭11字節
    len += fwrite(packet->m_body, 1, packet->m_nBodySize, flv_fp); // 寫入數據包體
    int_to_bytes(len, &data[0], 4);                   // 寫入的總字節長度
    fwrite(data, 1, 4, flv_fp);                       // 等於前Tag總長度
#endif
    return RTMP_SendPacket(rtmp, packet, queue);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章