使用librtmp库发送音频,视频数据包

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. 发送视频数据包

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);  
}  

 

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值