HLS學習(六)HLSDownloader原始碼分析(5)解析Media PlayList
阿新 • • 發佈:2019-02-03
解析Media PlayList
PlayList就是m3u8檔案或者索引檔案,Media PlayList也叫媒體播放列表或者媒體索引檔案
解析Media PlayList的流程如下:
1、如果hls_media_playlist結構體中媒體資訊存在,那麼先刪除
2、如果Media PlayList檔案已經存在,那麼先刪除。沒有Master PlayList的時候,這種情況就會存在,因為前面已經下載了PlayList,這個PlayList有可能是Media PlayList,也有可能是Master PlayList
3、根據URL下載Media PlayList檔案
4、計算Media PlayList中URL的數量,每一個URL表示了一個TS視訊片段
5、分配hls_media_playlist結構體中的媒體資訊陣列的記憶體
6、解析並格式化Media PlayList中的URL
// 處理Media PlayList int handle_hls_media_playlist(struct hls_media_playlist *me,std::string user_agent) { // 加密型別 me->encryption = false; me->encryptiontype = ENC_NONE; // 先刪除媒體資訊 if(me->media_segment !=NULL){ delete [] me->media_segment; } // 如果Media PlayList已經存在(沒有Master PlayList的時候就是這種情況),那麼先釋放掉 if(me->source != NULL){ free(me->source) ; } // 根據URL獲取資料 get_data_from_url(me->url, &me->source, NULL, STRING,user_agent); // 獲取PlayList的型別 if (get_playlist_type(me->source) != MEDIA_PLAYLIST) { return 1; } // 得到Media PlayList中URL的數量,一個URL表示了一個TS視訊片段 me->count = get_link_count(me->source); me->media_segment = new hls_media_segment[me->count];//(hls_media_segment *)malloc(sizeof(struct hls_media_segment) * me->count); // 獲取Media PlayList中的URL if (media_playlist_get_links(me,user_agent)) { MSG_ERROR("Could not parse links. Exiting.\n"); return 1; } return 0; }
解析Media PlayList中的URL
// 解析Media PlayList中的URL static int media_playlist_get_links(struct hls_media_playlist *me,std::string user_agent) { int ms_init = media_playlist_get_media_sequence(me->source); int target_duration=media_playlist_get_media_target_duration(me->source); if(target_duration!=0){ me->target_duration=target_duration; } struct hls_media_segment *ms = me->media_segment; char *src = me->source; char * tmp_url = (char *)malloc(strlen(src)); for (int i = 0; i < me->count && src!=NULL; i++) { while (src != NULL) { if (*src == '\n') { src = next_line(src); continue; } if (*src == '#') { parse_playlist_tag(me, src,user_agent); // 下載金鑰 src = next_line(src); continue; } if (*src == '\0') { goto finish; } if (sscanf(src, "%[^\n]", tmp_url) == 1) { ms[i].sequence_number = i + ms_init; extend_url(&tmp_url, me->url); // 格式化URL ms[i].url=std::string(tmp_url); // 如果需要解密 if (me->encryptiontype == ENC_AES128 || me->encryptiontype == ENC_AES_SAMPLE) { memcpy(ms[i].enc_aes.key_value, me->enc_aes.key_value, KEYLEN); if (me->enc_aes.iv_is_static == false) { char iv_str[STRLEN_BTS(KEYLEN)]; snprintf(iv_str, STRLEN_BTS(KEYLEN), "%032x\n", ms[i].sequence_number); uint8_t *iv_bin = (uint8_t *)malloc(KEYLEN); str_to_bin(iv_bin, iv_str, KEYLEN); memcpy(ms[i].enc_aes.iv_value, iv_bin, KEYLEN); free(iv_bin); } } src = next_line(src); break; } } } finish: // Extend the individual urls. // for (int i = 0; i < me->count; i++) { // extend_url(&ms[i].url, me->url); // } free(tmp_url); return 0; }
計算TS視訊片段的序號
// 計算TS視訊片段的序號
static int media_playlist_get_media_sequence(char *source)
{
int j = 0;
// 序號欄位用#EXT-X-MEDIA-SEQUENCE來標識
char *p_media_sequence = strstr(source, "#EXT-X-MEDIA-SEQUENCE:");
if (p_media_sequence) {
if (sscanf(p_media_sequence, "#EXT-X-MEDIA-SEQUENCE:%d", &j) != 1) {
MSG_ERROR("Could not read EXT-X-MEDIA-SEQUENCE\n");
return 0;
}
}
return j;
}
計算TS視訊片段的最大時長
所有的視訊片段的時長都不能超過它
// 計算TS視訊片段的最大時長,這些視訊片段的時長都不能超過它
static int media_playlist_get_media_target_duration(char *source)
{
int j = 1;
// 最大時長欄位使用#EXT-X-TARGETDURATION來標識
char *p_media_sequence = strstr(source, "#EXT-X-TARGETDURATION:");
if (p_media_sequence) {
if (sscanf(p_media_sequence, "#EXT-X-TARGETDURATION:%d", &j) != 1) {
MSG_ERROR("Could not read EXT-X-MEDIA-TARGETDURATION\n");
return 0;
}
}
return j;
}
下載金鑰
判斷是否需要解密處理,如果需要,那麼進行解密,然後根據解析出來的uri,下載key-file(即金鑰)// 判斷是否需要解密處理,如果需要,那麼進行解密,然後根據解析出來的uri,下載key-file(即金鑰)
static int parse_playlist_tag(struct hls_media_playlist *me, char *tag,std::string user_agent)
{
int enc_type;
// 加密解密處理
if (!strncmp(tag, "#EXT-X-KEY:METHOD=AES-128", 25)) {
enc_type = ENC_AES128;
} else if (!strncmp(tag, "#EXT-X-KEY:METHOD=SAMPLE-AES", 28)) {
enc_type = ENC_AES_SAMPLE;
} else {
return 1; // 不需要解密
}
me->encryption = true;
me->encryptiontype = enc_type;
me->enc_aes.iv_is_static = false;
char *link_to_key = (char *)malloc(strlen(tag) + strlen(me->url) + 10);
char iv_str[STRLEN_BTS(KEYLEN)];
if (sscanf(tag, "#EXT-X-KEY:METHOD=AES-128,URI=\"%[^\"]\",IV=0x%s", link_to_key, iv_str) == 2 ||
sscanf(tag, "#EXT-X-KEY:METHOD=SAMPLE-AES,URI=\"%[^\"]\",IV=0x%s", link_to_key, iv_str) == 2)
{
uint8_t *iv_bin = (uint8_t *)malloc(KEYLEN);
str_to_bin(iv_bin, iv_str, KEYLEN);
memcpy(me->enc_aes.iv_value, iv_bin, KEYLEN);
me->enc_aes.iv_is_static = true;
free(iv_bin);
}
extend_url(&link_to_key, me->url);
uint8_t *decrypt;
// 下載金鑰
if (get_data_from_url(link_to_key, NULL, &decrypt, BINKEY,user_agent) == 0) {
MSG_ERROR("Getting key-file failed.\n");
free(link_to_key);
return 1;
}
memcpy(me->enc_aes.key_value, decrypt, KEYLEN);
free(link_to_key);
free(decrypt);
return 0;
}