1 /*
2 * Copyright (C) 2023 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
16 #include "helper_data_source_callback.h"
17 #include "avsharedmemory.h"
18 #include "media_dfx.h"
19 #include "media_log.h"
20 #include "media_errors.h"
21 #include "scope_guard.h"
22
23 namespace {
24 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_METADATA, "HelperDataSourceCallback"};
25 }
26
27 namespace OHOS {
28 namespace Media {
29 const std::string HELPER_READAT_CALLBACK_NAME = "readAt";
30
~HelperDataSourceJsCallback()31 HelperDataSourceJsCallback::~HelperDataSourceJsCallback()
32 {
33 isExit_ = true;
34 cond_.notify_all();
35 memory_ = nullptr;
36 }
37
WaitResult()38 void HelperDataSourceJsCallback::WaitResult()
39 {
40 std::unique_lock<std::mutex> lock(mutexCond_);
41 if (!setResult_) {
42 static constexpr int32_t timeout = 100;
43 cond_.wait_for(lock, std::chrono::milliseconds(timeout), [this]() { return setResult_ || isExit_; });
44 if (!setResult_) {
45 readSize_ = 0;
46 if (isExit_) {
47 MEDIA_LOGW("Reset, ReadAt has been cancel!");
48 } else {
49 MEDIA_LOGW("timeout 100ms!");
50 }
51 }
52 }
53 setResult_ = false;
54 }
55
HelperDataSourceCallback(napi_env env,int64_t fileSize)56 HelperDataSourceCallback::HelperDataSourceCallback(napi_env env, int64_t fileSize)
57 : env_(env),
58 size_(fileSize)
59 {
60 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
61 }
62
~HelperDataSourceCallback()63 HelperDataSourceCallback::~HelperDataSourceCallback()
64 {
65 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
66 env_ = nullptr;
67 }
68
ReadAt(const std::shared_ptr<AVSharedMemory> & mem,uint32_t length,int64_t pos)69 int32_t HelperDataSourceCallback::ReadAt(const std::shared_ptr<AVSharedMemory> &mem, uint32_t length, int64_t pos)
70 {
71 MEDIA_LOGD("ReadAt in");
72 {
73 std::lock_guard<std::mutex> lock(mutex_);
74 if (refMap_.find(HELPER_READAT_CALLBACK_NAME) == refMap_.end()) {
75 return SOURCE_ERROR_IO;
76 }
77 cb_ = std::make_shared<HelperDataSourceJsCallback>(HELPER_READAT_CALLBACK_NAME, mem, length, pos);
78 CHECK_AND_RETURN_RET_LOG(cb_ != nullptr, 0, "Failed to Create HelperDataSourceJsCallback");
79 cb_->callback_ = refMap_.at(HELPER_READAT_CALLBACK_NAME);
80 }
81 ON_SCOPE_EXIT(0) {
82 cb_ = nullptr;
83 };
84
85 uv_loop_s *loop = nullptr;
86 napi_get_uv_event_loop(env_, &loop);
87 CHECK_AND_RETURN_RET_LOG(loop != nullptr, 0, "Failed to get uv event loop");
88 uv_work_t *work = new(std::nothrow) uv_work_t;
89 CHECK_AND_RETURN_RET_LOG(work != nullptr, 0, "Failed to new uv_work_t");
90 ON_SCOPE_EXIT(1) {
91 delete work;
92 };
93
94 HelperDataSourceJsCallbackWraper *cbWrap = new(std::nothrow) HelperDataSourceJsCallbackWraper();
95 CHECK_AND_RETURN_RET_LOG(cbWrap != nullptr, 0, "Failed to new HelperDataSourceJsCallbackWraper");
96 cbWrap->cb_ = cb_;
97 work->data = reinterpret_cast<void *>(cbWrap);
98 // async callback, jsWork and jsWork->data should be heap object.
99 int ret = UvWork(loop, work);
100 CHECK_AND_RETURN_RET_LOG(ret == 0, SOURCE_ERROR_IO, "Failed to execute uv queue work");
101 CANCEL_SCOPE_EXIT_GUARD(1);
102 cb_->WaitResult();
103 MEDIA_LOGD("HelperDataSourceCallback ReadAt out");
104 return cb_->readSize_;
105 }
106
UvWork(uv_loop_s * loop,uv_work_t * work)107 int32_t HelperDataSourceCallback::UvWork(uv_loop_s *loop, uv_work_t *work)
108 {
109 MEDIA_LOGD("begin UvWork");
110 return uv_queue_work_with_qos(loop, work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) {
111 // Js Thread
112 CHECK_AND_RETURN_LOG(work != nullptr && work->data != nullptr, "work is nullptr");
113 HelperDataSourceJsCallbackWraper *wrap = reinterpret_cast<HelperDataSourceJsCallbackWraper *>(work->data);
114 std::shared_ptr<HelperDataSourceJsCallback> event = wrap->cb_.lock();
115 CHECK_AND_RETURN_LOG(event != nullptr, "HelperDataSourceJsCallback is nullptr");
116 MEDIA_LOGD("length is %{public}u", event->length_);
117 do {
118 CHECK_AND_BREAK(status != UV_ECANCELED);
119 std::shared_ptr<AutoRef> ref = event->callback_.lock();
120 CHECK_AND_BREAK_LOG(ref != nullptr, "%{public}s AutoRef is nullptr", event->callbackName_.c_str());
121
122 napi_handle_scope scope = nullptr;
123 napi_open_handle_scope(ref->env_, &scope);
124 CHECK_AND_BREAK_LOG(scope != nullptr, "%{public}s scope is nullptr", event->callbackName_.c_str());
125 ON_SCOPE_EXIT(0) {
126 napi_close_handle_scope(ref->env_, scope);
127 };
128
129 napi_value jsCallback = nullptr;
130 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, &jsCallback);
131 CHECK_AND_BREAK(nstatus == napi_ok && jsCallback != nullptr);
132
133 // noseek mode don't need pos, so noseek mode need 2 parameters and seekable mode need 3 parameters
134 int32_t paramNum;
135 napi_value args[3] = { nullptr };
136 CHECK_AND_BREAK_LOG(event->memory_ != nullptr, "failed to checkout memory");
137 nstatus = napi_create_external_arraybuffer(ref->env_, event->memory_->GetBase(),
138 static_cast<size_t>(event->length_), [](napi_env env, void *data, void *hint) {}, nullptr, &args[0]);
139 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "create napi arraybuffer failed");
140 CHECK_AND_BREAK_LOG(napi_create_uint32(ref->env_, event->length_, &args[1]) == napi_ok,
141 "set length failed");
142 if (event->pos_ != -1) {
143 paramNum = 3; // 3 parameters
144 CHECK_AND_BREAK_LOG(napi_create_int64(ref->env_, event->pos_, &args[2]) == napi_ok, // 2 parameters
145 "set pos failed");
146 } else {
147 paramNum = 2; // 2 parameters
148 }
149
150 napi_value size;
151 MEDIA_LOGD("call JS function");
152 nstatus = napi_call_function(ref->env_, nullptr, jsCallback, paramNum, args, &size);
153 CHECK_AND_BREAK(nstatus == napi_ok);
154 nstatus = napi_get_value_int32(ref->env_, size, &event->readSize_);
155 CHECK_AND_BREAK_LOG(nstatus == napi_ok, "get size failed");
156 std::unique_lock<std::mutex> lock(event->mutexCond_);
157 event->setResult_ = true;
158 event->cond_.notify_all();
159 } while (0);
160 delete work;
161 }, uv_qos_user_initiated);
162 }
163
ReadAt(int64_t pos,uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)164 int32_t HelperDataSourceCallback::ReadAt(int64_t pos, uint32_t length, const std::shared_ptr<AVSharedMemory> &mem)
165 {
166 (void)pos;
167 (void)length;
168 (void)mem;
169 return MSERR_OK;
170 }
171
ReadAt(uint32_t length,const std::shared_ptr<AVSharedMemory> & mem)172 int32_t HelperDataSourceCallback::ReadAt(uint32_t length, const std::shared_ptr<AVSharedMemory> &mem)
173 {
174 (void)length;
175 (void)mem;
176 return MSERR_OK;
177 }
178
GetSize(int64_t & size)179 int32_t HelperDataSourceCallback::GetSize(int64_t &size)
180 {
181 size = size_;
182 return MSERR_OK;
183 }
184
SaveCallbackReference(const std::string & name,std::shared_ptr<AutoRef> ref)185 void HelperDataSourceCallback::SaveCallbackReference(const std::string &name, std::shared_ptr<AutoRef> ref)
186 {
187 MEDIA_LOGD("Add Callback: %{public}s", name.c_str());
188 std::lock_guard<std::mutex> lock(mutex_);
189 refMap_[name] = ref;
190 }
191
GetCallback(const std::string & name,napi_value * callback)192 int32_t HelperDataSourceCallback::GetCallback(const std::string &name, napi_value *callback)
193 {
194 (void)name;
195 if (refMap_.find(HELPER_READAT_CALLBACK_NAME) == refMap_.end()) {
196 return MSERR_INVALID_VAL;
197 }
198 auto ref = refMap_.at(HELPER_READAT_CALLBACK_NAME);
199 napi_status nstatus = napi_get_reference_value(ref->env_, ref->cb_, callback);
200 CHECK_AND_RETURN_RET(nstatus == napi_ok && callback != nullptr, MSERR_INVALID_OPERATION);
201 return MSERR_OK;
202 }
203
ClearCallbackReference()204 void HelperDataSourceCallback::ClearCallbackReference()
205 {
206 std::lock_guard<std::mutex> lock(mutex_);
207 std::map<std::string, std::shared_ptr<AutoRef>> temp;
208 temp.swap(refMap_);
209 MEDIA_LOGD("callback has been clear");
210 if (cb_) {
211 cb_->isExit_ = true;
212 cb_->cond_.notify_all();
213 }
214 }
215
AddNapiValueProp(napi_env env,napi_value obj,const std::string & key,napi_value value)216 bool HelperDataSourceCallback::AddNapiValueProp(napi_env env, napi_value obj, const std::string &key, napi_value value)
217 {
218 CHECK_AND_RETURN_RET(obj != nullptr, false);
219
220 napi_value keyNapi = nullptr;
221 napi_status status = napi_create_string_utf8(env, key.c_str(), NAPI_AUTO_LENGTH, &keyNapi);
222 CHECK_AND_RETURN_RET(status == napi_ok, false);
223
224 status = napi_set_property(env, obj, keyNapi, value);
225 CHECK_AND_RETURN_RET_LOG(status == napi_ok, false, "Failed to set property");
226
227 return true;
228 }
229 } // namespace Media
230 } // namespace OHOS
231