精华内容
下载资源
问答
  • P2P版M3U8解析源码2.1正式版
  • m3u8解析m3u8解析器 EXT-X-CUE-IN 尚不支持 自定义解析器 包括解析器 [removed]标记 浏览器 要求JS / AMD 执照 安装 npm install --save m3u8-parser 首选npm安装,但是Bower也可以。 bower install --...
  • 收费视频教程m3u8解析获取软件源码
  • m3u8 Python 解析器。 文献资料 加载播放列表 要将播放列表从uri,文件路径或直接从字符串加载到对象中,请使用加载/加载功能: import m3u8 playlist = m3u8 . load ( 'http://videoserver.com/playlist.m3u8' ) ...
  • m3u8直链解析源码

    2018-01-03 11:43:47
    安装教程 将ckplayer文件夹全部上传至网站空间根目录即可 接口测试:http://你的域名/ckplayer/m3u8.php?url= 播放地址要有效才能解析,失效的地址无法解析 可自行整合到其他程序中!
  • viu-m3u8-解析器 从viu.com v1.1解析并保存m3u8 特征: 解析240-1080p并保存到m3u8文件 绕过高级视频(预览模式视频) 来自带有UI的简单脚本 显示订阅和系列信息 说明: 我已经制作了带图标的小书签版本。 所以...
  • m3u8 Python 解析器。 文献资料 基本用法是从uri,文件路径或直接从字符串创建播放列表对象: import m3u8 m3u8_obj = m3u8.load('http://videoserver.com/playlist.m3u8') # this could also be an absolute ...
  • M3U8套件 去做 用Swift重写 新的功能 EXT-X版本:支持4 添加现在为每个ts资源提供的X-Key解析器支持。 添加对AVERAGE-BANDWIDTH属性的支持 安装 播客文件 在您的Podfile中指定它: pod 'M3U8Kit' 然后,运行...
  • M3U8解析器 一个nodejs应用程序,它解析m3u8播放列表并将数据作为json返回。 使用进行实际解析。 只需运行node application.js "[url to m3u8 playlist file]" ,就会在代表播放列表的stdout上输出json。 例如,在...
  • m3u8.cr:生成和解析HLS的m3u8播放列表
  • m3u8可以轻松生成和解析Apple发行的 Internet草案中定义的m3u8播放列表。 该库完全实现了HLS Internet Draft版本20。 提供从任何File,StringIO或字符串将m3u8播放列表解析为对象模型的功能。 提供将播放列表...
  • m3u8:为Crystal中的HTTP实时流(HLS)生成和解析m3u8播放列表
  • 素材描述:dplayer播放器+p2p云播,适用于m3u8视频格式解析,支持各大m3u8资源站 详细介绍 上传到服务器解压。 播放地址: 你的域名/m3u8.php?url=视频.m3u8 url后面填视频链接即可使用!
  • M3U8是简单的文本格式,并且解析库也必须很简单。 它不提供通过HTTP播放HLS或处理播放列表的方法。 因此,库功能是: 支持HLS规范,直到协议的版本5。 解析和生成主播放列表和媒体播放列表。 自动将输入流检测为...
  • m3u8播放器网页源码 p2p网页解析加速播放 人越多越快.rar
  • 你只需指定必要的 flag (u、o、n、ht) 来运行, 工具就会自动帮你解析 M3U8 文件,并将 TS 片段下载下来合并成一个文件。 功能介绍 下载和解析 M3U8 下载 TS 失败重试 (加密的同步解密) 合并 TS 片段 可以下载岛国...
  • m3u8_download ...支持m3u8格式的解析与下载,加密视频文件也可以完成下载,并可以转码为mp4视频 json保存数据 自动开始任务 下载完成合并成mp4并使用ffmpeg简单转下流 快捷表格补充 未实现进度信息
  • 介绍: 之前花了20元在淘宝买的,一直无法正常使用,卖家也不给解决,今天没事研究了一会才发现,原来是PHP...直接上传到网站根目录即可,支持m3u8和mp4格式的视频地址解析,红色部分为你的域名,蓝色部分为视频地址 ...

    介绍:

    之前花了20元在淘宝买的,一直无法正常使用,卖家也不给解决,今天没事研究了一会才发现,原来是PHP版本的问题,从5.4切换到7.3就能正常使用了,现在分享出来给大家
    直接上传到网站根目录即可,支持m3u8和mp4格式的视频地址解析,红色部分为你的域名,蓝色部分为视频地址
    http://shenqiwunet1.gz01.bdysite.com/?url=https://cn7.kankia.com/hls/20191221/c07d9e3c899887e67e852f296000ab8f/1576909865/index.m3u8
    唯一的缺陷就是在微信里无法自动播放,看到其他一些版本的都是支持自动播放的,如果有此类更好的源码也希望能够分享出来,我这个就当是抛砖引玉了~


    网盘下载地址:

    http://www.bytepan.net/sNe4IrbEA4G


    图片:


    展开全文
  • m3u8解析器 一个用于Java的简单HLS播放列表解析器。 该项目的目标是根据实现解析器和一致的Java对象模型。 该解析器与iHeartRadio的非常相似。 主要区别在于: m3u8-parser不会尝试验证播放列表。 您负责创建...
  • 下载并解析M3U8(VOD) 重试下载TS失败 解析大师播放列表 解密TS 合并TS 用法 来源 go run main.go -u=http://example.com/index.m3u8 -o=/data/example 二进制: Linux和MacOS ./m3u8 -u=...
  • m3u8-dl 一个CLI程序,用于下载m3u8播放列表中的视频,将其写入单个视频文件,然后使用ffmpeg将其转换为mp4。 在此处阅读有关m3u8的信息 依存关系 在激活virtualenv之后,使用pip install -r requirements.txt...
  • 支持下载m3u8链接或文件为mp4或ts格式,并提供丰富的命令行选项。 不支持优酷视频解密 不支持气球云视频解密 支持AES-128-CBC加密自动解密 支持多线程下载 支持下载限速 支持断点续传 支持Master List 支持直播...
  • 提供从任何File,io.Reader或字符串将m3u8播放列表解析为对象模型的功能。 提供通过String()将播放列表写入字符串的功能 母版播放列表与媒体播放列表之间的区别是自动处理的(单个播放列表类)。 (可选)该库...
  • iq.com_m3u8_parser-源码

    2021-05-03 04:56:43
    轻松抓取/解析并将m3u8内容保存到文件。 我已经将带书签的小书签版本包括在内。 只需将其导入到您的Chrome书签中 笔记: 仅适用于 。 我还没有从iqyi.com尝试过 要获得1080p,您应该具有VIP访问权限。 在单击...
  • C#HLS(m3u8)库以解析HLS流和自适应HLS流列表 职能: 直接从文件或文本中解析HLS流和自适应HLS流列表; (Todo)将HLS流下载到可播放的文件; (Todo [可能不会添加]。)创建HLS流和自适应HLS流列表(带有可选...
  • 易语言源码m3u8视频下载合并 .版本 2 .支持库 EThread 标签5.标题 = “正在解析地址” 获取m3u8资源列表 (编辑框1.内容, 数据列表) .如果 (取数组成员数 (数据列表) = 0) 标签5.标题 = “视频提取失败”...
  • 解码这一步就做一件事情,拿到播放链接,读取M3U8索引文件,解析出每一个TS文件的下载地址和时长,封装到Model中,供后面使用。 解码器ZYLM3U8Handler.h文件 #import <Foundation/Foundation.h> #import "M3U8...

    HLS下载流程

    解码
    解码这一步就做一件事情,拿到播放链接,读取M3U8索引文件,解析出每一个TS文件的下载地址和时长,封装到Model中,供后面使用。
    解码器ZYLM3U8Handler.h文件

    #import <Foundation/Foundation.h>
    #import "M3U8Playlist.h"
    @class ZYLM3U8Handler;
    @protocol ZYLM3U8HandlerDelegate <NSObject>
    /**
     * 解析M3U8连接失败
     */
    - (void)praseM3U8Finished:(ZYLM3U8Handler *)handler;
    /**
     * 解析M3U8成功
     */
    - (void)praseM3U8Failed:(ZYLM3U8Handler *)handler;
    @end
    @interface ZYLM3U8Handler : NSObject
    /**
     * 解码M3U8
     */
    - (void)praseUrl:(NSString *)urlStr;
    /**
     * 传输成功或者失败的代理
     */
    @property (weak, nonatomic)id <ZYLM3U8HandlerDelegate> delegate;
    /**
     * 存储TS片段的数组
     */
    @property (strong, nonatomic) NSMutableArray *segmentArray;
    /**
     * 打包获取的TS片段
     */
    @property (strong, nonatomic) M3U8Playlist *playList;
    /**
     * 存储原始的M3U8数据
     */
    @property (copy, nonatomic) NSString *oriM3U8Str;
    @end
    ZYLM3U8Handler.m文件
    #import "ZYLM3U8Handler.h"
    #import "M3U8SegmentModel.h"
    @implementation ZYLM3U8Handler
    #pragma mark - 解析M3U8链接
    - (void)praseUrl:(NSString *)urlStr {
        //判断是否是HTTP连接
        if (!([urlStr hasPrefix:@"http://"] || [urlStr hasPrefix:@"https://"])) {
            if (self.delegate != nil && [self.delegate respondsToSelector:@selector(praseM3U8Failed:)]) {
                [self.delegate praseM3U8Failed:self];
            }
            return;
        }
        //解析出M3U8
        NSError *error = nil;
        NSStringEncoding encoding;
        NSString *m3u8Str = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:urlStr] usedEncoding:&encoding error:&error];//这一步是耗时操作,要在子线程中进行
    self.oriM3U8Str = m3u8Str;
        /*注意1、请看代码下方注意1*/
        if (m3u8Str == nil) {
            if (self.delegate != nil && [self.delegate respondsToSelector:@selector(praseM3U8Failed:)]) {
                    [self.delegate praseM3U8Failed:self];
            }
            return;
        }
        //解析TS文件
        NSRange segmentRange = [m3u8Str rangeOfString:@"#EXTINF:"];
        if (segmentRange.location == NSNotFound) {
            //M3U8里没有TS文件
            if (self.delegate != nil && [self.delegate respondsToSelector:@selector(praseM3U8Failed:)]) {
                    [self.delegate praseM3U8Failed:self];
            }
            return;
        }
    if (self.segmentArray.count > 0) {
        [self.segmentArray removeAllObjects];
    }
        //逐个解析TS文件,并存储
        while (segmentRange.location != NSNotFound) {
            //声明一个model存储TS文件链接和时长的model
            M3U8SegmentModel *model = [[M3U8SegmentModel alloc] init];
            //读取TS片段时长
            NSRange commaRange = [m3u8Str rangeOfString:@","];
            NSString* value = [m3u8Str substringWithRange:NSMakeRange(segmentRange.location + [@"#EXTINF:" length], commaRange.location -(segmentRange.location + [@"#EXTINF:" length]))];
            model.duration = [value integerValue];
            //截取M3U8
            m3u8Str = [m3u8Str substringFromIndex:commaRange.location];
            //获取TS下载链接,这需要根据具体的M3U8获取链接,可以根据自己公司的需求
            NSRange linkRangeBegin = [m3u8Str rangeOfString:@","];
            NSRange linkRangeEnd = [m3u8Str rangeOfString:@".ts"];
            NSString* linkUrl = [m3u8Str substringWithRange:NSMakeRange(linkRangeBegin.location + 2, (linkRangeEnd.location + 3) - (linkRangeBegin.location + 2))];
            model.locationUrl = linkUrl;
            [self.segmentArray addObject:model];
            m3u8Str = [m3u8Str substringFromIndex:(linkRangeEnd.location + 3)];
            segmentRange = [m3u8Str rangeOfString:@"#EXTINF:"];
        }
        /*注意2、请看代码下方注意2*/
        //已经获取了所有TS片段,继续打包数据
        [self.playList initWithSegmentArray:self.segmentArray];
        self.playList.uuid = @"moive1";
        //到此数据TS解析成功,通过代理发送成功消息
        if (self.delegate != nil && [self.delegate respondsToSelector:@selector(praseM3U8Finished:)]) {
                [self.delegate praseM3U8Finished:self];
        }
    }
    #pragma mark - getter
    - (NSMutableArray *)segmentArray {
    if (_segmentArray == nil) {
        _segmentArray = [[NSMutableArray alloc] init];
    }
    return _segmentArray;
    }
    - (M3U8Playlist *)playList {
    if (_playList == nil) {
        _playList = [[M3U8Playlist alloc] init];
    }
    return _playList;
    }
    @end
    

    注意:
    1、下面就是解析出来的M3U8索引数据,#EXTINF:10表示的是这段TS的时长是10秒,57b3f432.ts这里表示的是每一个TS的文件名,有的M3U8这里直接是一个完成的http链接。前面说到我们要拼接处每一个TS文件的下载链接,这里应该如何拼接呢,在一开始做这里的时候,我也费解了一段时间,查阅了一些资料和博文都不靠谱,所以不建议大家根据这些不靠谱的信息拼接链接,我这里总结出来的经验是,TS文件一般都存储在.M3U8索引文件所在的路径,只需要将TS文件名替换到.M3U8索引即可,当然最靠谱的做法和你们的服务器小伙伴协商好下载路径。

    #EXTM3U
    #EXT-X-VERSION:2
    #EXT-X-MEDIA-SEQUENCE:102
    #EXT-X-TARGETDURATION:12
    #EXTINF:10,
    57b3f432.ts
    #EXTINF:12,
    57b3f43c.ts
    #EXTINF:9,
    57b3f446.ts
    

    2、M3U8Playlist是一个存储一个M3U8数据的Model,存储的是TS下载链接数组,数组的数量。uuid设置为固定的"moive1",主要是用来拼接统一的缓存路径。

    下载
    拿到每一个TS的链接就可以下载了,下载后缓存到本地。
    下载器ZYLVideoDownLoader.h文件

    #import <Foundation/Foundation.h>
    #import "M3U8Playlist.h"
        @class ZYLVideoDownLoader;
        @protocol ZYLVideoDownLoaderDelegate <NSObject>
        /**
         * 下载成功
         */
        - (void)videoDownloaderFinished:(ZYLVideoDownLoader *)videoDownloader;
        /**
         * 下载失败
         */
        - (void)videoDownloaderFailed:(ZYLVideoDownLoader *)videoDownloader;
        @end
        @interface ZYLVideoDownLoader : NSObject
        @property (strong, nonatomic) M3U8Playlist *playList;
        /**
         * 记录原始的M3U8
         */
        @property (copy, nonatomic) NSString *oriM3U8Str;
        /**
         * 下载TS数据
         */
        - (void)startDownloadVideo;
        /**
         * 储存正在下载的数组
         */
        @property (strong, nonatomic) NSMutableArray *downLoadArray;
        /**
         * 下载成功或者失败的代理
         */
        @property (weak, nonatomic) id <ZYLVideoDownLoaderDelegate> delegate;
        /**
         * 创建M3U8文件
         */
        - (void)createLocalM3U8file;
        @end
    

    下载器ZYLVideoDownLoader.m文件

    #import "ZYLVideoDownLoader.h"
    #import "M3U8SegmentModel.h"
    #import "SegmentDownloader.h"
        @interface ZYLVideoDownLoader () <SegmentDownloaderDelegate>
        @property (assign, nonatomic) NSInteger index;//记录一共多少TS文件
        @property (strong, nonatomic) NSMutableArray *downloadUrlArray;//记录所有的下载链接
        @property (assign, nonatomic) NSInteger sIndex;//记录下载成功的文件的数量
        @end
        @implementation ZYLVideoDownLoader
        -(instancetype)init {
            self = [super init];
            if (self) {
                self.index = 0;
                self.sIndex = 0;
            }
            return self;
        }
    #pragma mark - 下载TS数据
        - (void)startDownloadVideo {
            //首相检查是否存在路径
            [self checkDirectoryIsCreateM3U8:NO];
            __weak __typeof(self)weakSelf = self;
    /*注意1,请看下方注意1*/
            //将解析的数据打包成一个个独立的下载器装进数组
            [self.playList.segmentArray enumerateObjectsUsingBlock:^(M3U8SegmentModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                //检查此下载对象是否存在
                __block BOOL isE = NO;
                [weakSelf.downloadUrlArray enumerateObjectsUsingBlock:^(NSString *inObj, NSUInteger inIdx, BOOL * _Nonnull inStop) {
                    if ([inObj isEqualToString:obj.locationUrl]) {
                        //已经存在
                        isE = YES;
                        *inStop = YES;
                    } else {
                        //不存在
                        isE = NO;
                    }
                }];
                if (isE) {
                    //存在
                } else {
                    //不存在
                    NSString *fileName = [NSString stringWithFormat:@"id%ld.ts", (long)weakSelf.index];
                    SegmentDownloader *sgDownloader = [[SegmentDownloader alloc] initWithUrl:[@"http://111.206.23.22:55336/tslive/c25_ct_btv2_btvwyHD_smooth_t10/" stringByAppendingString:obj.locationUrl] andFilePath:weakSelf.playList.uuid andFileName:fileName withDuration:obj.duration withIndex:weakSelf.index];
                    sgDownloader.delegate = weakSelf;
                    [weakSelf.downLoadArray addObject:sgDownloader];
                    [weakSelf.downloadUrlArray addObject:obj.locationUrl];
                    weakSelf.index++;
                }
            }];
             /*注意2,请看下方注意2*/
            //根据新的数据更改新的playList
            __block NSMutableArray *newPlaylistArray = [[NSMutableArray alloc] init];
            [self.downLoadArray enumerateObjectsUsingBlock:^(SegmentDownloader *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                M3U8SegmentModel *model = [[M3U8SegmentModel alloc] init];
                model.duration = obj.duration;
                model.locationUrl = obj.fileName;
                model.index = obj.index;
                [newPlaylistArray addObject:model];
            }];
            if (newPlaylistArray.count > 0) {
                self.playList.segmentArray = newPlaylistArray;
            }
            //打包完成开始下载
            [self.downLoadArray enumerateObjectsUsingBlock:^(SegmentDownloader *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                obj.flag = YES;
                [obj start];
            }];
        }
    #pragma mark - 检查路径
        - (void)checkDirectoryIsCreateM3U8:(BOOL)isC {
            //创建缓存路径
            NSString *pathPrefix = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0];
            NSString *saveTo = [[pathPrefix stringByAppendingPathComponent:@"Downloads"] stringByAppendingPathComponent:self.playList.uuid];
            NSFileManager *fm = [NSFileManager defaultManager];
            //路径不存在就创建一个
            BOOL isD = [fm fileExistsAtPath:saveTo];
            if (isD) {
                //存在
            } else {
                //不存在
                BOOL isS = [fm createDirectoryAtPath:saveTo withIntermediateDirectories:YES attributes:nil error:nil];
                if (isS) {
                    NSLog(@"路径不存在创建成功");
                } else {
                    NSLog(@"路径不存在创建失败");
                }
            }
        }
    #pragma mark - SegmentDownloaderDelegate
     /*注意3,请看下方注意3*/
    #pragma mark - 数据下载成功
        - (void)segmentDownloadFinished:(SegmentDownloader *)downloader {
            //数据下载成功后再数据源中移除当前下载器
            self.sIndex++;
            if (self.sIndex >= 3) {
                //每次下载完成后都要创建M3U8文件
                [self createLocalM3U8file];
                //证明所有的TS已经下载完成
                [self.delegate videoDownloaderFinished:self];
            }
        }
    #pragma mark - 数据下载失败
        - (void)segmentDownloadFailed:(SegmentDownloader *)downloader {
            [self.delegate videoDownloaderFailed:self];
        }
    #pragma mark - 进度更新
        - (void)segmentProgress:(SegmentDownloader *)downloader TotalUnitCount:(int64_t)totalUnitCount completedUnitCount:(int64_t)completedUnitCount {
            //NSLog(@"下载进度:%f", completedUnitCount * 1.0 / totalUnitCount * 1.0);
        }
     /*注意4,请看下方注意4*/
    #pragma mark - 创建M3U8文件
                - (void)createLocalM3U8file {
            [self checkDirectoryIsCreateM3U8:YES];
            //创建M3U8的链接地址
            NSString *path = [[[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0] stringByAppendingPathComponent:@"Downloads"] stringByAppendingPathComponent:self.playList.uuid] stringByAppendingPathComponent:@"movie.m3u8"];
            //拼接M3U8链接的头部具体内容
            //NSString *header = @"#EXTM3U\n#EXT-X-VERSION:2\n#EXT-X-MEDIA-SEQUENCE:371\n#EXT-X-TARGETDURATION:12\n";
            NSString *header = [NSString stringWithFormat:@"#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-TARGETDURATION:15\n"];
            //填充M3U8数据
            __block NSString *tsStr = [[NSString alloc] init];
            [self.playList.segmentArray enumerateObjectsUsingBlock:^(M3U8SegmentModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                //文件名
                NSString *fileName = [NSString stringWithFormat:@"id%ld.ts", obj.index];
                //文件时长
                NSString* length = [NSString stringWithFormat:@"#EXTINF:%ld,\n",obj.duration];
                //拼接M3U8
                tsStr = [tsStr stringByAppendingString:[NSString stringWithFormat:@"%@%@\n", length, fileName]];
            }];
            //M3U8头部和中间拼接,到此我们完成的新的M3U8链接的拼接
            header = [header stringByAppendingString:tsStr];
    /*注意5,请看下方注意5*/
            header = [header stringByAppendingString:@"#EXT-X-ENDLIST"];
            //拼接完成,存储到本地
            NSMutableData *writer = [[NSMutableData alloc] init];
            NSFileManager *fm = [NSFileManager defaultManager];
            //判断m3u8是否存在,已经存在的话就不再重新创建
            if ([fm fileExistsAtPath:path isDirectory:nil]) {
                //存在这个链接
                NSLog(@"存在这个链接");
            } else {
                //不存在这个链接
                NSString *saveTo = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0] stringByAppendingPathComponent:@"Downloads"] stringByAppendingPathComponent:self.playList.uuid];
                BOOL isS = [fm createDirectoryAtPath:saveTo withIntermediateDirectories:YES attributes:nil error:nil];
                if (isS) {
                    NSLog(@"创建目录成功");
                } else {
                    NSLog(@"创建目录失败");
                }
            }
            [writer appendData:[header dataUsingEncoding:NSUTF8StringEncoding]];
            BOOL bSucc = [writer writeToFile:path atomically:YES];
            if (bSucc) {
                //成功
                NSLog(@"M3U8数据保存成功");
            } else {
                //失败
                NSLog(@"M3U8数据保存失败");
            }
            NSLog(@"新数据\n%@", header);
        }
    #pragma mark - 删除缓存文件
        - (void)deleteCache {
            //获取缓存路径
            NSString *pathPrefix = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) objectAtIndex:0];
            NSString *saveTo = [[pathPrefix stringByAppendingPathComponent:@"Downloads"] stringByAppendingPathComponent:@"moive1"];
            NSFileManager *fm = [NSFileManager defaultManager];
            //路径不存在就创建一个
            BOOL isD = [fm fileExistsAtPath:saveTo];
            if (isD) {
                //存在
                NSArray *deleteArray = [_downloadUrlArray subarrayWithRange:NSMakeRange(0, _downloadUrlArray.count - 20)];
                //清空当前的M3U8文件
                [deleteArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    BOOL isS = [fm removeItemAtPath:[saveTo stringByAppendingPathComponent:[NSString stringWithFormat:@"%@", obj]] error:nil];
                    if (isS) {
                        NSLog(@"多余路径存在清空成功%@", obj);
                    } else {
                        NSLog(@"多余路径存在清空失败%@", obj);
                    }
                }];
            }
        }
    #pragma mark - getter
        - (NSMutableArray *)downLoadArray {
            if (_downLoadArray == nil) {
                _downLoadArray = [[NSMutableArray alloc] init];
            }
            return _downLoadArray;
        }
        - (NSMutableArray *)downloadUrlArray {
            if (_downloadUrlArray == nil) {
                _downloadUrlArray = [[NSMutableArray alloc] init];
            }
            return _downloadUrlArray;
        }
        @end
    

    注意:

    1、这里获取到的M3U8数据包含了很多TS文件,并不会在下载器里直接下载,而是要对每一个TS文件再次封装,然后每一个封装好的数据模型单独下载;
    2、这里更新playlist的目的是为了后续创建.M3U8索引,可以暂时略过这里,到了创建索引的地方自然就懂了;
    3、这是数据下载成功的代理,由于本文使用的测试连接每一个M3U8里有3个TS文件,所以当第一次3个文件全部下载完成后告诉系在工具类下载完成,后续没下载完成一个就告诉下载工具类一次;
    4、在第一次3个TS文件下载成功和后续每有一个TS下载成功后,都会更新.M3U8索引文件,保证索引文件的更新;
    5、这里要注意,添加了#EXT-X-ENDLIST,表明这个源事HLS的点播源,当播放的时候,HLS会从头开始播放。

    展开全文
  • 它还可以获取编解码器信息,并生成一个fmp4 HLS m3u8播放列表。 您必须将正确的输出args与ffmpeg一起使用,以创建兼容的分段mp4格式,类似于以下实际示例: ffmpeg -loglevel quiet -rtsp_transport tcp -i rtsp:/...
  • 不少人都在找FFmpeg中是否有hls(m3u8)解析源码,其实是有的。就是ffmpeg/libavformat/hlsproto.c,它依赖的文件也在那个目录中。 如果要是单纯想解析HLS的话,建议参考https://github.com/winlinvip/srs-bench,...

    不少人都在找FFmpeg中是否有hls(m3u8)解析的源码,其实是有的。就是ffmpeg/libavformat/hlsproto.c,它依赖的文件也在那个目录中。

    如果要是单纯想解析HLS的话,建议参考https://github.com/winlinvip/srs-bench,这是一个http、hls、rtmp集合在一起的压测工具,里面的代码更专一,代码量也不大。

    下面是ffmpeg/libavformat/hlsproto.c

    /*
     * Apple HTTP Live Streaming Protocol Handler
     * Copyright (c) 2010 Martin Storsjo
     *
     * This file is part of FFmpeg.
     *
     * FFmpeg is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2.1 of the License, or (at your option) any later version.
     *
     * FFmpeg is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser General Public
     * License along with FFmpeg; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     */
    
    /**
     * @file
     * Apple HTTP Live Streaming Protocol Handler
     * http://tools.ietf.org/html/draft-pantos-http-live-streaming
     */
    
    #include "libavutil/avstring.h"
    #include "libavutil/time.h"
    #include "avformat.h"
    #include "internal.h"
    #include "url.h"
    #include "version.h"
    
    /*
     * An apple http stream consists of a playlist with media segment files,
     * played sequentially. There may be several playlists with the same
     * video content, in different bandwidth variants, that are played in
     * parallel (preferably only one bandwidth variant at a time). In this case,
     * the user supplied the url to a main playlist that only lists the variant
     * playlists.
     *
     * If the main playlist doesn't point at any variants, we still create
     * one anonymous toplevel variant for this, to maintain the structure.
     */
    
    struct segment {
        int duration;
        char url[MAX_URL_SIZE];
    };
    
    struct variant {
        int bandwidth;
        char url[MAX_URL_SIZE];
    };
    
    typedef struct HLSContext {
        char playlisturl[MAX_URL_SIZE];
        int target_duration;
        int start_seq_no;
        int finished;
        int n_segments;
        struct segment **segments;
        int n_variants;
        struct variant **variants;
        int cur_seq_no;
        URLContext *seg_hd;
        int64_t last_load_time;
    } HLSContext;
    
    static int read_chomp_line(AVIOContext *s, char *buf, int maxlen)
    {
        int len = ff_get_line(s, buf, maxlen);
        while (len > 0 && av_isspace(buf[len - 1]))
            buf[--len] = '\0';
        return len;
    }
    
    static void free_segment_list(HLSContext *s)
    {
        int i;
        for (i = 0; i < s->n_segments; i++)
            av_free(s->segments[i]);
        av_freep(&s->segments);
        s->n_segments = 0;
    }
    
    static void free_variant_list(HLSContext *s)
    {
        int i;
        for (i = 0; i < s->n_variants; i++)
            av_free(s->variants[i]);
        av_freep(&s->variants);
        s->n_variants = 0;
    }
    
    struct variant_info {
        char bandwidth[20];
    };
    
    static void handle_variant_args(struct variant_info *info, const char *key,
                                    int key_len, char **dest, int *dest_len)
    {
        if (!strncmp(key, "BANDWIDTH=", key_len)) {
            *dest     =        info->bandwidth;
            *dest_len = sizeof(info->bandwidth);
        }
    }
    
    static int parse_playlist(URLContext *h, const char *url)
    {
        HLSContext *s = h->priv_data;
        AVIOContext *in;
        int ret = 0, duration = 0, is_segment = 0, is_variant = 0, bandwidth = 0;
        char line[1024];
        const char *ptr;
    
        if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
                              &h->interrupt_callback, NULL)) < 0)
            return ret;
    
        read_chomp_line(in, line, sizeof(line));
        if (strcmp(line, "#EXTM3U"))
            return AVERROR_INVALIDDATA;
    
        free_segment_list(s);
        s->finished = 0;
        while (!url_feof(in)) {
            read_chomp_line(in, line, sizeof(line));
            if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) {
                struct variant_info info = {{0}};
                is_variant = 1;
                ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_variant_args,
                                   &info);
                bandwidth = atoi(info.bandwidth);
            } else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
                s->target_duration = atoi(ptr);
            } else if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
                s->start_seq_no = atoi(ptr);
            } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
                s->finished = 1;
            } else if (av_strstart(line, "#EXTINF:", &ptr)) {
                is_segment = 1;
                duration = atoi(ptr);
            } else if (av_strstart(line, "#", NULL)) {
                continue;
            } else if (line[0]) {
                if (is_segment) {
                    struct segment *seg = av_malloc(sizeof(struct segment));
                    if (!seg) {
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                    seg->duration = duration;
                    ff_make_absolute_url(seg->url, sizeof(seg->url), url, line);
                    dynarray_add(&s->segments, &s->n_segments, seg);
                    is_segment = 0;
                } else if (is_variant) {
                    struct variant *var = av_malloc(sizeof(struct variant));
                    if (!var) {
                        ret = AVERROR(ENOMEM);
                        goto fail;
                    }
                    var->bandwidth = bandwidth;
                    ff_make_absolute_url(var->url, sizeof(var->url), url, line);
                    dynarray_add(&s->variants, &s->n_variants, var);
                    is_variant = 0;
                }
            }
        }
        s->last_load_time = av_gettime();
    
    fail:
        avio_close(in);
        return ret;
    }
    
    static int hls_close(URLContext *h)
    {
        HLSContext *s = h->priv_data;
    
        free_segment_list(s);
        free_variant_list(s);
        ffurl_close(s->seg_hd);
        return 0;
    }
    
    static int hls_open(URLContext *h, const char *uri, int flags)
    {
        HLSContext *s = h->priv_data;
        int ret, i;
        const char *nested_url;
    
        if (flags & AVIO_FLAG_WRITE)
            return AVERROR(ENOSYS);
    
        h->is_streamed = 1;
    
        if (av_strstart(uri, "hls+", &nested_url)) {
            av_strlcpy(s->playlisturl, nested_url, sizeof(s->playlisturl));
        } else if (av_strstart(uri, "hls://", &nested_url)) {
            av_log(h, AV_LOG_ERROR,
                   "No nested protocol specified. Specify e.g. hls+http://%s\n",
                   nested_url);
            ret = AVERROR(EINVAL);
            goto fail;
        } else {
            av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri);
            ret = AVERROR(EINVAL);
            goto fail;
        }
        av_log(h, AV_LOG_WARNING,
               "Using the hls protocol is discouraged, please try using the "
               "hls demuxer instead. The hls demuxer should be more complete "
               "and work as well as the protocol implementation. (If not, "
               "please report it.) To use the demuxer, simply use %s as url.\n",
               s->playlisturl);
    
        if ((ret = parse_playlist(h, s->playlisturl)) < 0)
            goto fail;
    
        if (s->n_segments == 0 && s->n_variants > 0) {
            int max_bandwidth = 0, maxvar = -1;
            for (i = 0; i < s->n_variants; i++) {
                if (s->variants[i]->bandwidth > max_bandwidth || i == 0) {
                    max_bandwidth = s->variants[i]->bandwidth;
                    maxvar = i;
                }
            }
            av_strlcpy(s->playlisturl, s->variants[maxvar]->url,
                       sizeof(s->playlisturl));
            if ((ret = parse_playlist(h, s->playlisturl)) < 0)
                goto fail;
        }
    
        if (s->n_segments == 0) {
            av_log(h, AV_LOG_WARNING, "Empty playlist\n");
            ret = AVERROR(EIO);
            goto fail;
        }
        s->cur_seq_no = s->start_seq_no;
        if (!s->finished && s->n_segments >= 3)
            s->cur_seq_no = s->start_seq_no + s->n_segments - 3;
    
        return 0;
    
    fail:
        hls_close(h);
        return ret;
    }
    
    static int hls_read(URLContext *h, uint8_t *buf, int size)
    {
        HLSContext *s = h->priv_data;
        const char *url;
        int ret;
        int64_t reload_interval;
    
    start:
        if (s->seg_hd) {
            ret = ffurl_read(s->seg_hd, buf, size);
            if (ret > 0)
                return ret;
        }
        if (s->seg_hd) {
            ffurl_close(s->seg_hd);
            s->seg_hd = NULL;
            s->cur_seq_no++;
        }
        reload_interval = s->n_segments > 0 ?
                          s->segments[s->n_segments - 1]->duration :
                          s->target_duration;
        reload_interval *= 1000000;
    retry:
        if (!s->finished) {
            int64_t now = av_gettime();
            if (now - s->last_load_time >= reload_interval) {
                if ((ret = parse_playlist(h, s->playlisturl)) < 0)
                    return ret;
                /* If we need to reload the playlist again below (if
                 * there's still no more segments), switch to a reload
                 * interval of half the target duration. */
                reload_interval = s->target_duration * 500000LL;
            }
        }
        if (s->cur_seq_no < s->start_seq_no) {
            av_log(h, AV_LOG_WARNING,
                   "skipping %d segments ahead, expired from playlist\n",
                   s->start_seq_no - s->cur_seq_no);
            s->cur_seq_no = s->start_seq_no;
        }
        if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
            if (s->finished)
                return AVERROR_EOF;
            while (av_gettime() - s->last_load_time < reload_interval) {
                if (ff_check_interrupt(&h->interrupt_callback))
                    return AVERROR_EXIT;
                av_usleep(100*1000);
            }
            goto retry;
        }
        url = s->segments[s->cur_seq_no - s->start_seq_no]->url,
        av_log(h, AV_LOG_DEBUG, "opening %s\n", url);
        ret = ffurl_open(&s->seg_hd, url, AVIO_FLAG_READ,
                         &h->interrupt_callback, NULL);
        if (ret < 0) {
            if (ff_check_interrupt(&h->interrupt_callback))
                return AVERROR_EXIT;
            av_log(h, AV_LOG_WARNING, "Unable to open %s\n", url);
            s->cur_seq_no++;
            goto retry;
        }
        goto start;
    }
    
    URLProtocol ff_hls_protocol = {
        .name           = "hls",
        .url_open       = hls_open,
        .url_read       = hls_read,
        .url_close      = hls_close,
        .flags          = URL_PROTOCOL_FLAG_NESTED_SCHEME,
        .priv_data_size = sizeof(HLSContext),
    };

    自己用的到,先做个记录,到时候整理吧。

    转载于:https://www.cnblogs.com/bugutian/p/5507659.html

    展开全文
  • 浏览器抓取真实直播源地址(纯前端JS解析源码) 网上搜索各种平台的直播源地址都是满天飞,但是经常会有失效的时候,因为官方也会定期的升级系统修改各种参数或链接让直播源不能永久,所以敝人一直崇尚的是 授人以鱼...

    浏览器抓取真实直播源地址(纯前端JS解析)

    网上搜索各种平台的直播源地址都是满天飞,但是经常会有失效的时候,因为官方也会定期的升级系统修改各种参数或链接让直播源不能永久,所以敝人一直崇尚的是 授人以鱼不如授人以渔,与其给直播源别人,不如教大家如何去爬取直播源,就算失效了也不怕。
    在这里插入图片描述

    0. 前言

    本人业余时间喜欢用虎牙看直播,所以第一个便是想到如何抓取虎牙的直播源。
    在抓取之前,需要了解视频直播源的分类和区别,可以自行了解hls,flv,m3u8等知识。

    Tips: 本教程只是教大家如何利用前端调试技巧和爬虫基本操作,不作为商业用途,各位童鞋耗子尾汁。

    1. 浏览器抓取流程

    首选打开虎牙官网,随便找个直播间:https://m.huya.com/949527,这里是使用的手机端的网页(因为手机端的简单)
    随便看了下,没有ajax请求,那么地址定是随页面带进来了,现在大部分直播网页都是SSR(服务器端渲染),所以只能去页面源代码找找:
    好家伙!直接就找到了一个很想地址的东西 liveLineUrl,是一个m3u8的地址:
    在这里插入图片描述

    在线m3u8播放测试网站:https://www.m3u8play.com/

    这个网站可以测试播放源是不是好的,来!试一下!
    在这里插入图片描述
    就很完美!
    在这里插入图片描述
    但是就这么简单的吗?
    在这里插入图片描述
    我又试了一下我经常看的【一起看】的直播间,来看看电影啥的,结果:
    在这里插入图片描述
    在这里插入图片描述
    这是咋回事。。。然后对比下前后两个链接发现了问题,下面是【一起看】的链接:
    在这里插入图片描述
    然后想到 liveLineUrl 这个参数不是全局变量吗,控制台打印看一下,再仔细对比发现参数变了有个fm参数已经变成了seqid
    在这里插入图片描述
    先试下控制台打印的能不能播放:
    在这里插入图片描述
    行,司马懿出来了,现在只用分析如何破解参数即可。

    2. 参数解析

    Ctrl + Shift + F 搜索 liveLineUrl , 然后找到这里处理urljs,打个断点调试一下,看看怎么处理的:
    在这里插入图片描述
    断点进入 Object(m.default)(window.liveLineUrl) 可以看到这里就是处理参数的地方,最后返回的就是解析后的参数字符串:
    在这里插入图片描述
    我整理了下解析函数,重新实现了一下:

    function parseUrl(url){
        let params = url.split("?")[1];
        params = params.split("&");
        let paramsObj = {};
        for (let i = 0; i < params.length; i++) {
            let item = params[i].split("=");
            2 === item.length && (paramsObj[item[0]] = item[1])
        }
    
        let mainUrl = url.split("?")[0];
        let r = mainUrl.split("/");
        let streamName = r[r.length - 1].replace(/.(flv|m3u8)/g, "");
        let {fm: fm, wsTime: wsTime, wsSecret: u, ...others} = paramsObj;
        let fmParse = Base64.decode(decodeURIComponent(fm));
        let p = fmParse.split("_")[0];
        let time = parseInt(1e4 * (new Date).getTime() + 1e4 * Math.random());
        let newWsSecret = `${p}_0_${streamName}_${time}_${wsTime}`;
        newWsSecret = md5.hex(newWsSecret);
        let y = "";
        Object.keys(others).forEach(e=>{
            y += `&${e}=${others[e]}`
        });
        return `${mainUrl}?wsSecret=${newWsSecret}&wsTime=${wsTime}&u=0&seqid=${time}${y}`;
    }
    

    其中用到了Base64MD5相关函数:

    // md5下载:https://raw.githubusercontent.com/emn178/js-md5/master/src/md5.js
    
    let Base64 = {
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        encode: function(e) {
            var t = "";
            var n, r, i, s, o, u, a;
            var f = 0;
            e = Base64._utf8_encode(e);
            while (f < e.length) {
                n = e.charCodeAt(f++);
                r = e.charCodeAt(f++);
                i = e.charCodeAt(f++);
                s = n >> 2;
                o = (n & 3) << 4 | r >> 4;
                u = (r & 15) << 2 | i >> 6;
                a = i & 63;
                if (isNaN(r)) {
                    u = a = 64
                } else if (isNaN(i)) {
                    a = 64
                }
                t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
            }
            return t
        },
        decode: function(e) {
            var t = "";
            var n, r, i;
            var s, o, u, a;
            var f = 0;
            e = e.replace(/[^A-Za-z0-9+/=]/g, "");
            while (f < e.length) {
                s = this._keyStr.indexOf(e.charAt(f++));
                o = this._keyStr.indexOf(e.charAt(f++));
                u = this._keyStr.indexOf(e.charAt(f++));
                a = this._keyStr.indexOf(e.charAt(f++));
                n = s << 2 | o >> 4;
                r = (o & 15) << 4 | u >> 2;
                i = (u & 3) << 6 | a;
                t = t + String.fromCharCode(n);
                if (u != 64) {
                    t = t + String.fromCharCode(r)
                }
                if (a != 64) {
                    t = t + String.fromCharCode(i)
                }
            }
            t = Base64._utf8_decode(t);
            return t
        },
        _utf8_encode: function(e) {
            e = e.replace(/rn/g, "n");
            var t = "";
            for (var n = 0; n < e.length; n++) {
                var r = e.charCodeAt(n);
                if (r < 128) {
                    t += String.fromCharCode(r)
                } else if (r > 127 && r < 2048) {
                    t += String.fromCharCode(r >> 6 | 192);
                    t += String.fromCharCode(r & 63 | 128)
                } else {
                    t += String.fromCharCode(r >> 12 | 224);
                    t += String.fromCharCode(r >> 6 & 63 | 128);
                    t += String.fromCharCode(r & 63 | 128)
                }
            }
            return t
        },
        _utf8_decode: function(e) {
            var t = "";
            var n = 0;
            var r = c1 = c2 = 0;
            while (n < e.length) {
                r = e.charCodeAt(n);
                if (r < 128) {
                    t += String.fromCharCode(r);
                    n++
                } else if (r > 191 && r < 224) {
                    c2 = e.charCodeAt(n + 1);
                    t += String.fromCharCode((r & 31) << 6 | c2 & 63);
                    n += 2
                } else {
                    c2 = e.charCodeAt(n + 1);
                    c3 = e.charCodeAt(n + 2);
                    t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
                    n += 3
                }
            }
            return t
        }
    }
    

    3. 源码及播放器实现

    播放器官网: videojs

    来吧,直接上全部代码:

    <!doctype html>
    <html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="applicable-device" content="pc,mobile">
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/video.js@7.4.1/dist/video-js.min.css">
        <title>虎牙直播</title>
    </head>
    <body>
        <video id="player" class="video-js vjs-16-9 vjs-big-play-centered" controls preload="auto"  data-setup="{}">
            <source src="https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8" type="application/x-mpegURL">
            <p class="vjs-no-js">
                To view this video please enable JavaScript, and consider upgrading to a web browser that
                <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a>
            </p>
        </video>
        <script src="https://cdn.jsdelivr.net/npm/video.js@7.4.1/dist/video.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/@videojs/http-streaming@1.10.3/dist/videojs-http-streaming.min.js"></script>
        <script src="js/md5.js"></script>
        <script>
            let Base64 = {
                _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
                encode: function(e) {
                    var t = "";
                    var n, r, i, s, o, u, a;
                    var f = 0;
                    e = Base64._utf8_encode(e);
                    while (f < e.length) {
                        n = e.charCodeAt(f++);
                        r = e.charCodeAt(f++);
                        i = e.charCodeAt(f++);
                        s = n >> 2;
                        o = (n & 3) << 4 | r >> 4;
                        u = (r & 15) << 2 | i >> 6;
                        a = i & 63;
                        if (isNaN(r)) {
                            u = a = 64
                        } else if (isNaN(i)) {
                            a = 64
                        }
                        t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
                    }
                    return t
                },
                decode: function(e) {
                    var t = "";
                    var n, r, i;
                    var s, o, u, a;
                    var f = 0;
                    e = e.replace(/[^A-Za-z0-9+/=]/g, "");
                    while (f < e.length) {
                        s = this._keyStr.indexOf(e.charAt(f++));
                        o = this._keyStr.indexOf(e.charAt(f++));
                        u = this._keyStr.indexOf(e.charAt(f++));
                        a = this._keyStr.indexOf(e.charAt(f++));
                        n = s << 2 | o >> 4;
                        r = (o & 15) << 4 | u >> 2;
                        i = (u & 3) << 6 | a;
                        t = t + String.fromCharCode(n);
                        if (u != 64) {
                            t = t + String.fromCharCode(r)
                        }
                        if (a != 64) {
                            t = t + String.fromCharCode(i)
                        }
                    }
                    t = Base64._utf8_decode(t);
                    return t
                },
                _utf8_encode: function(e) {
                    e = e.replace(/rn/g, "n");
                    var t = "";
                    for (var n = 0; n < e.length; n++) {
                        var r = e.charCodeAt(n);
                        if (r < 128) {
                            t += String.fromCharCode(r)
                        } else if (r > 127 && r < 2048) {
                            t += String.fromCharCode(r >> 6 | 192);
                            t += String.fromCharCode(r & 63 | 128)
                        } else {
                            t += String.fromCharCode(r >> 12 | 224);
                            t += String.fromCharCode(r >> 6 & 63 | 128);
                            t += String.fromCharCode(r & 63 | 128)
                        }
                    }
                    return t
                },
                _utf8_decode: function(e) {
                    var t = "";
                    var n = 0;
                    var r = c1 = c2 = 0;
                    while (n < e.length) {
                        r = e.charCodeAt(n);
                        if (r < 128) {
                            t += String.fromCharCode(r);
                            n++
                        } else if (r > 191 && r < 224) {
                            c2 = e.charCodeAt(n + 1);
                            t += String.fromCharCode((r & 31) << 6 | c2 & 63);
                            n += 2
                        } else {
                            c2 = e.charCodeAt(n + 1);
                            c3 = e.charCodeAt(n + 2);
                            t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
                            n += 3
                        }
                    }
                    return t
                }
            }
    
            function parseUrl(url){
                let params = url.split("?")[1];
                params = params.split("&");
                let paramsObj = {};
                for (let i = 0; i < params.length; i++) {
                    let item = params[i].split("=");
                    2 === item.length && (paramsObj[item[0]] = item[1])
                }
    
                let mainUrl = url.split("?")[0];
                let r = mainUrl.split("/");
                let streamName = r[r.length - 1].replace(/.(flv|m3u8)/g, "");
                let {fm: fm, wsTime: wsTime, wsSecret: u, ...others} = paramsObj;
                let fmParse = Base64.decode(decodeURIComponent(fm));
                let p = fmParse.split("_")[0];
                let time = parseInt(1e4 * (new Date).getTime() + 1e4 * Math.random());
                let newWsSecret = `${p}_0_${streamName}_${time}_${wsTime}`;
                newWsSecret = md5.hex(newWsSecret);
                let y = "";
                Object.keys(others).forEach(e=>{
                    y += `&${e}=${others[e]}`
                });
                return `${mainUrl}?wsSecret=${newWsSecret}&wsTime=${wsTime}&u=0&seqid=${time}${y}`;
            }
    
            let e = parseUrl("//al.hls.huya.com/src/1423787831-1423787831-6115122170587774976-2847699118-10057-A-0-1-imgplus_2000.m3u8?wsSecret=f9aaf4fcbe42e724d152c265cf1837fb&wsTime=5ff71b32&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctype=tars_mobile&txyp=o%3Aj10%3B&fs=bgct&&sphdcdn=al_7-tx_3-js_3-ws_7-bd_2-hw_2&sphdDC=huya&sphd=264_*-265_*&t=103");
            let t = videojs("#player");
            t.src(e);
            t.play();
        </script>
    </body>
    </html>
    

    看看诸葛亮弹琴退仲达 :
    在这里插入图片描述

    4. 总结

    • 目前发现虎牙【一起看】栏目下的需要把url做第二次解析,普通直播间可以直接拿来播放;
    • 有人可能说为啥要用移动端的页面解析,而不用PC端的,其实也可以的,分析源码可以找到一个config对象,里面就包含了所需的信息,看到这些参数熟悉不?(同样的【一起看】栏目的需要二次解析):
      在这里插入图片描述

    在这里插入图片描述

    展开全文

空空如也

空空如也

1 2 3 4 5
收藏数 84
精华内容 33
关键字:

m3u8解析源码