OSS MEDIA C SDK 客户端部分支持将接收到的H.264、AAC格式封装为TS、M3U8格式,然后写到OSS上,用户通过对应的m3u8地址就可以欣赏视频音频了。
接口
HLS相关基础接口都位于oss_media_hls.h中,目前提供的接口有:
- oss_media_hls_open
- oss_media_hls_write_frame
- oss_media_hls_begin_m3u8
- oss_media_hls_write_m3u8
- oss_media_hls_end_m3u8
- oss_media_hls_flush
- oss_media_hls_close
下面详细介绍各个接口的功能和注意事项。
- 基础结构体介绍
/** * OSS MEDIA HLS FRAME的元数据 */ typedef struct oss_media_hls_frame_s { stream_type_t stream_type; frame_type_t frame_type; uint64_t pts; uint64_t dts; uint32_t continuity_counter; uint8_t key:1; uint8_t *pos; uint8_t *end; } oss_media_hls_frame_t; /** * OSS MEDIA HLS的描述信息 */ typedef struct oss_media_hls_options_s { uint16_t video_pid; uint16_t audio_pid; uint32_t hls_delay_ms; uint8_t encrypt:1; char key[OSS_MEDIA_HLS_ENCRYPT_KEY_SIZE]; file_handler_fn_t handler_func; uint16_t pat_interval_frame_count; } oss_media_hls_options_t; /** * OSS MEDIA HLS FILE的描述信息 */ typedef struct oss_media_hls_file_s { oss_media_file_t *file; oss_media_hls_buf_t *buffer; oss_media_hls_options_t options; int64_t frame_count; } oss_media_hls_file_t;
说明- stream_type:流类型,目前支持st_h264和st_aac。
- frame_type:帧类型,目前支持ft_non_idr、ft_idr、ft_sei、ft_sps、ft_pps、ft_aud等。
- pts:显示时间戳。
- dts:解码时间戳。
- continuity_counter:递增计数器,从0-15,起始值不一定取0,但必须是连续的。
- key:是否为关键帧。
- pos:当前帧数据的起始位置(包含)。
- end:当前帧数据的结束位置(不包含)。
- video_pid:视频的pid。
- audio_pid:音频的pid。
- hls_delay_ms:延迟毫秒数。
- encrypt:是否使用AES-128加密。目前暂不支持使用AES-128加密。
- key:使用加密时的密钥。目前暂不支持使用加密时的密钥。
- handler_func:文件操作回调函数。
- pat_interval_frame_count:隔多少帧插入一个pat、mpt表。
- 打开HLS文件
/** * @brief 打开一个OSS HLS文件 * @param[in] bucket_name oss上存储文件的存储空间名称 * @param[in] object_key oss上的文件名称 * @param[in] auth_func 授权函数,设置access_key_id/access_key_secret等 * @return: * 返回非NULL时成功,否则失败 */ oss_media_hls_file_t* oss_media_hls_open(char *bucket_name, char *object_key, auth_fn_t auth_func);
说明 示例代码请参考 GitHub。 - 关闭HLS文件
/** * @brief 关闭OSS HLS文件 */ int oss_media_hls_close(oss_media_hls_file_t *file);
说明 示例代码请参考 GitHub 。 - 写HLS文件
/** * @brief 写H.264或者AAC的一帧数据到oss上 * @param[in] frame h.264或者aac格式的一帧数据 * @param[out] file hls file * @return: * 返回0时表示成功 * 否则, 表示出现了错误 */ int oss_media_hls_write_frame(oss_media_hls_frame_t *frame, oss_media_hls_file_t *file);
示例程序:
static void write_frame(oss_media_hls_file_t *file) { oss_media_hls_frame_t frame; FILE *file_h264; uint8_t *buf_h264; int len_h264, i; int cur_pos = -1; int last_pos = -1; int video_frame_rate = 30; int max_size = 10 * 1024 * 1024; char *h264_file_name = "/path/to/example.h264"; /* 读取H.264文件 */ buf_h264 = calloc(max_size, 1); file_h264 = fopen(h264_file_name, "r"); len_h264 = fread(buf_h264, 1, max_size, file_h264); /* 初始化frame结构体 */ frame.stream_type = st_h264; frame.pts = 0; frame.continuity_counter = 1; frame.key = 1; /* 遍历H.264的数据,抽取出每帧数据,然后写入oss */ for (i = 0; i < len_h264; i++) { /* 判断当前位置是否下一帧数据的开头,也就是当前帧的结尾 */ if ((buf_h264[i] & 0x0F)==0x00 && buf_h264[i+1]==0x00 && buf_h264[i+2]==0x00 && buf_h264[i+3]==0x01) { cur_pos = i; } /* 如果获取到完整的一帧数据,就调用接口转为HLS格式后写入OSS */ if (last_pos != -1 && cur_pos > last_pos) { frame.pts += 90000 / video_frame_rate; frame.dts = frame.pts; frame.pos = buf_h264 + last_pos; frame.end = buf_h264 + cur_pos; oss_media_hls_write_frame(&frame, file); } last_pos = cur_pos; } /* 关闭文件,释放资源 */ fclose(file_h264); free(buf_h264); }
说明- 示例代码请参考GitHub
- 如果H.264的数据中缺少Access Unit Delimiter NALs(00 00 00 01 09 xx)时,需要添加这个NAL,否则无法在ipad,iphone,safari上播放。
- H.264的帧是通过0xX0,0x00,0x00,0x01分隔的;AAC的帧是通过0xFF,0x0X分隔的。
- 当前帧为关键帧时,frame.key需要设置为1。
- 写M3U8文件
/** * @brief 写M3U8文件的头部数据 * @param[in] max_duration TS文件最长持续时间 * @param[in] sequence TS文件起始编号 * @param[out] file m3u8 file * @return: * 返回0时表示成功 * 否则, 返回-1时表示出现了错误 */ void oss_media_hls_begin_m3u8(int32_t max_duration, int32_t sequence, oss_media_hls_file_t *file); /** * @brief 写M3U8文件数据 * @param[in] size m3u8 item个数 * @param[in] m3u8 m3u8 item的详细数据 * @param[out] file m3u8 file * @return: * 返回0时表示成功 * 否则, 返回-1时表示出现了错误 */ int oss_media_hls_write_m3u8(int size, oss_media_hls_m3u8_info_t m3u8[], oss_media_hls_file_t *file); /** * @brief 写M3U8文件的结束符等数据 * @param[out] file m3u8 file */ void oss_media_hls_end_m3u8(oss_media_hls_file_t *file);
示例程序:
static void write_m3u8() { char *bucket_name; char *key; oss_media_hls_file_t *file; bucket_name = "<your bucket name>"; key = "<your m3u8 file name>"; /* 打开一个HLS文件用来写M3U8格式的数据,文件名必须以.m3u8结尾 */ file = oss_media_hls_open(bucket_name, key, auth_func); if (file == NULL) { printf("open m3u8 file[%s] failed.", key); return; } /* 构造3个ts格式文件的信息 */ oss_media_hls_m3u8_info_t m3u8[3]; m3u8[0].duration = 9; memcpy(m3u8[0].url, "video-0.ts", strlen("video-0.ts")); m3u8[1].duration = 10; memcpy(m3u8[1].url, "video-1.ts", strlen("video-1.ts")); /* 写入M3U8文件 oss_media_hls_begin_m3u8(10, 0, file); oss_media_hls_write_m3u8(2, m3u8, file); oss_media_hls_end_m3u8(file); /* 关闭HLS文件 */ oss_media_hls_close(file); printf("write m3u8 to oss file succeeded\n"); }