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 "napi_avcast_controller_callback.h"
17 #include "avsession_log.h"
18 #include "avsession_trace.h"
19 #include "napi_control_command.h"
20 #include "napi_meta_data.h"
21 #include "napi_playback_state.h"
22 #include "napi_media_description.h"
23 #include "napi_queue_item.h"
24 #include "napi_utils.h"
25 #include "napi_cast_control_command.h"
26 
27 namespace OHOS::AVSession {
NapiAVCastControllerCallback()28 NapiAVCastControllerCallback::NapiAVCastControllerCallback()
29 {
30     SLOGI("construct");
31 }
32 
~NapiAVCastControllerCallback()33 NapiAVCastControllerCallback::~NapiAVCastControllerCallback()
34 {
35     SLOGI("destroy");
36 }
37 
HandleEvent(int32_t event)38 void NapiAVCastControllerCallback::HandleEvent(int32_t event)
39 {
40     std::lock_guard<std::mutex> lockGuard(lock_);
41     if (callbacks_[event].empty()) {
42         SLOGE("not register callback event=%{public}d", event);
43         return;
44     }
45     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
46         asyncCallback_->Call(*ref);
47     }
48 }
49 
CheckCallbackValid(int32_t event,const std::list<napi_ref>::iterator & ref)50 std::function<bool()> NapiAVCastControllerCallback::CheckCallbackValid(int32_t event,
51     const std::list<napi_ref>::iterator& ref)
52 {
53     return [this, event, ref]() {
54         std::lock_guard<std::mutex> lockGuard(lock_);
55         if (callbacks_[event].empty()) {
56             SLOGE("checkCallbackValid with empty list for event %{public}d", event);
57             return false;
58         }
59         bool hasFunc = false;
60         for (auto it = callbacks_[event].begin(); it != callbacks_[event].end(); ++it) {
61             hasFunc = (ref == it ? true : hasFunc);
62         }
63         SLOGI("checkCallbackValid return hasFunc %{public}d, %{public}d", hasFunc, event);
64         return hasFunc;
65     };
66 }
67 
68 template<typename T>
HandleEvent(int32_t event,const T & param)69 void NapiAVCastControllerCallback::HandleEvent(int32_t event, const T& param)
70 {
71     std::lock_guard<std::mutex> lockGuard(lock_);
72     if (callbacks_[event].empty()) {
73         SLOGE("not register callback event=%{public}d", event);
74         return;
75     }
76     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
77         SLOGI("call with flag for event %{public}d", event);
78         asyncCallback_->CallWithFunc(*ref, isValid_, CheckCallbackValid(event, ref),
79             [param](napi_env env, int& argc, napi_value *argv) {
80                 argc = NapiUtils::ARGC_ONE;
81                 auto status = NapiUtils::SetValue(env, param, *argv);
82                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
83             });
84     }
85 }
86 
87 template<typename T>
HandleEvent(int32_t event,const std::string & firstParam,const T & secondParam)88 void NapiAVCastControllerCallback::HandleEvent(int32_t event, const std::string& firstParam, const T& secondParam)
89 {
90     std::lock_guard<std::mutex> lockGuard(lock_);
91     if (callbacks_[event].empty()) {
92         SLOGE("not register callback event=%{public}d", event);
93         return;
94     }
95     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
96         asyncCallback_->CallWithFunc(*ref, isValid_, CheckCallbackValid(event, ref),
97             [firstParam, secondParam](napi_env env, int& argc, napi_value *argv) {
98                 argc = NapiUtils::ARGC_TWO;
99                 auto status = NapiUtils::SetValue(env, firstParam, argv[0]);
100                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
101                 status = NapiUtils::SetValue(env, secondParam, argv[1]);
102                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
103             });
104     }
105 }
106 
107 template<typename T>
HandleEvent(int32_t event,const int32_t firstParam,const T & secondParam)108 void NapiAVCastControllerCallback::HandleEvent(int32_t event, const int32_t firstParam, const T& secondParam)
109 {
110     std::lock_guard<std::mutex> lockGuard(lock_);
111     if (callbacks_[event].empty()) {
112         SLOGE("not register callback event=%{public}d", event);
113         return;
114     }
115     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
116         asyncCallback_->CallWithFunc(*ref, isValid_, CheckCallbackValid(event, ref),
117             [firstParam, secondParam](napi_env env, int& argc, napi_value *argv) {
118                 argc = NapiUtils::ARGC_TWO;
119                 auto status = NapiUtils::SetValue(env, firstParam, argv[0]);
120                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
121                 status = NapiUtils::SetValue(env, secondParam, argv[1]);
122                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
123             });
124     }
125 }
126 
HandleEvent(int32_t event,const int32_t firstParam,const int32_t secondParam,const int32_t thirdParam)127 void NapiAVCastControllerCallback::HandleEvent(int32_t event,
128     const int32_t firstParam, const int32_t secondParam, const int32_t thirdParam)
129 {
130     std::lock_guard<std::mutex> lockGuard(lock_);
131     if (callbacks_[event].empty()) {
132         SLOGE("not register callback event=%{public}d", event);
133         return;
134     }
135     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
136         asyncCallback_->CallWithFunc(*ref, isValid_, CheckCallbackValid(event, ref),
137             [firstParam, secondParam, thirdParam](napi_env env, int& argc, napi_value *argv) {
138                 argc = NapiUtils::ARGC_THREE;
139                 auto status = NapiUtils::SetValue(env, firstParam, argv[NapiUtils::ARGV_FIRST]);
140                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
141                 status = NapiUtils::SetValue(env, secondParam, argv[NapiUtils::ARGV_SECOND]);
142                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
143                 status = NapiUtils::SetValue(env, thirdParam, argv[NapiUtils::ARGV_THIRD]);
144                 CHECK_RETURN_VOID(status == napi_ok, "ControllerCallback SetValue invalid");
145             });
146     }
147 }
148 
HandleErrorEvent(int32_t event,const int32_t errorCode,const std::string & errorMsg)149 void NapiAVCastControllerCallback::HandleErrorEvent(int32_t event, const int32_t errorCode,
150     const std::string& errorMsg)
151 {
152     std::lock_guard<std::mutex> lockGuard(lock_);
153     if (callbacks_[event].empty()) {
154         SLOGE("not register callback event=%{public}d", event);
155         return;
156     }
157     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
158         asyncCallback_->CallWithFunc(*ref, isValid_, CheckCallbackValid(event, ref),
159             [errorCode, errorMsg](napi_env env, int& argc, napi_value *argv) {
160                 napi_status status = napi_create_object(env, argv);
161                 CHECK_RETURN_VOID((status == napi_ok) && (argv != nullptr), "create object failed");
162 
163                 napi_value property = nullptr;
164                 status = NapiUtils::SetValue(env, errorCode, property);
165                 CHECK_RETURN_VOID((status == napi_ok) && (property != nullptr), "create property failed");
166                 status = napi_set_named_property(env, *argv, "code", property);
167                 CHECK_RETURN_VOID(status == napi_ok, "napi_set_named_property failed");
168 
169                 status = NapiUtils::SetValue(env, errorMsg, property);
170                 CHECK_RETURN_VOID((status == napi_ok) && (property != nullptr), "create property failed");
171                 status = napi_set_named_property(env, *argv, "message", property);
172                 CHECK_RETURN_VOID(status == napi_ok, "napi_set_named_property failed");
173             });
174     }
175 }
176 
OnCastPlaybackStateChange(const AVPlaybackState & state)177 void NapiAVCastControllerCallback::OnCastPlaybackStateChange(const AVPlaybackState& state)
178 {
179     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnCastPlaybackStateChange");
180     SLOGI("Start handle OnCastPlaybackStateChange event with state: %{public}d", state.GetState());
181     HandleEvent(EVENT_CAST_PLAYBACK_STATE_CHANGE, state);
182 }
183 
OnMediaItemChange(const AVQueueItem & avQueueItem)184 void NapiAVCastControllerCallback::OnMediaItemChange(const AVQueueItem& avQueueItem)
185 {
186     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnMediaItemChange");
187     SLOGI("Start handle OnMediaItemChange event");
188     HandleEvent(EVENT_CAST_MEDIA_ITEM_CHANGE, avQueueItem);
189 }
190 
OnPlayNext()191 void NapiAVCastControllerCallback::OnPlayNext()
192 {
193     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnPlayNext");
194     SLOGI("Start handle OnPlayNext event");
195     HandleEvent(EVENT_CAST_PLAY_NEXT);
196 }
197 
OnPlayPrevious()198 void NapiAVCastControllerCallback::OnPlayPrevious()
199 {
200     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnPlayPrevious");
201     SLOGI("Start handle OnPlayPrevious event");
202     HandleEvent(EVENT_CAST_PLAY_PREVIOUS);
203 }
204 
OnSeekDone(const int32_t seekNumber)205 void NapiAVCastControllerCallback::OnSeekDone(const int32_t seekNumber)
206 {
207     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnSeekDone");
208     SLOGI("Start handle OnSeekDone event");
209     HandleEvent(EVENT_CAST_SEEK_DONE, seekNumber);
210 }
211 
OnVideoSizeChange(const int32_t width,const int32_t height)212 void NapiAVCastControllerCallback::OnVideoSizeChange(const int32_t width, const int32_t height)
213 {
214     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnVideoSizeChange");
215     SLOGI("Start handle OnVideoSizeChange event");
216     HandleEvent(EVENT_CAST_VIDEO_SIZE_CHANGE, width, height);
217 }
218 
HandlePlayerErrorAPI13(const int32_t errorCode,const std::string & errorMsg)219 void NapiAVCastControllerCallback::HandlePlayerErrorAPI13(const int32_t errorCode, const std::string& errorMsg)
220 {
221     CastExtErrCodeAPI13 jsErr;
222     if (CAST_GENERIC_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
223         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
224         // Generic error
225         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
226         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
227         HandleErrorEvent(EVENT_CAST_GENERIC_ERR, static_cast<int32_t>(jsErr), errorMsg);
228     } else if (CAST_IO_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
229         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
230         // Input/Output errors
231         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
232         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
233         HandleErrorEvent(EVENT_CAST_IO_ERR, static_cast<int32_t>(jsErr), errorMsg);
234     } else if (CAST_PARSING_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
235         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
236         // Content parsing errors
237         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
238         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
239         HandleErrorEvent(EVENT_CAST_PARSING_ERR, static_cast<int32_t>(jsErr), errorMsg);
240     } else if (CAST_DECODE_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
241         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
242         // Decoding errors
243         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
244         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
245         HandleErrorEvent(EVENT_CAST_DECOD_EERR, static_cast<int32_t>(jsErr), errorMsg);
246     } else if (CAST_RENDER_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
247         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
248         // AudioRender errors
249         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
250         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
251         HandleErrorEvent(EVENT_CAST_RENDER_ERR, static_cast<int32_t>(jsErr), errorMsg);
252     } else if (CAST_DRM_ERRCODE_INFOS.count(static_cast<CastErrCode>(errorCode)) != 0 &&
253         CASTERRCODE_TO_EXTERRORCODEAPI13.count(static_cast<CastErrCode>(errorCode)) != 0) {
254         // DRM errors
255         jsErr = CASTERRCODE_TO_EXTERRORCODEAPI13.at(static_cast<CastErrCode>(errorCode));
256         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
257         HandleErrorEvent(EVENT_CAST_DRM_ERR, static_cast<int32_t>(jsErr), errorMsg);
258     } else {
259         SLOGW("Can not match error code, use default");
260         // If error not in map, need add error and should not return default ERROR_CODE_UNSPECIFIED.
261         jsErr = CAST_GENERICERR_EXT_API13_UNSPECIFIED;
262         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
263         HandleErrorEvent(EVENT_CAST_GENERIC_ERR, static_cast<int32_t>(jsErr), errorMsg);
264     }
265 }
266 
OnPlayerError(const int32_t errorCode,const std::string & errorMsg)267 void NapiAVCastControllerCallback::OnPlayerError(const int32_t errorCode, const std::string& errorMsg)
268 {
269     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnPlayerError");
270     SLOGI("Start handle OnPlayerError event");
271     if (static_cast<MediaServiceErrCode>(errorCode) >= MSERR_NO_MEMORY &&
272         static_cast<MediaServiceErrCode>(errorCode) <= MSERR_EXTEND_START) {
273         MediaServiceExtErrCodeAPI9 jsErr;
274         if (MSERRCODE_INFOS.count(static_cast<MediaServiceErrCode>(errorCode)) != 0 &&
275             MSERRCODE_TO_EXTERRORCODEAPI9.count(static_cast<MediaServiceErrCode>(errorCode)) != 0) {
276             jsErr = MSERRCODE_TO_EXTERRORCODEAPI9.at(static_cast<MediaServiceErrCode>(errorCode));
277         } else {
278             SLOGW("Can not match error code, use default");
279             // If error not in map, need add error and should not return default MSERR_EXT_API9_IO.
280             jsErr = MSERR_EXT_API9_IO;
281         }
282         SLOGI("Native errCode: %{public}d, JS errCode: %{public}d", errorCode, static_cast<int32_t>(jsErr));
283         HandleErrorEvent(EVENT_CAST_ERROR, static_cast<int32_t>(jsErr), errorMsg);
284     } else {
285         HandlePlayerErrorAPI13(errorCode, errorMsg);
286     }
287 }
288 
OnEndOfStream(const int32_t isLooping)289 void NapiAVCastControllerCallback::OnEndOfStream(const int32_t isLooping)
290 {
291     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnEndOfStream");
292     SLOGI("Start handle OnEndOfStream event");
293     HandleEvent(EVENT_CAST_END_OF_STREAM, isLooping);
294 }
295 
OnPlayRequest(const AVQueueItem & avQueueItem)296 void NapiAVCastControllerCallback::OnPlayRequest(const AVQueueItem& avQueueItem)
297 {
298     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnPlayRequest");
299     SLOGI("Start handle OnPlayRequest event");
300     HandleEvent(EVENT_CAST_PLAY_REQUEST, avQueueItem);
301 }
302 
OnKeyRequest(const std::string & assetId,const std::vector<uint8_t> & keyRequestData)303 void NapiAVCastControllerCallback::OnKeyRequest(const std::string &assetId, const std::vector<uint8_t> &keyRequestData)
304 {
305     AVSESSION_TRACE_SYNC_START("NapiAVCastControllerCallback::OnKeyRequest");
306     SLOGI("Start handle OnKeyRequest event");
307     HandleEvent(EVENT_CAST_KEY_REQUEST, assetId, keyRequestData);
308 }
309 
OnCastValidCommandChanged(const std::vector<int32_t> & cmds)310 void NapiAVCastControllerCallback::OnCastValidCommandChanged(const std::vector<int32_t>& cmds)
311 {
312     SLOGI("Start handle OnValidCommandChanged event. cmd size:%{public}zd", cmds.size());
313     std::vector<std::string> stringCmds = NapiCastControlCommand::ConvertCommands(cmds);
314     HandleEvent(EVENT_CAST_VALID_COMMAND_CHANGED, stringCmds);
315 }
316 
AddCallback(napi_env env,int32_t event,napi_value callback)317 napi_status NapiAVCastControllerCallback::AddCallback(napi_env env, int32_t event, napi_value callback)
318 {
319     std::lock_guard<std::mutex> lockGuard(lock_);
320     napi_ref ref = nullptr;
321 
322     CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
323         napi_generic_failure, "get callback reference failed");
324     CHECK_AND_RETURN_RET_LOG(ref == nullptr, napi_ok, "callback has been registered");
325     napi_status status = napi_create_reference(env, callback, NapiUtils::ARGC_ONE, &ref);
326     if (status != napi_ok) {
327         SLOGE("napi_create_reference failed");
328         return status;
329     }
330     if (asyncCallback_ == nullptr) {
331         asyncCallback_ = std::make_shared<NapiAsyncCallback>(env);
332         if (asyncCallback_ == nullptr) {
333             SLOGE("no memory");
334             return napi_generic_failure;
335         }
336     }
337     SLOGI("addCallback isValidSet to prevent off, with ref %{public}d, %{public}p, %{public}p", event, &ref, *(&ref));
338     callbacks_[event].push_back(ref);
339     if (isValid_ == nullptr) {
340         SLOGI("addCallback with no isValid_ init");
341         isValid_ = std::make_shared<bool>(true);
342     } else {
343         SLOGI("addCallback with isValid_ set true");
344         *isValid_ = true;
345     }
346     return napi_ok;
347 }
348 
RemoveCallback(napi_env env,int32_t event,napi_value callback)349 napi_status NapiAVCastControllerCallback::RemoveCallback(napi_env env, int32_t event, napi_value callback)
350 {
351     std::lock_guard<std::mutex> lockGuard(lock_);
352     SLOGI("try remove callback for event %{public}d", event);
353     if (callback == nullptr) {
354         SLOGD("Remove callback, the callback is nullptr");
355         for (auto callbackRef = callbacks_[event].begin(); callbackRef != callbacks_[event].end(); ++callbackRef) {
356             napi_status ret = napi_delete_reference(env, *callbackRef);
357             CHECK_AND_RETURN_RET_LOG(ret == napi_ok, ret, "delete callback reference failed");
358             *callbackRef = nullptr;
359         }
360         callbacks_[event].clear();
361         // not remove this logic for play button will not valid when stopcast at media control second page
362         SLOGE("RemoveCallback with isvalid set false when playbackstatechange off");
363         if (event == EVENT_CAST_PLAYBACK_STATE_CHANGE) {
364             if (isValid_ == nullptr) {
365                 SLOGE("remove callback with no isValid_ init");
366                 return napi_ok;
367             }
368             SLOGI("removeCallback with isValid_ set false");
369             *isValid_ = false;
370         }
371         return napi_ok;
372     }
373     napi_ref ref = nullptr;
374     CHECK_AND_RETURN_RET_LOG(napi_ok == NapiUtils::GetRefByCallback(env, callbacks_[event], callback, ref),
375         napi_generic_failure, "get callback reference failed");
376     SLOGI("remove single callback with ref %{public}d, %{public}p, %{public}p", event, &ref, *(&ref));
377     CHECK_AND_RETURN_RET_LOG(ref != nullptr, napi_ok, "callback has been remove");
378     callbacks_[event].remove(ref);
379     return napi_delete_reference(env, ref);
380 }
381 
IsCallbacksEmpty(int32_t event)382 bool NapiAVCastControllerCallback::IsCallbacksEmpty(int32_t event)
383 {
384     return callbacks_[event].empty();
385 }
386 } // namespace OHOS::AVSession
387