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