1 /*
2 * Copyright (c) 2022-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #define HST_LOG_TAG "HlsMediaDownloader"
16
17 #include "hls_media_downloader.h"
18 #include "hls_playlist_downloader.h"
19 #include "securec.h"
20
21 namespace OHOS {
22 namespace Media {
23 namespace Plugin {
24 namespace HttpPlugin {
25 namespace {
26 constexpr int RING_BUFFER_SIZE = 5 * 48 * 1024;
27 }
28
29 // Description:
30 // hls manifest, m3u8 --- content get from m3u8 url, we get play list from the content
31 // fragment --- one item in play list, download media data according to the fragment address.
HlsMediaDownloader()32 HlsMediaDownloader::HlsMediaDownloader() noexcept
33 {
34 buffer_ = std::make_shared<RingBuffer>(RING_BUFFER_SIZE);
35 buffer_->Init();
36
37 downloader_ = std::make_shared<Downloader>("hlsMedia");
38 downloadTask_ = std::make_shared<OSAL::Task>(std::string("FragmentDownload"));
39 downloadTask_->RegisterHandler([this] { FragmentDownloadLoop(); });
40
41 playList_ = std::make_shared<BlockingQueue<std::string>>("PlayList", 50); // 50
42
43 dataSave_ = [this] (uint8_t*&& data, uint32_t&& len) {
44 return SaveData(std::forward<decltype(data)>(data), std::forward<decltype(len)>(len));
45 };
46
47 playListDownloader_ = std::make_shared<HlsPlayListDownloader>();
48 playListDownloader_->SetPlayListCallback(this);
49 }
50
FragmentDownloadLoop()51 void HlsMediaDownloader::FragmentDownloadLoop()
52 {
53 std::string url = playList_->Pop();
54 if (url.empty()) { // when monitor pause, playList_ set active false, it's empty
55 OSAL::SleepFor(10); // 10
56 return;
57 }
58 if (!fragmentDownloadStart[url]) {
59 fragmentDownloadStart[url] = true;
60 auto realStatusCallback = [this] (DownloadStatus&& status, std::shared_ptr<Downloader>& downloader,
61 std::shared_ptr<DownloadRequest>& request) {
62 statusCallback_(status, downloader_, std::forward<decltype(request)>(request));
63 };
64 // TO DO: If the fragment file is too large, should not requestWholeFile.
65 downloadRequest_ = std::make_shared<DownloadRequest>(url, dataSave_, realStatusCallback, true);
66 downloader_->Download(downloadRequest_, -1); // -1
67 downloader_->Start();
68 }
69 }
70
Open(const std::string & url)71 bool HlsMediaDownloader::Open(const std::string& url)
72 {
73 playListDownloader_->Open(url);
74 downloadTask_->Start();
75 return true;
76 }
77
Close(bool isAsync)78 void HlsMediaDownloader::Close(bool isAsync)
79 {
80 buffer_->SetActive(false);
81 playList_->SetActive(false);
82 downloadTask_->Stop();
83 playListDownloader_->Close();
84 downloader_->Stop();
85 }
86
Pause()87 void HlsMediaDownloader::Pause()
88 {
89 bool cleanData = GetSeekable() != Seekable::SEEKABLE;
90 buffer_->SetActive(false, cleanData);
91 playList_->SetActive(false, cleanData);
92 playListDownloader_->Pause();
93 downloadTask_->Pause();
94 downloader_->Pause();
95 }
96
Resume()97 void HlsMediaDownloader::Resume()
98 {
99 buffer_->SetActive(true);
100 playList_->SetActive(true);
101 playListDownloader_->Resume();
102 downloadTask_->Start();
103 downloader_->Resume();
104 }
105
Read(unsigned char * buff,unsigned int wantReadLength,unsigned int & realReadLength,bool & isEos)106 bool HlsMediaDownloader::Read(unsigned char* buff, unsigned int wantReadLength,
107 unsigned int& realReadLength, bool& isEos)
108 {
109 FALSE_RETURN_V(buffer_ != nullptr, false);
110 realReadLength = buffer_->ReadBuffer(buff, wantReadLength, 2); // wait 2 times
111 MEDIA_LOG_D("Read: wantReadLength " PUBLIC_LOG_D32 ", realReadLength " PUBLIC_LOG_D32 ", isEos "
112 PUBLIC_LOG_D32, wantReadLength, realReadLength, isEos);
113 return true;
114 }
115
Seek(int offset)116 bool HlsMediaDownloader::Seek(int offset)
117 {
118 FALSE_RETURN_V(buffer_ != nullptr, false);
119 MEDIA_LOG_I("Seek: buffer size " PUBLIC_LOG_ZU ", offset " PUBLIC_LOG_D32, buffer_->GetSize(), offset);
120 if (buffer_->Seek(offset)) {
121 return true;
122 }
123 buffer_->Clear(); // First clear buffer, avoid no available buffer then task pause never exit.
124 downloader_->Pause();
125 buffer_->Clear();
126 downloader_->Seek(offset);
127 downloader_->Start();
128 return true;
129 }
130
GetContentLength() const131 size_t HlsMediaDownloader::GetContentLength() const
132 {
133 return 0;
134 }
135
GetDuration() const136 double HlsMediaDownloader::GetDuration() const
137 {
138 return playListDownloader_->GetDuration();
139 }
140
GetSeekable() const141 Seekable HlsMediaDownloader::GetSeekable() const
142 {
143 return playListDownloader_->GetSeekable();
144 }
145
SetCallback(Callback * cb)146 void HlsMediaDownloader::SetCallback(Callback* cb)
147 {
148 callback_ = cb;
149 }
150
OnPlayListChanged(const std::vector<std::string> & playList)151 void HlsMediaDownloader::OnPlayListChanged(const std::vector<std::string>& playList)
152 {
153 for (auto& fragment : playList) {
154 playList_->Push(fragment);
155 }
156 }
157
GetStartedStatus()158 bool HlsMediaDownloader::GetStartedStatus()
159 {
160 return playListDownloader_->GetPlayListDownloadStatus() && startedPlayStatus_;
161 }
162
SaveData(uint8_t * data,uint32_t len)163 bool HlsMediaDownloader::SaveData(uint8_t* data, uint32_t len)
164 {
165 startedPlayStatus_ = true;
166 return buffer_->WriteBuffer(data, len);
167 }
168
SetStatusCallback(StatusCallbackFunc cb)169 void HlsMediaDownloader::SetStatusCallback(StatusCallbackFunc cb)
170 {
171 statusCallback_ = cb;
172 playListDownloader_->SetStatusCallback(cb);
173 }
174 }
175 }
176 }
177 }