1 /*
2 * Copyright (C) 2024 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 "video_composition_callback_imp.h"
17
18 #include "media_log.h"
19 #include "media_file_utils.h"
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include "medialibrary_errno.h"
24 #include "photo_file_utils.h"
25 #include "directory_ex.h"
26
27 using std::string;
28
29 namespace OHOS {
30 namespace Media {
31 static const mode_t CHOWN_RW_USR_GRP = 0600;
32 static const int32_t LOG_FREQUENCY = 10;
33 std::unordered_map<uint32_t, std::shared_ptr<VideoEditor>> VideoCompositionCallbackImpl::editorMap_;
34 std::queue<VideoCompositionCallbackImpl::Task> VideoCompositionCallbackImpl::waitQueue_;
35 int32_t VideoCompositionCallbackImpl::curWorkerNum_ = 0;
36 std::mutex VideoCompositionCallbackImpl::mutex_;
37
CheckDirPathReal(const std::string & filePath)38 static int32_t CheckDirPathReal(const std::string &filePath)
39 {
40 string dirPath;
41 auto index = filePath.rfind('/');
42 CHECK_AND_RETURN_RET_LOG(index != std::string::npos, E_HAS_FS_ERROR,
43 "find split for last string failed, %{private}s, errno: %{public}d", filePath.c_str(), errno);
44 dirPath = filePath.substr(0, index);
45 string absDirPath;
46 CHECK_AND_RETURN_RET_LOG(PathToRealPath(dirPath, absDirPath),
47 E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", dirPath.c_str(), errno);
48 return E_OK;
49 }
50
VideoCompositionCallbackImpl()51 VideoCompositionCallbackImpl::VideoCompositionCallbackImpl() {}
52
onResult(VEFResult result,VEFError errorCode)53 void VideoCompositionCallbackImpl::onResult(VEFResult result, VEFError errorCode)
54 {
55 close(inputFileFd_);
56 close(outputFileFd_);
57 editorMap_.erase(inputFileFd_);
58 size_t lastSlash = videoPath_.rfind('.');
59 string sourceImagePath = videoPath_.substr(0, lastSlash) + ".jpg";
60 string sourceVideoPath = MediaFileUtils::GetMovingPhotoVideoPath(sourceImagePath);
61 if (errorCode != VEFError::ERR_OK) {
62 mutex_.lock();
63 --curWorkerNum_;
64 mutex_.unlock();
65 MEDIA_ERR_LOG("VideoCompositionCallbackImpl onResult error:%{public}d", (int32_t)errorCode);
66 // Video Composite failed save sourceVideo to photo directory
67 CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(sourceVideoPath, videoPath_),
68 "Copy sourceVideoPath to videoPath, path:%{private}s", sourceVideoPath.c_str());
69 return;
70 }
71
72 mutex_.lock();
73 if (waitQueue_.empty()) {
74 --curWorkerNum_;
75 mutex_.unlock();
76 } else {
77 Task task = std::move(waitQueue_.front());
78 waitQueue_.pop();
79 mutex_.unlock();
80 if (CallStartComposite(task.sourceVideoPath_, task.videoPath_, task.editData_) != E_OK) {
81 mutex_.lock();
82 --curWorkerNum_;
83 mutex_.unlock();
84 MEDIA_ERR_LOG("Failed to CallStartComposite, path:%{private}s", task.videoPath_.c_str());
85 CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(task.sourceVideoPath_, task.videoPath_),
86 "Copy sourceVideoPath to videoPath, path:%{private}s", task.sourceVideoPath_.c_str());
87 }
88 }
89 }
90
onProgress(uint32_t progress)91 void VideoCompositionCallbackImpl::onProgress(uint32_t progress)
92 {
93 if (!(progress % LOG_FREQUENCY)) {
94 MEDIA_INFO_LOG("VideoCompositionCallbackImpl onProcess:%{public}d, tempPath:%{public}s",
95 (int32_t)progress, videoPath_.c_str());
96 }
97 }
98
CallStartComposite(const std::string & sourceVideoPath,const std::string & videoPath,const std::string & effectDescription)99 int32_t VideoCompositionCallbackImpl::CallStartComposite(const std::string& sourceVideoPath,
100 const std::string& videoPath, const std::string& effectDescription)
101 {
102 MEDIA_INFO_LOG("Call StartComposite begin, sourceVideoPath:%{public}s", sourceVideoPath.c_str());
103 string absSourceVideoPath;
104 CHECK_AND_RETURN_RET_LOG(PathToRealPath(sourceVideoPath, absSourceVideoPath), E_HAS_FS_ERROR,
105 "file is not real path, file path: %{private}s, errno: %{public}d", sourceVideoPath.c_str(), errno);
106 int32_t inputFileFd = open(absSourceVideoPath.c_str(), O_RDONLY);
107 CHECK_AND_RETURN_RET_LOG(inputFileFd != -1, E_ERR, "Open failed for inputFileFd file, errno: %{public}d", errno);
108
109 if (CheckDirPathReal(videoPath) != E_OK) {
110 MEDIA_ERR_LOG("dirFile is not real path, file path: %{private}s, errno: %{public}d",
111 videoPath.c_str(), errno);
112 close(inputFileFd);
113 return E_HAS_FS_ERROR;
114 }
115 int32_t outputFileFd = open(videoPath.c_str(), O_WRONLY|O_CREAT, CHOWN_RW_USR_GRP);
116 if (outputFileFd == -1) {
117 close(inputFileFd);
118 MEDIA_ERR_LOG("Open failed for outputFileFd file, errno: %{public}d", errno);
119 return E_ERR;
120 }
121
122 auto callBack = std::make_shared<VideoCompositionCallbackImpl>();
123 auto editor = VideoEditorFactory::CreateVideoEditor();
124 if (editor == nullptr) {
125 close(inputFileFd);
126 close(outputFileFd);
127 MEDIA_ERR_LOG("CreateEditor failed with error");
128 return E_ERR;
129 }
130 callBack->inputFileFd_ = inputFileFd;
131 callBack->outputFileFd_ = outputFileFd;
132 callBack->videoPath_ = videoPath;
133
134 VEFError error = editor->AppendVideoFile(inputFileFd, effectDescription);
135 if (error != VEFError::ERR_OK) {
136 close(inputFileFd);
137 close(outputFileFd);
138 editor = nullptr;
139 MEDIA_ERR_LOG("AppendVideoFile failed with error: %{public}d", (int32_t)error);
140 return E_ERR;
141 }
142 auto compositionOptions = std::make_shared<CompositionOptions>(outputFileFd, callBack);
143 error = editor->StartComposite(compositionOptions);
144 if (error != VEFError::ERR_OK) {
145 close(inputFileFd);
146 close(outputFileFd);
147 editor = nullptr;
148 MEDIA_ERR_LOG("StartComposite failed with error: %{public}d", (int32_t)error);
149 return E_ERR;
150 }
151 callBack->editorMap_[inputFileFd] = editor;
152 return E_OK;
153 }
154
AddCompositionTask(std::string & assetPath,std::string & editData)155 void VideoCompositionCallbackImpl::AddCompositionTask(std::string& assetPath, std::string& editData)
156 {
157 string sourceImagePath = PhotoFileUtils::GetEditDataSourcePath(assetPath);
158 string videoPath = MediaFileUtils::GetMovingPhotoVideoPath(assetPath);
159 string sourceVideoPath = MediaFileUtils::GetMovingPhotoVideoPath(sourceImagePath);
160
161 mutex_.lock();
162 if (curWorkerNum_ < MAX_CONCURRENT_NUM) {
163 ++curWorkerNum_;
164 mutex_.unlock();
165 if (CallStartComposite(sourceVideoPath, videoPath, editData) != E_OK) {
166 mutex_.lock();
167 --curWorkerNum_;
168 mutex_.unlock();
169 MEDIA_ERR_LOG("Failed to CallStartComposite, path:%{private}s", videoPath.c_str());
170 CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(sourceVideoPath, videoPath),
171 "Copy sourceVideoPath to videoPath, path:%{private}s", sourceVideoPath.c_str());
172 }
173 } else {
174 MEDIA_WARN_LOG("Failed to CallStartComposite, curWorkerNum over MAX_CONCURRENT_NUM");
175 Task newWaitTask{sourceVideoPath, videoPath, editData};
176 waitQueue_.push(std::move(newWaitTask));
177 mutex_.unlock();
178 }
179 }
180
EraseStickerField(std::string & editData,size_t index,bool isTimingSticker)181 void VideoCompositionCallbackImpl::EraseStickerField(std::string& editData, size_t index, bool isTimingSticker)
182 {
183 auto begin = index - START_DISTANCE;
184 auto end = index;
185 while (editData[end] != '}') {
186 ++end;
187 }
188 if (!isTimingSticker) {
189 ++end;
190 }
191 auto len = end - begin + 1;
192 editData.erase(begin, len);
193 }
194
195 } // end of namespace
196 }