1. 程式人生 > >HLS學習(六)HLSDownloader原始碼分析(5)解析Media PlayList

HLS學習(六)HLSDownloader原始碼分析(5)解析Media PlayList

解析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;
}