hls 解密播放

Signed-off-by: mark1010 <ligaokui3@huawei.com>
This commit is contained in:
mark1010 2024-01-08 10:15:18 +08:00
parent 3ebc19fc1f
commit 4bc3de33b1
No known key found for this signature in database
GPG Key ID: C036A3B35AEAA1D0
8 changed files with 223 additions and 13 deletions

View File

@ -64,7 +64,8 @@
"third_party": [
"bounds_checking_function",
"ffmpeg",
"curl"
"curl",
"openssl"
]
},
"build": {

View File

@ -46,6 +46,7 @@ config("http_source_plugin_config") {
"$media_foundation_root_dir/src",
"//third_party/ffmpeg",
"//third_party/curl/include",
"//third_party/openssl/include",
]
}
@ -70,7 +71,10 @@ ohos_shared_library("media_plugin_HttpSource") {
"monitor/download_monitor.cpp",
]
deps = [ "//third_party/curl:curl_shared" ]
deps = [
"//third_party/curl:curl_shared",
"//third_party/openssl:libcrypto_shared",
]
external_deps = [
"c_utils:utils",

View File

@ -15,16 +15,20 @@
#define HST_LOG_TAG "HlsMediaDownloader"
#include "hls_media_downloader.h"
#include "media_downloader.h"
#include "hls_playlist_downloader.h"
#include "securec.h"
#include <algorithm>
#include "plugin/plugin_time.h"
#include "openssl/aes.h"
#include "osal/task/task.h"
namespace OHOS {
namespace Media {
namespace Plugins {
namespace HttpPlugin {
namespace {
constexpr int RING_BUFFER_SIZE = 5 * 48 * 1024;
constexpr uint32_t DECRYPT_COPY_LEN = 128;
}
// hls manifest, m3u8 --- content get from m3u8 url, we get play list from the content
@ -184,7 +188,58 @@ bool HlsMediaDownloader::GetStartedStatus()
bool HlsMediaDownloader::SaveData(uint8_t* data, uint32_t len)
{
startedPlayStatus_ = true;
return buffer_->WriteBuffer(data, len);
uint32_t writeLen = 0;
uint8_t *writeDataPoint = data;
uint32_t waitLen = len;
if (keyLen_ == 0) {
return buffer_->WriteBuffer(data, len);
}
if ((len + afterAlignRemainedLength_) < DECRYPT_UNIT_LEN) {
memcpy_s(afterAlignRemainedBuffer_ + afterAlignRemainedLength_, DECRYPT_UNIT_LEN - afterAlignRemainedLength_,
data, len);
afterAlignRemainedLength_ += len;
return true;
}
writeLen =
((waitLen + afterAlignRemainedLength_) / DECRYPT_UNIT_LEN) * DECRYPT_UNIT_LEN - afterAlignRemainedLength_;
memcpy_s(decryptBuffer_, afterAlignRemainedLength_, afterAlignRemainedBuffer_, afterAlignRemainedLength_);
uint32_t minWriteLen = (RING_BUFFER_SIZE - afterAlignRemainedLength_) > writeLen ?
writeLen : RING_BUFFER_SIZE - afterAlignRemainedLength_;
memcpy_s(decryptBuffer_ + afterAlignRemainedLength_, minWriteLen, writeDataPoint, minWriteLen);
// decry buffer data
uint32_t realLen = writeLen + afterAlignRemainedLength_;
AES_cbc_encrypt(decryptBuffer_, decryptCache_, realLen, &aesKey_, iv_, AES_DECRYPT);
totalLen_ += realLen;
bool ret = buffer_->WriteBuffer(decryptCache_, realLen);
memset_s(decryptCache_, realLen, 0x00, realLen);
if (!ret) {
return false;
}
afterAlignRemainedLength_ = 0;
memset_s(afterAlignRemainedBuffer_, DECRYPT_UNIT_LEN, 0x00, DECRYPT_UNIT_LEN);
writeDataPoint += writeLen;
waitLen -= writeLen;
if (waitLen > 0) {
afterAlignRemainedLength_ = waitLen;
memcpy_s(afterAlignRemainedBuffer_, DECRYPT_UNIT_LEN, writeDataPoint, waitLen);
}
return true;
}
void HlsMediaDownloader::OnSourceKeyChange(const uint8_t *key, size_t keyLen, const uint8_t *iv)
{
keyLen_ = keyLen;
if (keyLen == 0) {
return;
}
memcpy_s(iv_, DECRYPT_UNIT_LEN, iv, DECRYPT_UNIT_LEN);
memcpy_s(key_, DECRYPT_UNIT_LEN, key, keyLen);
AES_set_decrypt_key(key_, DECRYPT_COPY_LEN, &aesKey_);
}
void HlsMediaDownloader::SetStatusCallback(StatusCallbackFunc cb)

View File

@ -17,8 +17,11 @@
#define HISTREAMER_HLS_MEDIA_DOWNLOADER_H
#include "playlist_downloader.h"
#include "osal/utils/ring_buffer.h"
#include "media_downloader.h"
#include "osal/utils/ring_buffer.h"
#include "osal/utils/steady_clock.h"
#include "openssl/aes.h"
#include "osal/task/task.h"
namespace OHOS {
namespace Media {
@ -44,10 +47,12 @@ public:
bool GetStartedStatus() override;
std::vector<uint32_t> GetBitRates() override;
bool SelectBitRate(uint32_t bitRate) override;
void OnSourceKeyChange(const uint8_t *key, size_t keyLen, const uint8_t *iv) override;
void SeekToTs(int64_t seekTime);
void PutRequestIntoDownloader(const PlayInfo& palyInfo);
void UpdateDownloadFinished(std::string url);
std::string GetTsNameFromUrl(std::string url); // get file name from url
constexpr static int RING_BUFFER_SIZE = 5 * 48 * 1024;
private:
bool SaveData(uint8_t* data, uint32_t len);
@ -70,7 +75,17 @@ private:
std::deque<PlayInfo> backPlayList_;
bool isSelectingBitrate_ {false};
bool isDownloadStarted_ {false};
static constexpr uint32_t DECRYPT_UNIT_LEN = 16;
uint8_t afterAlignRemainedBuffer_[DECRYPT_UNIT_LEN] { 0 };
uint32_t afterAlignRemainedLength_ = 0;
uint64_t totalLen_ = 0;
std::string curUrl_;
uint8_t key_[16];
size_t keyLen_ { 0 };
uint8_t iv_[16];
AES_KEY aesKey_;
uint8_t decryptCache_[RING_BUFFER_SIZE] { 0 };
uint8_t decryptBuffer_[RING_BUFFER_SIZE] { 0 };
};
}
}

View File

@ -16,6 +16,7 @@
#include <mutex>
#include "plugin/plugin_time.h"
#include "hls_playlist_downloader.h"
#include <unistd.h>
namespace OHOS {
namespace Media {
@ -79,6 +80,16 @@ void HlsPlayListDownloader::NotifyListChange()
{
auto files = currentVariant_->m3u8_->files_;
auto playList = std::vector<PlayInfo>();
if (currentVariant_->m3u8_->isDecryptAble_) {
while (!currentVariant_->m3u8_->isDecryptKeyReady_) {
OSAL::SleepFor(10); // 10
}
callback_->OnSourceKeyChange(currentVariant_->m3u8_->key_, currentVariant_->m3u8_->keyLen_,
currentVariant_->m3u8_->iv_);
} else {
MEDIA_LOG_E("Decrypkey is not needed.");
callback_->OnSourceKeyChange(nullptr, 0, nullptr);
}
playList.reserve(files.size());
for (auto &file: files) {
PlayInfo palyInfo;

View File

@ -24,7 +24,8 @@ namespace Media {
namespace Plugins {
namespace HttpPlugin {
namespace {
bool StrHasPrefix(std::string& str, const std::string& prefix)
constexpr int MAX_LOOP = 16;
bool StrHasPrefix(std::string &str, const std::string &prefix)
{
return str.find(prefix) == 0;
}
@ -42,6 +43,23 @@ std::string UriJoin(std::string& baseUrl, const std::string& uri)
}
}
M3U8Fragment::M3U8Fragment(M3U8Fragment m3u8, uint8_t *key, uint8_t *iv)
: uri_(std::move(m3u8.uri_)),
title_(std::move(m3u8.title_)),
duration_(m3u8.duration_),
sequence_(m3u8.sequence_),
discont_(m3u8.discont_)
{
if (iv == nullptr || key == nullptr) {
return;
}
for (int i = 0; i < MAX_LOOP; i++) {
iv_[i] = iv[i];
key_[i] = key[i];
}
}
M3U8Fragment::M3U8Fragment(std::string uri, std::string title, double duration, int sequence, bool discont)
: uri_(std::move(uri)), title_(std::move(title)), duration_(duration), sequence_(sequence), discont_(discont)
{
@ -52,6 +70,11 @@ M3U8::M3U8(std::string uri, std::string name) : uri_(std::move(uri)), name_(std:
InitTagUpdatersMap();
}
M3U8::~M3U8()
{
downloader_->Stop();
}
bool M3U8::Update(std::string& playList)
{
if (playList_ == playList) {
@ -116,10 +139,12 @@ void M3U8::InitTagUpdatersMap()
info.discontinuity = true;
};
tagUpdatersMap_[HlsTag::EXTXKEY] = [](std::shared_ptr<Tag> &tag, M3U8Info &info) {
std::ignore = tag;
std::ignore = info;
MEDIA_LOG_I("need to parse EXTXKEY");
tagUpdatersMap_[HlsTag::EXTXKEY] = [this](std::shared_ptr<Tag> &tag, M3U8Info &info) {
isDecryptAble_ = true;
isDecryptKeyReady_ = false;
ParseKey(std::static_pointer_cast<AttributesTag>(tag));
DownloadKey();
// wait for key downloaded
};
tagUpdatersMap_[HlsTag::EXTXMAP] = [](std::shared_ptr<Tag> &tag, M3U8Info &info) {
@ -143,8 +168,16 @@ void M3U8::UpdateFromTags(std::list<std::shared_ptr<Tag>>& tags)
}
if (!info.uri.empty()) {
files_.emplace_back(std::make_shared<M3U8Fragment>(info.uri, info.title, info.duration, sequence_++,
info.discontinuity));
// add key_ and iv_ to M3U8Fragment(file)
if (isDecryptAble_) {
auto m3u8 = M3U8Fragment(info.uri, info.title, info.duration, sequence_++, info.discontinuity);
auto fragment = std::make_shared<M3U8Fragment>(m3u8, key_, iv_);
files_.emplace_back(fragment);
} else {
auto fragment = std::make_shared<M3U8Fragment>(info.uri, info.title, info.duration, sequence_++,
info.discontinuity);
files_.emplace_back(fragment);
}
info.uri = "", info.title = "", info.duration = 0, info.discontinuity = false;
}
}
@ -171,6 +204,73 @@ bool M3U8::IsLive() const
return bLive_;
}
void M3U8::ParseKey(const std::shared_ptr<AttributesTag> &tag)
{
auto methodAttribute = tag->GetAttributeByName("METHOD");
if (methodAttribute) {
method_ = std::make_shared<std::string>(methodAttribute->QuotedString());
}
auto uriAttribute = tag->GetAttributeByName("URI");
if (uriAttribute) {
keyUri_ = std::make_shared<std::string>(uriAttribute->QuotedString());
}
auto ivAttribute = tag->GetAttributeByName("IV");
if (ivAttribute) {
std::vector<uint8_t> iv_buff = ivAttribute->HexSequence();
int size = iv_buff.size() > MAX_LOOP ? MAX_LOOP : iv_buff.size();
for (int i = 0; i < size; i++) {
iv_[i] = iv_buff[i];
}
}
}
void M3U8::DownloadKey()
{
if (keyUri_ == nullptr) {
return;
}
downloader_ = std::make_shared<Downloader>("hlsSourceKey");
dataSave_ = [this](uint8_t *&&data, uint32_t &&len) {
return SaveData(std::forward<decltype(data)>(data), std::forward<decltype(len)>(len));
};
// this is default callback
statusCallback_ = [this](DownloadStatus &&status, std::shared_ptr<Downloader> d,
std::shared_ptr<DownloadRequest> &request) {
OnDownloadStatus(std::forward<decltype(status)>(status), downloader_, std::forward<decltype(request)>(request));
};
auto realStatusCallback = [this](DownloadStatus &&status, std::shared_ptr<Downloader> &downloader,
std::shared_ptr<DownloadRequest> &request) {
statusCallback_(status, downloader_, std::forward<decltype(request)>(request));
};
std::string realKeyUrl = UriJoin(uri_, *keyUri_);
downloadRequest_ = std::make_shared<DownloadRequest>(realKeyUrl, dataSave_, realStatusCallback, true);
downloader_->Download(downloadRequest_, -1);
downloader_->Start();
}
bool M3U8::SaveData(uint8_t *data, uint32_t len)
{
// 16 is a magic number
if (len <= MAX_LOOP && len != 0) {
memcpy_s(key_, MAX_LOOP, data, len);
keyLen_ = len;
isDecryptKeyReady_ = true;
MEDIA_LOG_I("DownloadKey hlsSourceKey end.\n");
return true;
}
return false;
}
void M3U8::OnDownloadStatus(DownloadStatus status, std::shared_ptr<Downloader> &,
std::shared_ptr<DownloadRequest> &request)
{
// This should not be called normally
if (request->GetClientError() != NetworkClientErrorCode::ERROR_OK || request->GetServerError() != 0) {
MEDIA_LOG_E("OnDownloadStatus " PUBLIC_LOG_D32 ", url : " PUBLIC_LOG_S, status, request->GetUrl().c_str());
}
}
M3U8VariantStream::M3U8VariantStream(std::string name, std::string uri, std::shared_ptr<M3U8> m3u8)
: name_(std::move(name)), uri_(std::move(uri)), m3u8_(std::move(m3u8))
{

View File

@ -22,6 +22,8 @@
#include <unordered_map>
#include <functional>
#include "hls_tags.h"
#include "playlist_downloader.h"
#include "download/downloader.h"
namespace OHOS {
namespace Media {
@ -44,12 +46,13 @@ struct M3U8InitFile {
struct M3U8Fragment {
M3U8Fragment(std::string uri, std::string title, double duration, int sequence, bool discont);
M3U8Fragment(M3U8Fragment m3u8, uint8_t *key, uint8_t *iv);
std::string uri_;
std::string title_;
double duration_;
int64_t sequence_;
bool discont_ {false};
std::string key_ {};
uint8_t key_[16] { 0 };
int iv_[16] {0};
int offset_ {-1};
int size_ {0};
@ -65,6 +68,7 @@ struct M3U8Info {
struct M3U8 {
M3U8(std::string uri, std::string name);
~M3U8();
void InitTagUpdatersMap();
bool Update(std::string& playList);
void UpdateFromTags(std::list<std::shared_ptr<Tag>>& tags);
@ -82,6 +86,25 @@ struct M3U8 {
uint64_t sequence_ {1}; // default 1
int discontSequence_ {0};
std::string playList_;
void ParseKey(const std::shared_ptr<AttributesTag> &tag);
void DownloadKey();
bool SaveData(uint8_t *data, uint32_t len);
void OnDownloadStatus(DownloadStatus status, std::shared_ptr<Downloader> &,
std::shared_ptr<DownloadRequest> &request);
std::shared_ptr<std::string> method_;
std::shared_ptr<std::string> keyUri_;
uint8_t iv_[16] { 0 };
uint8_t key_[16] { 0 };
size_t keyLen_ { 0 };
std::shared_ptr<Downloader> downloader_;
std::shared_ptr<DownloadRequest> downloadRequest_;
DataSaveFunc dataSave_;
StatusCallbackFunc statusCallback_;
PlayListChangeCallback *callback_ { nullptr };
bool startedDownloadStatus_ { false };
bool isDecryptAble_ { false };
bool isDecryptKeyReady_ { false };
};
struct M3U8Media {

View File

@ -31,6 +31,7 @@ struct PlayInfo {
struct PlayListChangeCallback {
virtual ~PlayListChangeCallback() = default;
virtual void OnPlayListChanged(const std::vector<PlayInfo>& playList) = 0;
virtual void OnSourceKeyChange(const uint8_t* key, size_t key_len, const uint8_t* iv) = 0;
};
class PlayListDownloader {
public: