Qt 之播放m3u8视频流


前言

项目中,需要播放视频,拿到的数据是一个m3u8结尾的http链接,比如http://charge.zzzz.cc//fudao_79d049cdbe22116dd19bdc1bad150b90.m3u8?,然后本以为直接调用QMediaPlayer的接口即可,如下

player = new QMediaPlayer;
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setMedia(QUrl::fromLocalFile("/Users/me/Music/coolsong.mp3"));
player->setVolume(50);
player->play();

或者qml:

MediaPlayer {
        id: player;
}

function startPlay(url)
{
	player.source = url;
    player.videoPlayClicked = true;
    player.start();
}

却发现,针对m3u8这种url,不能直接播放,然后就研究了下什么是m3u8.


一、m3u8是什么?

m3u8 文件格式详解

简介

M3U8 是 Unicode 版本的 M3U,用 UTF-8 编码。“M3U” 和 “M3U8” 文件都是苹果公司使用的 HTTP Live Streaming(HLS) 协议格式的基础,这种协议格式可以在 iPhone 和 Macbook 等设备播放。

上述文字定义来自于维基百科。可以看到,m3u8 文件其实是 HTTP Live Streaming(缩写为 HLS) 协议的部分内容,而 HLS 是一个由苹果公司提出的基于 HTTP 的流媒体网络传输协议。

HLS 的工作原理是把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时,客户端会下载一个包含元数据的 extended M3U (m3u8) playlist文件,用于寻找可用的媒体流。
HLS 只请求基本的 HTTP 报文,与实时传输协议(RTP)不同,HLS 可以穿过任何允许 HTTP 数据通过的防火墙或者代理服务器。它也很容易使用内容分发网络来传输媒体流。

简而言之,HLS 是新一代流媒体传输协议,其基本实现原理为将一个大的媒体文件进行分片,将该分片文件资源路径记录于 m3u8 文件(即 playlist)内,其中附带一些额外描述(比如该资源的多带宽信息···)用于提供给客户端。客户端依据该 m3u8 文件即可获取对应的媒体资源,进行播放。

因此,客户端获取 HLS 流文件,主要就是对 m3u8 文件进行解析操作。

那么,下面就简单介绍下 m3u8 文件。

M3U8 文件简介

m3u8 文件实质是一个播放列表(playlist),其可能是一个媒体播放列表(Media Playlist),或者是一个主列表(Master Playlist)。但无论是哪种播放列表,其内部文字使用的都是 utf-8 编码。

当 m3u8 文件作为媒体播放列表(Meida Playlist)时,其内部信息记录的是一系列媒体片段资源,顺序播放该片段资源,即可完整展示多媒体资源。其格式如下所示:

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts

对于点播来说,客户端只需按顺序下载上述片段资源,依次进行播放即可。而对于直播来说,客户端需要 定时重新请求 该 m3u8 文件,看下是否有新的片段数据需要进行下载并播放。

当 m3u8 作为主播放列表(Master Playlist)时,其内部提供的是同一份媒体资源的多份流列表资源(Variant Stream)。其格式如下所示:

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/lo_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/hi_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/high/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.5"
http://example.com/audio/index.m3u8

该备用流资源指定了多种不同码率,不同格式的媒体播放列表,并且,该备用流资源也可同时提供不同版本的资源内容,比如不同语言的音频文件,不同角度拍摄的视屏文件等等。客户可以根据不同的网络状态选取合适码流的资源,并且最好根据用户喜好选择合适的资源内容。

以上,就是 m3u8 文件的大概内容

二、qt 如何播放?

1.播放流程

在这里插入图片描述

  1. 通过QNetworkAccessManager get获得video的url,即上面提到的

http://charge.zzzz.cc//fudao_79d049cdbe22116dd19bdc1bad150b90.m3u8?

  1. 下载url文件,保存到本地文件

fudao_79d049cdbe22116dd19bdc1bad150b90.m3u8

  1. 设置QMediapPlayer的 media

核心代码如下(下载):

void M3u8DownloadManager::startDownLoad(const QByteArray &url)
{
    QUrl surl = QUrl::fromEncoded(url);
    revData.clear();
    filename = getFileName(surl);  //根据url获得文件名 字符串解析
    if(QFile::exists(DevConfig::GetInstance()->getTempM3u8Dir() + filename))
    {
        qDebug() << "exists" << filename;
        emit getU3m8(DevConfig::GetInstance()->getTempM3u8Dir() + filename);
        return;
    }
    int linkpos = surl.toString().indexOf("&c=");
    QString link = surl.toString().mid(linkpos+3);

    QString urlDecode = QUrl::fromPercentEncoding(link.toLocal8Bit());

    QUrl linkUrl = QUrl(urlDecode);
    QNetworkRequest request(linkUrl);
    currentDownload = pManager->get(request);
    connect(currentDownload, SIGNAL(finished()),
            SLOT(downloadFinished()));
    connect(currentDownload, SIGNAL(readyRead()),
            SLOT(downloadReadyRead()));

}

保存下载文件

void M3u8DownloadManager::downloadFinished()
{

    int datalen = revData.length();
    qDebug() << filename << datalen /*<< revData*/;
    QFile output;
    output.setFileName(DevConfig::GetInstance()->getTempM3u8Dir() + filename);
    if (!output.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
        qDebug() <<"open file error" << output.errorString()<< output.fileName();
        return;                 // skip this download
    }
    output.write(revData,val);
    output.close();
    emit getU3m8(DevConfig::GetInstance()->getTempM3u8Dir() + filename);

    currentDownload->deleteLater();
}

接受下载完信号,然后播放器播放

Connections
    {
        target: dataManager
        onGetU3m8:
        {
            player.source = "file:///"+ m3u8;
            player.videoPlayClicked = true;
            player.start();
        }
   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值