1 /*
2 * Copyright (C) 2021 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_demo.h"
17 #include <random>
18 #include <sync_fence.h>
19 #include "scope_guard.h"
20 #include "media_errors.h"
21 #include "media_log.h"
22 #include "display_type.h"
23 #include "securec.h"
24 #include "surface_utils.h"
25
26 namespace {
27 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "NapiDemo"};
28 constexpr uint32_t STRIDE_ALIGN = 8;
29 }
30
31 namespace OHOS {
32 namespace Media {
33 thread_local napi_ref NapiDemo::constructor_ = nullptr;
34 const std::string CLASS_NAME = "MediaTest";
35
NapiDemo()36 NapiDemo::NapiDemo()
37 {
38 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
39 }
40
~NapiDemo()41 NapiDemo::~NapiDemo()
42 {
43 if (wrap_ != nullptr) {
44 napi_delete_reference(env_, wrap_);
45 }
46 MEDIA_LOGD("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
47 }
48
Init(napi_env env,napi_value exports)49 napi_value NapiDemo::Init(napi_env env, napi_value exports)
50 {
51 napi_property_descriptor properties[] = {
52 DECLARE_NAPI_FUNCTION("startStream", StartStream),
53 DECLARE_NAPI_FUNCTION("closeStream", CloseStream),
54 DECLARE_NAPI_FUNCTION("setResolution", SetResolution),
55 DECLARE_NAPI_FUNCTION("setFrameCount", SetFrameCount),
56 DECLARE_NAPI_FUNCTION("setFrameRate", SetFrameRate),
57 };
58 napi_property_descriptor staticProperty[] = {
59 DECLARE_NAPI_STATIC_FUNCTION("createMediaTest", CreateMediaTest),
60 };
61
62 napi_value constructor = nullptr;
63 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
64 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
65 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define AudioDecodeProcessor class");
66
67 status = napi_create_reference(env, constructor, 1, &constructor_);
68 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor");
69
70 status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
71 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor");
72
73 status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
74 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function");
75
76 MEDIA_LOGD("Init success");
77 return exports;
78 }
79
Constructor(napi_env env,napi_callback_info info)80 napi_value NapiDemo::Constructor(napi_env env, napi_callback_info info)
81 {
82 napi_value result = nullptr;
83 napi_get_undefined(env, &result);
84
85 napi_value jsThis = nullptr;
86 size_t argCount = 0;
87 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
88 CHECK_AND_RETURN_RET(status == napi_ok, result);
89
90 NapiDemo *napiDemo = new(std::nothrow) NapiDemo();
91 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
92
93 napiDemo->env_ = env;
94
95 status = napi_wrap(env, jsThis, reinterpret_cast<void *>(napiDemo),
96 NapiDemo::Destructor, nullptr, &(napiDemo->wrap_));
97 if (status != napi_ok) {
98 delete napiDemo;
99 MEDIA_LOGE("Failed to wrap native instance");
100 return result;
101 }
102
103 MEDIA_LOGD("Constructor success");
104 return jsThis;
105 }
106
Destructor(napi_env env,void * nativeObject,void * finalize)107 void NapiDemo::Destructor(napi_env env, void *nativeObject, void *finalize)
108 {
109 (void)env;
110 (void)finalize;
111 if (nativeObject != nullptr) {
112 delete reinterpret_cast<NapiDemo *>(nativeObject);
113 }
114 MEDIA_LOGD("Destructor success");
115 }
116
CreateMediaTest(napi_env env,napi_callback_info info)117 napi_value NapiDemo::CreateMediaTest(napi_env env, napi_callback_info info)
118 {
119 MEDIA_LOGD("Enter CreateMediaTest");
120 napi_value result = nullptr;
121 napi_get_undefined(env, &result);
122
123 napi_value constructor = nullptr;
124 napi_status status = napi_get_reference_value(env, constructor_, &constructor);
125 CHECK_AND_RETURN_RET(status == napi_ok, result);
126
127 status = napi_new_instance(env, constructor, 0, nullptr, &result);
128 CHECK_AND_RETURN_RET(status == napi_ok, result);
129
130 MEDIA_LOGD("CreateMediaTest success");
131 return result;
132 }
133
StartStream(napi_env env,napi_callback_info info)134 napi_value NapiDemo::StartStream(napi_env env, napi_callback_info info)
135 {
136 MEDIA_LOGD("Enter StartStream");
137 napi_value result = nullptr;
138 napi_get_undefined(env, &result);
139
140 napi_value jsThis = nullptr;
141 napi_value args[1] = {nullptr};
142 size_t argCount = 1;
143 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
144 CHECK_AND_RETURN_RET(status == napi_ok && jsThis != nullptr, result);
145
146 NapiDemo *napiDemo = nullptr;
147 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiDemo));
148 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
149
150 std::string surfaceId = "";
151 napi_valuetype type = napi_undefined;
152 if (args[0] != nullptr && napi_typeof(env, args[0], &type) == napi_ok && type == napi_string) {
153 surfaceId = GetStringArgument(env, args[0]);
154 }
155
156 uint64_t surfaceUniqueId = 0;
157 StrToUint64(surfaceId, surfaceUniqueId);
158
159 napiDemo->producerSurface_ = SurfaceUtils::GetInstance()->GetSurface(surfaceUniqueId);
160 CHECK_AND_RETURN_RET(napiDemo->producerSurface_ != nullptr, result);
161
162 if (napiDemo->bufferThread_ != nullptr) {
163 napiDemo->isStart_.store(false);
164 napiDemo->bufferThread_->join();
165 napiDemo->bufferThread_.reset();
166 napiDemo->bufferThread_ = nullptr;
167 }
168
169 napiDemo->isStart_.store(true);
170 napiDemo->requestConfig_ = {
171 .width = napiDemo->width_,
172 .height = napiDemo->height_,
173 .strideAlignment = STRIDE_ALIGN,
174 .format = PIXEL_FMT_YCRCB_420_SP,
175 .usage = HBM_USE_CPU_READ | HBM_USE_CPU_WRITE | HBM_USE_MEM_DMA,
176 .timeout = 0
177 };
178
179 napiDemo->flushConfig_ = {
180 .damage = {
181 .x = 0,
182 .y = 0,
183 .w = napiDemo->width_,
184 .h = napiDemo->height_
185 },
186 .timestamp = 0
187 };
188 napiDemo->bufferThread_ = std::make_unique<std::thread>(&NapiDemo::BufferLoop, napiDemo);
189
190 MEDIA_LOGD("StartStream success");
191 return result;
192 }
193
CloseStream(napi_env env,napi_callback_info info)194 napi_value NapiDemo::CloseStream(napi_env env, napi_callback_info info)
195 {
196 MEDIA_LOGD("Enter CloseStream");
197 napi_value result = nullptr;
198 napi_get_undefined(env, &result);
199
200 napi_value jsThis = nullptr;
201 size_t argCount = 0;
202 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
203 CHECK_AND_RETURN_RET(status == napi_ok && jsThis != nullptr, result);
204
205 NapiDemo *napiDemo = nullptr;
206 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiDemo));
207 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
208
209 napiDemo->isStart_.store(false);
210 if (napiDemo->bufferThread_ != nullptr) {
211 napiDemo->bufferThread_->join();
212 napiDemo->bufferThread_.reset();
213 napiDemo->bufferThread_ = nullptr;
214 }
215
216 MEDIA_LOGD("CloseStream success");
217 return result;
218 }
219
SetResolution(napi_env env,napi_callback_info info)220 napi_value NapiDemo::SetResolution(napi_env env, napi_callback_info info)
221 {
222 MEDIA_LOGD("Enter SetResolution");
223 napi_value result = nullptr;
224 napi_get_undefined(env, &result);
225
226 napi_value jsThis = nullptr;
227 napi_value args[2] = {nullptr};
228 size_t argCount = 2;
229 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
230 CHECK_AND_RETURN_RET(status == napi_ok && jsThis != nullptr, result);
231
232 NapiDemo *napiDemo = nullptr;
233 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiDemo));
234 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
235
236 napi_valuetype type = napi_undefined;
237 if (args[0] != nullptr && napi_typeof(env, args[0], &type) == napi_ok && type == napi_number) {
238 (void)napi_get_value_int32(env, args[0], &napiDemo->width_);
239 }
240
241 if (args[1] != nullptr && napi_typeof(env, args[1], &type) == napi_ok && type == napi_number) {
242 (void)napi_get_value_int32(env, args[1], &napiDemo->height_);
243 }
244
245 CHECK_AND_RETURN_RET(napiDemo->width_ > 0 && napiDemo->height_ > 0, result);
246 MEDIA_LOGD("SetResolution success, width:%{public}d, height:%{public}d", napiDemo->width_, napiDemo->height_);
247
248 return result;
249 }
250
SetFrameCount(napi_env env,napi_callback_info info)251 napi_value NapiDemo::SetFrameCount(napi_env env, napi_callback_info info)
252 {
253 MEDIA_LOGD("Enter SetFrameCount");
254 napi_value result = nullptr;
255 napi_get_undefined(env, &result);
256
257 napi_value jsThis = nullptr;
258 napi_value args[1] = {nullptr};
259 size_t argCount = 1;
260 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
261 CHECK_AND_RETURN_RET(status == napi_ok && jsThis != nullptr, result);
262
263 NapiDemo *napiDemo = nullptr;
264 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiDemo));
265 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
266
267 napi_valuetype type = napi_undefined;
268 if (args[0] != nullptr && napi_typeof(env, args[0], &type) == napi_ok && type == napi_number) {
269 (void)napi_get_value_int32(env, args[0], &napiDemo->totalFrameCount_);
270 }
271
272 CHECK_AND_RETURN_RET(napiDemo->totalFrameCount_ > 0, result);
273 MEDIA_LOGD("SetFrameCount success:%{public}d", napiDemo->totalFrameCount_);
274 return result;
275 }
276
SetFrameRate(napi_env env,napi_callback_info info)277 napi_value NapiDemo::SetFrameRate(napi_env env, napi_callback_info info)
278 {
279 MEDIA_LOGD("Enter SetFrameRate");
280 napi_value result = nullptr;
281 napi_get_undefined(env, &result);
282
283 napi_value jsThis = nullptr;
284 napi_value args[1] = {nullptr};
285 size_t argCount = 1;
286 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
287 CHECK_AND_RETURN_RET(status == napi_ok && jsThis != nullptr, result);
288
289 NapiDemo *napiDemo = nullptr;
290 (void)napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiDemo));
291 CHECK_AND_RETURN_RET(napiDemo != nullptr, result);
292
293 napi_valuetype type = napi_undefined;
294 if (args[0] != nullptr && napi_typeof(env, args[0], &type) == napi_ok && type == napi_number) {
295 (void)napi_get_value_int32(env, args[0], &napiDemo->frameRate_);
296 }
297
298 CHECK_AND_RETURN_RET(napiDemo->frameRate_ > 0, result);
299 MEDIA_LOGD("SetFrameRate success:%{public}d", napiDemo->frameRate_);
300 return result;
301 }
302
StrToUint64(const std::string & str,uint64_t & value)303 bool NapiDemo::StrToUint64(const std::string &str, uint64_t &value)
304 {
305 if (str.empty() || !isdigit(str.front())) {
306 return false;
307 }
308
309 char *end = nullptr;
310 auto addr = str.data();
311 auto result = strtoull(addr, &end, 10); // decimal
312 if (end == addr || end[0] != '\0') {
313 return false;
314 }
315
316 value = result;
317 return true;
318 }
319
GetStringArgument(napi_env env,napi_value value)320 std::string NapiDemo::GetStringArgument(napi_env env, napi_value value)
321 {
322 std::string strValue = "";
323 size_t bufLength = 0;
324 napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &bufLength);
325 if (status == napi_ok && bufLength > 0 && bufLength < PATH_MAX) {
326 char *buffer = (char *)malloc((bufLength + 1) * sizeof(char));
327 CHECK_AND_RETURN_RET(buffer != nullptr, strValue);
328 status = napi_get_value_string_utf8(env, value, buffer, bufLength + 1, &bufLength);
329 if (status == napi_ok) {
330 MEDIA_LOGD("argument = %{public}s", buffer);
331 strValue = buffer;
332 }
333 free(buffer);
334 buffer = nullptr;
335 }
336 return strValue;
337 }
338
BufferLoop()339 void NapiDemo::BufferLoop()
340 {
341 pts_ = 0;
342 color_ = 0xFF;
343 count_ = 0;
344 CHECK_AND_RETURN(frameRate_ > 0);
345 const uint32_t sleepTime = 1000000 / frameRate_; // 1s = 1000000us
346 const int64_t interval = 1000000000 / frameRate_; // 1s = 1000000000ns
347 const uint32_t bufferSize = width_ * height_ * 3 / 2;
348 while (count_ <= totalFrameCount_) {
349 if (!isStart_.load()) {
350 break;
351 }
352 usleep(sleepTime);
353
354 OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
355 int32_t releaseFence = 0;
356
357 CHECK_AND_BREAK(producerSurface_ != nullptr);
358 if (producerSurface_->RequestBuffer(buffer, releaseFence, requestConfig_) != SURFACE_ERROR_OK) {
359 continue;
360 }
361 CHECK_AND_BREAK(buffer != nullptr);
362
363 ON_SCOPE_EXIT(0) { (void)producerSurface_->CancelBuffer(buffer); };
364
365 sptr<SyncFence> tempFence = new SyncFence(releaseFence);
366 tempFence->Wait(100); // 100ms
367
368 auto addr = static_cast<uint8_t *>(buffer->GetVirAddr());
369 CHECK_AND_BREAK(addr != nullptr);
370 CHECK_AND_BREAK(bufferSize <= buffer->GetSize());
371 CHECK_AND_BREAK(memset_s(addr, buffer->GetSize(), color_, bufferSize) == EOK);
372
373 srand(static_cast<int32_t>(time(0)));
374
375 if (count_ == totalFrameCount_) {
376 (void)buffer->ExtraSet("endOfStream", true);
377 } else {
378 (void)buffer->ExtraSet("endOfStream", false);
379 }
380
381 (void)buffer->ExtraSet("timeStamp", pts_);
382
383 count_++;
384 color_ = color_ <= 0 ? 0xFF : (color_ - 1);
385 pts_ += interval;
386
387 CANCEL_SCOPE_EXIT_GUARD(0);
388
389 MEDIA_LOGD("FlushBuffer %{public}d", count_);
390 (void)producerSurface_->FlushBuffer(buffer, -1, flushConfig_);
391 }
392 }
393 } // namespace Media
394 } // namespace OHOS
395