1 /*
2 * Copyright (c) 2020-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 "recorder_sink.h"
17 #include <unistd.h>
18 #include <sys/prctl.h>
19 #include "format_interface.h"
20 #include "media_log.h"
21 #include "securec.h"
22
23 namespace OHOS {
24 namespace Media {
25 constexpr uint32_t RECORDER_PARAMS_CNT = 2;
26
27 static int32_t SinkOnError(CallbackHandle privateDataHandle, int32_t errorType, int32_t errorCode);
28 static int32_t SinkOnInfo(CallbackHandle privateDataHandle, int32_t type, int32_t extra);
29
RecorderSink()30 RecorderSink::RecorderSink()
31 :formatMuxerHandle_(nullptr),
32 prepared_(false),
33 started_(false),
34 outputFormat_(OUTPUT_FORMAT_INVALID),
35 outputFd_(-1),
36 outputNextFd_(-1),
37 path_("/userdata"),
38 maxFileSize_(-1),
39 maxDuration_(-1)
40 {
41 FormatInit();
42 MEDIA_INFO_LOG("RecorderSink");
43 }
44
~RecorderSink()45 RecorderSink::~RecorderSink()
46 {
47 threadRunning = false;
48 if (threadId != 0) {
49 MEDIA_INFO_LOG("message thread is still running. kill thread.");
50 pthread_cancel(threadId);
51 }
52 }
53
CheckPrepared()54 int32_t RecorderSink::CheckPrepared()
55 {
56 if (!prepared_) {
57 MEDIA_ERR_LOG("RecorderSink not prepared yet");
58 return ERR_ILLEGAL_STATE;
59 }
60 return SUCCESS;
61 }
62
CheckStarted() const63 int32_t RecorderSink::CheckStarted() const
64 {
65 if (!started_) {
66 MEDIA_ERR_LOG("RecorderSink not started yet");
67 return ERR_ILLEGAL_STATE;
68 }
69 return SUCCESS;
70 }
71
SetOutputPath(const string & path)72 int32_t RecorderSink::SetOutputPath(const string &path)
73 {
74 path_ = path;
75 return SUCCESS;
76 }
77
Prepare()78 int32_t RecorderSink::Prepare()
79 {
80 if (prepared_) {
81 return SUCCESS;
82 }
83
84 FormatOutputConfig outputConfig = {};
85 outputConfig.format = outputFormat_;
86 if (outputFd_ != -1) {
87 outputConfig.type = OUTPUT_TYPE_FD;
88 outputConfig.fd = outputFd_;
89 } else {
90 outputConfig.type = OUTPUT_TYPE_URI;
91 if (memcpy_s(outputConfig.url, URL_LEN, path_.c_str(), path_.length()) != EOK) {
92 MEDIA_ERR_LOG("memcpy_s failed %s", path_.c_str());
93 return ERR_INVALID_PARAM;
94 }
95 }
96 int32_t ret = FormatMuxerCreate(&formatMuxerHandle_, &outputConfig);
97 if (ret != SUCCESS) {
98 MEDIA_ERR_LOG("FormatMuxerCreate failed 0x%x outputFd_:%d", ret, outputFd_);
99 return ret;
100 }
101 prepared_ = true;
102
103 if (maxDuration_ != -1) {
104 ret = FormatMuxerSetMaxFileDuration(formatMuxerHandle_, maxDuration_);
105 if (ret != SUCCESS) {
106 MEDIA_ERR_LOG("FormatMuxersetMaxFileDuration failed 0x%x", ret);
107 }
108 }
109
110 if (maxFileSize_ != -1) {
111 ret = FormatMuxerSetMaxFileSize(formatMuxerHandle_, maxFileSize_);
112 if (ret != SUCCESS) {
113 MEDIA_ERR_LOG("FormatMuxersetMaxFileSize failed 0x%x", ret);
114 }
115 }
116
117 sinkCallback_ = std::make_shared<FormatCallback>();
118 sinkCallback_->privateDataHandle = this;
119 sinkCallback_->OnError = SinkOnError;
120 sinkCallback_->OnInfo = SinkOnInfo;
121 if (FormatMuxerSetCallBack(formatMuxerHandle_, sinkCallback_.get()) != 0) {
122 MEDIA_ERR_LOG("FormatMuxerSetCallBack failed");
123 }
124
125 return SUCCESS;
126 }
127
AddTrackSource(const TrackSource & trackSource,int32_t & trackId)128 int32_t RecorderSink::AddTrackSource(const TrackSource &trackSource, int32_t &trackId)
129 {
130 int32_t trackIndex = FormatMuxerAddTrack(formatMuxerHandle_, &trackSource);
131 if (trackIndex < 0) {
132 MEDIA_ERR_LOG("FormatMuxerAddTrack failed 0x%x", trackIndex);
133 return trackIndex;
134 }
135 trackId = trackIndex;
136 return SUCCESS;
137 }
138
WriteData(int32_t trackId,FormatFrame & frameData) const139 int32_t RecorderSink::WriteData(int32_t trackId, FormatFrame &frameData) const
140 {
141 if (CheckStarted() != SUCCESS) {
142 MEDIA_ERR_LOG("RecorderSink writeData checkStarted failed.");
143 return ERR_ILLEGAL_STATE;
144 }
145 return FormatMuxerWriteFrame(formatMuxerHandle_, &frameData);
146 }
147
SetOutputFormat(OutputFormat format)148 int32_t RecorderSink::SetOutputFormat(OutputFormat format)
149 {
150 outputFormat_ = format;
151 return SUCCESS;
152 }
153
SetOutputFile(int32_t fd)154 int32_t RecorderSink::SetOutputFile(int32_t fd)
155 {
156 outputFd_ = fd;
157 return SUCCESS;
158 }
159
SetNextOutputFile(int32_t fd)160 int32_t RecorderSink::SetNextOutputFile(int32_t fd)
161 {
162 if (CheckPrepared() != SUCCESS) {
163 return ERR_ILLEGAL_STATE;
164 }
165 outputNextFd_ = fd;
166 return FormatMuxerSetNextOutputFile(formatMuxerHandle_, fd);
167 }
168
SetMaxDuration(int64_t duration)169 int32_t RecorderSink::SetMaxDuration(int64_t duration)
170 {
171 if (started_) {
172 MEDIA_ERR_LOG("RecorderSink is started ,SetMaxDuration must be setted before Prepare");
173 return ERR_ILLEGAL_STATE;
174 }
175 if (duration <= 0) {
176 MEDIA_ERR_LOG("invalid MaxDuration size:%lld", duration);
177 return ERR_INVALID_PARAM;
178 }
179 maxDuration_ = duration;
180 if (prepared_) {
181 return FormatMuxerSetMaxFileDuration(formatMuxerHandle_, duration);
182 }
183 return SUCCESS;
184 }
185
SetMaxFileSize(int64_t size)186 int32_t RecorderSink::SetMaxFileSize(int64_t size)
187 {
188 if (started_) {
189 MEDIA_ERR_LOG("RecorderSink is started , SetMaxFileSize must setted before Prepare");
190 return ERR_ILLEGAL_STATE;
191 }
192 if (size <= 0) {
193 MEDIA_ERR_LOG("invalid MaxFileSize size:%lld", size);
194 return ERR_INVALID_PARAM;
195 }
196 maxFileSize_ = size;
197 if (prepared_) {
198 return FormatMuxerSetMaxFileSize(formatMuxerHandle_, size);
199 }
200 return SUCCESS;
201 }
202
SetOrientationHint(int degrees)203 int32_t RecorderSink::SetOrientationHint(int degrees)
204 {
205 if (CheckPrepared() != SUCCESS) {
206 return ERR_ILLEGAL_STATE;
207 }
208 return FormatMuxerSetOrientation(formatMuxerHandle_, degrees);
209 }
210
SetLocation(int latitude,int longitude)211 int32_t RecorderSink::SetLocation(int latitude, int longitude)
212 {
213 if (CheckPrepared() != SUCCESS) {
214 return ERR_ILLEGAL_STATE;
215 }
216 return FormatMuxerSetLocation(formatMuxerHandle_, latitude, longitude);
217 }
218
SendCallbackInfo(int32_t type,int32_t extra)219 int32_t RecorderSink::SendCallbackInfo(int32_t type, int32_t extra)
220 {
221 if (recCallBack_ == nullptr || recCallBack_.get() == nullptr) {
222 MEDIA_ERR_LOG("sink: is nullptr");
223 return ERR_INVALID_PARAM;
224 }
225 switch (type) {
226 case MUXER_INFO_MAX_DURATION_APPROACHING:
227 case MUXER_INFO_MAX_FILESIZE_APPROACHING:
228 case MUXER_INFO_MAX_DURATION_REACHED:
229 case MUXER_INFO_MAX_FILESIZE_REACHED:
230 case MUXER_INFO_NEXT_OUTPUT_FILE_STARTED:
231 case MUXER_INFO_FILE_SPLIT_FINISHED:
232 case MUXER_INFO_FILE_START_TIME_MS:
233 case MUXER_INFO_NEXT_FILE_FD_NOT_SET:
234 case MUXER_INFO_NO_FRAME_DATA:
235 #ifndef ENABLE_PASSTHROUGH_MODE
236 if (type == MUXER_INFO_NEXT_OUTPUT_FILE_STARTED && outputFd_ != -1 && outputNextFd_ != -1) {
237 close(outputFd_);
238 outputFd_ = outputNextFd_;
239 }
240 #endif
241 recCallBack_->OnInfo(type, extra);
242 return SUCCESS;
243 default:
244 MEDIA_ERR_LOG("pass event type %d", type);
245 return ERR_INVALID_PARAM;
246 }
247 }
248
SendCallbackError(int32_t errorType,int32_t errorCode)249 int32_t RecorderSink::SendCallbackError(int32_t errorType, int32_t errorCode)
250 {
251 MEDIA_INFO_LOG("errorType:%d", errorType);
252 if (recCallBack_ == nullptr || recCallBack_.get() == nullptr) {
253 MEDIA_ERR_LOG("sink: is nullptr");
254 return ERR_INVALID_PARAM;
255 }
256 switch (errorType) {
257 case ERROR_CREATE_FILE_FAIL:
258 case ERROR_WRITE_FILE_FAIL:
259 case ERROR_CLOSE_FILE_FAIL:
260 case ERROR_READ_DATA_ERROR:
261 case ERROR_INTERNAL_OPERATION_FAIL:
262 case ERROR_UNKNOWN:
263 MEDIA_ERR_LOG("recorder Callback error");
264 recCallBack_->OnError(errorType, errorCode);
265 return SUCCESS;
266 default:
267 MEDIA_ERR_LOG("pass event err %d", errorType);
268 return ERR_INVALID_PARAM;
269 }
270 }
271
SinkOnError(CallbackHandle privateDataHandle,int32_t errorType,int32_t errorCode)272 static int32_t SinkOnError(CallbackHandle privateDataHandle, int32_t errorType, int32_t errorCode)
273 {
274 RecorderSink *sink = reinterpret_cast<RecorderSink *>(privateDataHandle);
275 if (sink == nullptr) {
276 MEDIA_ERR_LOG("sink: is nullptr");
277 return ERR_INVALID_PARAM;
278 }
279 MessageData data;
280 data.event = errorType;
281 data.extra = errorCode;
282 data.isInfo = false;
283 sink->messageQueue.push(data);
284 sem_post(&sink->sem);
285 return 0;
286 }
287
SinkOnInfo(CallbackHandle privateDataHandle,int32_t type,int32_t extra)288 static int32_t SinkOnInfo(CallbackHandle privateDataHandle, int32_t type, int32_t extra)
289 {
290 MEDIA_INFO_LOG("type:%d", type);
291 RecorderSink *sink = reinterpret_cast<RecorderSink *>(privateDataHandle);
292 if (sink == nullptr) {
293 MEDIA_ERR_LOG("sink: is nullptr");
294 return ERR_INVALID_PARAM;
295 }
296 MessageData data;
297 data.event = type;
298 data.extra = extra;
299 data.isInfo = true;
300 sink->messageQueue.push(data);
301 sem_post(&sink->sem);
302 return 0;
303 }
304
305
SetRecorderCallback(const std::shared_ptr<RecorderCallback> & callback)306 int32_t RecorderSink::SetRecorderCallback(const std::shared_ptr<RecorderCallback> &callback)
307 {
308 recCallBack_ = callback;
309 return SUCCESS;
310 }
311
MessageThread(void * arg)312 static void *MessageThread(void *arg)
313 {
314 RecorderSink *sink = (RecorderSink *)arg;
315 sink->threadRunning = true;
316 MEDIA_INFO_LOG("Message thread start.");
317 pthread_detach(pthread_self());
318 prctl(PR_SET_NAME, "RecorderMessageThread", 0, 0, 0);
319 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, nullptr);
320 int32_t ret = sem_init(&sink->sem, 0, 0);
321 if (ret != 0) {
322 MEDIA_ERR_LOG("sem_init failed.");
323 return nullptr;
324 }
325
326 while (sink->threadRunning) {
327 sem_wait(&sink->sem);
328 if (sink->messageQueue.empty()) {
329 continue;
330 }
331
332 MessageData data = sink->messageQueue.front();
333 sink->messageQueue.pop();
334 if (data.isInfo) {
335 sink->SendCallbackInfo(data.event, data.extra);
336 } else {
337 sink->SendCallbackError(data.event, data.extra);
338 }
339 }
340
341 sem_destroy(&sink->sem);
342
343 sink->threadId = 0;
344 MEDIA_INFO_LOG("Message thread exit.");
345 return nullptr;
346 }
347
Start()348 int32_t RecorderSink::Start()
349 {
350 if (CheckPrepared() != SUCCESS) {
351 return ERR_ILLEGAL_STATE;
352 }
353 if (started_) {
354 return SUCCESS;
355 }
356
357 MEDIA_INFO_LOG("FormatMuxerStart");
358 if (sinkCallback_ != nullptr && sinkCallback_.get() != nullptr) {
359 int32_t ret = pthread_create(&threadId, nullptr, MessageThread, this);
360 if (ret != 0) {
361 MEDIA_ERR_LOG("create message thread failed %d", ret);
362 return -1;
363 }
364 }
365
366 int32_t ret = FormatMuxerStart(formatMuxerHandle_);
367 if (ret != SUCCESS) {
368 MEDIA_ERR_LOG("FormatMuxerStart failed 0x%x", ret);
369 return ret;
370 }
371 started_ = true;
372 return SUCCESS;
373 }
374
CloseFd()375 void RecorderSink::CloseFd()
376 {
377 #ifndef ENABLE_PASSTHROUGH_MODE
378 if (outputFd_ > 0) {
379 FILE *fp = fdopen(outputFd_, "r");
380 if (fp == nullptr) {
381 MEDIA_ERR_LOG("fdopen failed");
382 return;
383 }
384 fflush(fp);
385 fsync(outputFd_);
386 fclose(fp);
387 outputFd_ = -1;
388 }
389 if (outputNextFd_ > 0) {
390 close(outputNextFd_);
391 outputNextFd_ = -1;
392 }
393 #endif
394 }
395
Stop(bool block)396 int32_t RecorderSink::Stop(bool block)
397 {
398 if (!started_) {
399 CloseFd();
400 MEDIA_INFO_LOG("RecorderSink is stoped or not started");
401 return SUCCESS;
402 }
403 int32_t ret = FormatMuxerStop(formatMuxerHandle_, block);
404 if (ret != SUCCESS) {
405 MEDIA_ERR_LOG("FormatMuxerStop failed 0x%x", ret);
406 return ret;
407 }
408 if (threadId != 0) {
409 threadRunning = false;
410 sem_post(&sem);
411 }
412 CloseFd();
413 started_ = false;
414
415 return SUCCESS;
416 }
417
Reset()418 int32_t RecorderSink::Reset()
419 {
420 int32_t ret;
421 if (started_) {
422 ret = Stop(false);
423 if (ret != SUCCESS) {
424 MEDIA_ERR_LOG("Stop failed 0x%x", ret);
425 return ret;
426 }
427 }
428 if (prepared_) {
429 ret = Release();
430 if (ret != SUCCESS) {
431 MEDIA_ERR_LOG("Release failed 0x%x", ret);
432 return ret;
433 }
434 }
435 CloseFd();
436 prepared_ = false;
437 outputFormat_ = OUTPUT_FORMAT_INVALID;
438 maxFileSize_ = -1;
439 maxDuration_ = -1;
440 return SUCCESS;
441 }
442
Release()443 int32_t RecorderSink::Release()
444 {
445 int32_t ret;
446 if (!prepared_) {
447 return SUCCESS;
448 }
449 if (started_) {
450 ret = Stop(false);
451 if (ret != SUCCESS) {
452 MEDIA_ERR_LOG("Stop failed 0x%x", ret);
453 return ret;
454 }
455 started_ = false;
456 }
457 CloseFd();
458 ret = FormatMuxerDestroy(formatMuxerHandle_);
459 if (ret != SUCCESS) {
460 MEDIA_ERR_LOG("FormatMuxerDestroy failed ret:%d", ret);
461 return ret;
462 }
463 formatMuxerHandle_ = nullptr;
464 prepared_ = false;
465 return SUCCESS;
466 }
467
SetFileSplitDuration(ManualSplitType type,int64_t timestamp,uint32_t duration)468 int32_t RecorderSink::SetFileSplitDuration(ManualSplitType type, int64_t timestamp, uint32_t duration)
469 {
470 if (CheckStarted() != SUCCESS) {
471 return ERR_ILLEGAL_STATE;
472 }
473 return FormatMuxerSetFileSplitDuration(formatMuxerHandle_, type, timestamp, duration);
474 }
475
SetParameter(int32_t trackId,const Format & format)476 int32_t RecorderSink::SetParameter(int32_t trackId, const Format &format)
477 {
478 int32_t itemNum = 0;
479 ParameterItem items[RECORDER_PARAMS_CNT];
480 (void)memset_s(items, sizeof(ParameterItem) * RECORDER_PARAMS_CNT, 0x00,
481 sizeof(ParameterItem) * RECORDER_PARAMS_CNT);
482 int32_t value;
483 if (format.GetIntValue(RECORDER_PRE_CACHE_DURATION, value)) {
484 items[itemNum].key = KEY_TYPE_PRE_CACHE;
485 items[itemNum].value.s32Value = value;
486 items[itemNum].size = sizeof(int32_t);
487 itemNum++;
488 } else {
489 return ERR_INVALID_PARAM;
490 }
491 return FormatMuxerSetParameter(formatMuxerHandle_, trackId, items, itemNum);
492 }
493 } // namespace Media
494 } // namespace OHOS
495