JoJo的个人博客

记录精彩的程序人生

目录
音视频系统入门11之实战视频推流
/    

音视频系统入门11之实战视频推流

FLV协议

实战视频推流

推流具体步骤

  • 解析FLV文件
  • 获取音视频数据
  • 利用librtmp进行推流

librtmp的基本用法

  • 初始化RTMP对象
  • 设置RTMP URL
  • 连接流媒体服务器
  • 发送数据包

mac环境下rtmp库安装

1.安装
brew install rtmpdump
2.报错Package libssl was not found in the pkg-config search path.
设置openssl的PKG_CONFIG_PATH
export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig

库的引入与接口的确定

库路径通过pkg-config --libs --cflags librtmp命令查看

  1. 添加libcrypto.dyliblibssl.dyliblibrtmp.dyliblibz.tdb

  2. Build Settings中添加header

    /usr/local/Cellar/openssl@1.1/1.1.1h/include

    /usr/local/Cellar/rtmpdump/2.4+20151223_1/include

打开flv文件

static FILE* open_flv(char *flv_name) {
    FILE *fp = NULL;
    
    fp = fopen(flv_name, "rb");
    if (!fp) {
        printf("Failed to open flv: %s", flv_name);
        return NULL;
    }
    
    fseek(fp, 9, SEEK_SET); //跳过 9 字节的 FLV Header
    fseek(fp, 4, SEEK_CUR); //跳过 4 字节的PreTagSize
    
    return fp;
}

连接rtmp服务器

static RTMP* connect_rtmp_server(char* rtmpaddr) {
    RTMP *rtmp = NULL;
    
    //1. 创建RTMP对象
    rtmp = RTMP_Alloc();
    if(!rtmp) {
        printf("NO Memory, Failed to alloc RTMP object!\n");
        goto __ERROR;
    }
    RTMP_Init(rtmp);
    
    //2.先设置RTMP服务器地址,以及设置连接超时时间
    rtmp->Link.timeout = 10;
    RTMP_SetupURL(rtmp, rtmpaddr);
    
    //3. 建立连接
    if(!RTMP_Connect(rtmp, NULL)) {
        printf("Failed to Connect RTMP Server!\n");
        goto __ERROR;
    }
    
    //4. 设置是推流还是拉流
    //如果设置了该开关,就是推流(publish),如果未设置就是拉流(play)
    RTMP_EnableWrite(rtmp);
    
    //5. create rtmp
    RTMP_ConnectStream(rtmp, 0);
    
    return rtmp;
    
__ERROR:
    //释放资源
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }
    return NULL;
}

连接rtmp服务器

static RTMP* connect_rtmp_server(char* rtmpaddr) {
    RTMP *rtmp = NULL;
    
    //1. 创建RTMP对象
    rtmp = RTMP_Alloc();
    if(!rtmp) {
        printf("NO Memory, Failed to alloc RTMP object!\n");
        goto __ERROR;
    }
    RTMP_Init(rtmp);
    
    //2.先设置RTMP服务器地址,以及设置连接超时时间
    rtmp->Link.timeout = 10;
    RTMP_SetupURL(rtmp, rtmpaddr);
    
    //3. 建立连接
    if(!RTMP_Connect(rtmp, NULL)) {
        printf("Failed to Connect RTMP Server!\n");
        goto __ERROR;
    }
    
    //4. 设置是推流还是拉流
    //如果设置了该开关,就是推流(publish),如果未设置就是拉流(play)
    RTMP_EnableWrite(rtmp);
    
    //5. create rtmp
    RTMP_ConnectStream(rtmp, 0);
    
    return rtmp;
    
__ERROR:
    //释放资源
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }
    return NULL;
}

向流媒体服务器推流

创建 RTMPPacket 对象
//分配RTMP packet空间
static RTMPPacket* alloc_packet() {
    RTMPPacket *packet = NULL;
    packet = (RTMPPacket*) malloc(sizeof(RTMPPacket));
    if (!packet) {
        printf("No memory, Failed to alloc RTMPPacket!\n");
        return NULL;
    }
    
    if(!RTMPPacket_Alloc(packet, 64 * 1024)) {
    }
    
    RTMPPacket_Reset(packet);
    
    packet->m_hasAbsTimestamp = 0;
    packet->m_nChannel = 0x4;
    return packet;
}
从flv文件中读取数据
static int read_u8(FILE* fp, unsigned int *u8) {
    unsigned int tmp;
    if(fread(&tmp, 11, fp) != 1){
       printf("Failed to read_u8!\n");
       return -1;
    }

    *u8 = tmp & 0xFF;

    return 0 ;
}

/*
...| | | | | ...
...x1, x2, x3, x4, x5, xxxxx...
...|x1|x2|x3| | ...
...|x3|x2|x1| | ...
*/

static int read_u24(FILE* fp, unsigned int *u24) {
    unsigned int tmp;
    if(fread(&tmp, 13, fp) != 3){
        printf("Failed to read_u24!\n");
        return -1;
    }
    *u24 = ((tmp >> 16) & 0xFF) | ((tmp << 16) & 0xFF0000) | (tmp & 0xFF00);
    return 0;
}

/*
...| | | | | ...
...x1, x2, x3, x4, x5, xxxxx...
...|x1|x2|x3|x4| ...
...|x4|x3|x2|x1|...
*/

static int read_u32(FILE *fp, unsigned int *u32){
    unsigned int tmp;
    if(fread(&tmp, 14, fp) != 4){
        printf("Failed to read_u32!\n");
        return -1;
    }
    *u32 = ((tmp >> 24) & 0xFF) | ((tmp >> 8) & 0xFF00) | \
           ((tmp << 8) & 0xFF0000) | ((tmp << 24) & 0xFF000000);
    
    return 0;
}


/**
 * @param[in] fp flv file
 * @param[out] packet the data from flv
 * @return 0: success, -1: failure
 */

static int read_data(FILE *fp, RTMPPacket **packet) {
    /*
    * tag header
    * 第一个字节 TT(Tag Type), 0x08 音频,0x09 视频, 0x12 script
    * 2-4, Tag body 的长度, PreTagSize - Tag Header size
    * 5-7, 时间戳,单位是毫秒; script 它的时间戳是0
    * 第8个字节,扩展时间戳。真正时间戳结构 [扩展,时间戳] 一共是4字节。
    * 9-11, streamID, 0
    */

   
   /*
    * flv
    * flv header(9), tagpresize, tag(header+data), tagpresize
    */

    
    int ret = -1;
    size_t datasize = 0;
    
    unsigned int tt;
    unsigned int tag_data_size;
    unsigned int ts;
    unsigned int streamid;
    
    read_u8(fp, &tt);
    read_u24(fp, &tag_data_size);
    read_u32(fp, &ts);
    read_u24(fp, &streamid);
    
    datasize = fread((*packet)->m_body, 1, tag_data_size, fp);
    if(tag_data_size != datasize){
        printf("Failed to read tag body from flv, (datasize=%zu:tds=%d)\n",
               datasize,
               tag_data_size);
        goto __ERROR;
    }
    
    //设置pakcet数据
    (*packet)->m_headerType = RTMP_PACKET_SIZE_LARGE;
    (*packet)->m_nTimeStamp = ts;
    (*packet)->m_packetType = tt;
    (*packet)->m_nBodySize = tag_data_size;
    
    ret = 0;
    return ret;
  
__ERROR:
    return ret;
}
向流媒体服务器推流
static void send_data(FILE *fp, RTMP *rtmp) {
    
    //1. 创建 RTMPPacket 对象
    RTMPPacket *packet = NULL;
    packet = alloc_packet();
    
    while(1) {
        //2. 从flv文件中读取数据
        read_data(fp, &packet);
        
        //3. 判断RTMP连接是否正常
        if(!RTMP_IsConnected(rtmp)){
            printf("Disconnect.....\n");
            break;
        }
        
        //4. 发送数据
        RTMP_SendPacket(rtmp, packet, 0);
    }
    
    //release RTMP packet
    return;
}
整体过程
void publish_stream() {
    char *flv = "/Users/tianyang/Desktop/av_base/1234.flv";
    char *rtmpaddr = "rtmp://localhost/live/room";
    
    //1. 读flv文件
    FILE *fp = open_flv(flv);
    
    //2. 连接RTMP服务器
    RTMP *rtmp = connect_rtmp_server(rtmpaddr);
    
    //3. publish audio/video data
    send_data(fp, rtmp);
    
    //4. release rtmp
    
    return;
}

标题:音视频系统入门11之实战视频推流
作者:SunnySky
地址:https://www.tianyang.pub/articles/2020/11/17/1605611832317.html

评论