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