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加密,目前暫不支援
- 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");
}
註: