mirror of
https://gitee.com/openharmony/multimedia_av_codec
synced 2024-10-07 14:13:29 +00:00
hls 解密播放
Signed-off-by: mark1010 <ligaokui3@huawei.com>
This commit is contained in:
parent
3ebc19fc1f
commit
4bc3de33b1
@ -64,7 +64,8 @@
|
||||
"third_party": [
|
||||
"bounds_checking_function",
|
||||
"ffmpeg",
|
||||
"curl"
|
||||
"curl",
|
||||
"openssl"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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 };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 {
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user