• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <nlohmann/json.hpp>
19 
20 #include "media_log.h"
21 #include "media_file_utils.h"
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include "medialibrary_errno.h"
26 #include "photo_file_utils.h"
27 #include "dfx_utils.h"
28 #include "directory_ex.h"
29 #include "medialibrary_object_utils.h"
30 
31 using std::string;
32 
33 namespace OHOS {
34 namespace Media {
35 static const mode_t CHOWN_RW_USR_GRP = 0600;
36 static const int32_t LOG_FREQUENCY = 10;
37 std::unordered_map<uint32_t, std::shared_ptr<VideoEditor>> VideoCompositionCallbackImpl::editorMap_;
38 std::queue<VideoCompositionCallbackImpl::Task> VideoCompositionCallbackImpl::waitQueue_;
39 int32_t VideoCompositionCallbackImpl::curWorkerNum_ = 0;
40 std::mutex VideoCompositionCallbackImpl::mutex_;
41 // LCOV_EXCL_START
CheckDirPathReal(const std::string & filePath)42 static int32_t CheckDirPathReal(const std::string &filePath)
43 {
44     string dirPath;
45     auto index = filePath.rfind('/');
46     CHECK_AND_RETURN_RET_LOG(index != std::string::npos, E_HAS_FS_ERROR,
47         "find split for last string failed, %{private}s, errno: %{public}d", filePath.c_str(), errno);
48     dirPath = filePath.substr(0, index);
49     string absDirPath;
50     CHECK_AND_RETURN_RET_LOG(PathToRealPath(dirPath, absDirPath),
51         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", dirPath.c_str(), errno);
52     return E_OK;
53 }
54 
VideoCompositionCallbackImpl()55 VideoCompositionCallbackImpl::VideoCompositionCallbackImpl() {}
56 
CleanTempFilters(const std::string & tempFilters)57 static void CleanTempFilters(const std::string &tempFilters)
58 {
59     if (!MediaFileUtils::IsFileExists(tempFilters)) {
60         return;
61     }
62     if (!MediaFileUtils::DeleteFile(tempFilters)) {
63         MEDIA_ERR_LOG("Clean TempFilters errno: %{public}d", errno);
64     }
65 }
66 
SaveTempFiltersToVideo(const std::string & tempFilters,const std::string & videoPath)67 static int32_t SaveTempFiltersToVideo(const std::string &tempFilters, const std::string &videoPath)
68 {
69     if (!MediaFileUtils::IsFileExists(tempFilters)) {
70         MEDIA_ERR_LOG("SaveTempFiltersToVideo tempFilters not exists");
71         return E_HAS_FS_ERROR;
72     }
73     return rename(tempFilters.c_str(), videoPath.c_str());
74 }
75 
HandleAddFiltersError(const std::string & sourceVideoPath,const std::string & videoPath)76 static bool HandleAddFiltersError(const std::string &sourceVideoPath, const std::string &videoPath)
77 {
78     if (MediaFileUtils::IsFileExists(videoPath)) {
79         MEDIA_ERR_LOG("HandleAddFiltersError videoPath already exists");
80         return true;
81     }
82     if (!MediaFileUtils::IsFileExists(sourceVideoPath)) {
83         MEDIA_ERR_LOG("HandleAddFiltersError sourceVideoPath not exists");
84         return false;
85     }
86     return MediaFileUtils::CopyFileUtil(sourceVideoPath, videoPath);
87 }
88 
onResult(VEFResult result,VEFError errorCode)89 void VideoCompositionCallbackImpl::onResult(VEFResult result, VEFError errorCode)
90 {
91     close(inputFileFd_);
92     close(outputFileFd_);
93     editorMap_.erase(inputFileFd_);
94     if (errorCode != VEFError::ERR_OK) {
95         mutex_.lock();
96         --curWorkerNum_;
97         mutex_.unlock();
98         MEDIA_ERR_LOG("VideoCompositionCallbackImpl onResult error:%{public}d", (int32_t)errorCode);
99         // Video Composite failed save sourceVideo to photo directory
100         CHECK_AND_PRINT_LOG(HandleAddFiltersError(sourceVideoPath_, videoPath_),
101             "Copy sourceVideoPath to videoPath, path:%{private}s", sourceVideoPath_.c_str());
102         CleanTempFilters(tempFilters_);
103         return;
104     }
105     int32_t ret = SaveTempFiltersToVideo(tempFilters_, videoPath_);
106     if (ret != E_OK) {
107         CleanTempFilters(tempFilters_);
108     }
109     if (isNeedScan_ && MediaFileUtils::IsFileExists(assetPath_)) {
110         MediaLibraryObjectUtils::ScanMovingPhotoVideoAsync(assetPath_, true);
111     }
112     mutex_.lock();
113     if (waitQueue_.empty()) {
114         --curWorkerNum_;
115         mutex_.unlock();
116     } else {
117         Task task = std::move(waitQueue_.front());
118         waitQueue_.pop();
119         mutex_.unlock();
120         if (CallStartComposite(task.sourceVideoPath_, task.videoPath_, task.editData_,
121                                task.assetPath_, task.isNeedScan_) != E_OK) {
122             mutex_.lock();
123             --curWorkerNum_;
124             mutex_.unlock();
125             MEDIA_ERR_LOG("Failed to CallStartComposite, path:%{private}s", task.videoPath_.c_str());
126             CHECK_AND_RETURN_LOG(HandleAddFiltersError(task.sourceVideoPath_, task.videoPath_),
127                 "Copy sourceVideoPath to videoPath, path:%{private}s", task.sourceVideoPath_.c_str());
128         }
129     }
130 }
131 
onProgress(uint32_t progress)132 void VideoCompositionCallbackImpl::onProgress(uint32_t progress)
133 {
134     if (!(progress % LOG_FREQUENCY)) {
135         MEDIA_INFO_LOG("VideoCompositionCallbackImpl onProcess:%{public}d, tempPath:%{public}s",
136             (int32_t)progress, videoPath_.c_str());
137     }
138 }
139 
GetTempFiltersPath(const std::string videoPath)140 static std::string GetTempFiltersPath(const std::string videoPath)
141 {
142     return videoPath.substr(0, videoPath.rfind('.')) + "_temp_filters_"
143         + std::to_string(MediaFileUtils::UTCTimeMilliSeconds()) + videoPath.substr(videoPath.rfind('.'));
144 }
145 
CallStartComposite(const std::string & sourceVideoPath,const std::string & videoPath,const std::string & effectDescription,const std::string & assetPath,bool isNeedScan)146 int32_t VideoCompositionCallbackImpl::CallStartComposite(const std::string& sourceVideoPath,
147     const std::string& videoPath, const std::string& effectDescription, const std::string& assetPath, bool isNeedScan)
148 {
149     MEDIA_INFO_LOG("StartComposite, sourceVideoPath: %{public}s", DfxUtils::GetSafePath(sourceVideoPath).c_str());
150     string absSourceVideoPath;
151     CHECK_AND_RETURN_RET_LOG(PathToRealPath(sourceVideoPath, absSourceVideoPath), E_HAS_FS_ERROR,
152         "file is not real path, file path: %{private}s, errno: %{public}d", sourceVideoPath.c_str(), errno);
153     int32_t inputFileFd = open(absSourceVideoPath.c_str(), O_RDONLY);
154     CHECK_AND_RETURN_RET_LOG(inputFileFd != -1, E_ERR, "Open failed for inputFileFd file, errno: %{public}d", errno);
155     if (CheckDirPathReal(videoPath) != E_OK) {
156         MEDIA_ERR_LOG("dirFile is not real path, file path: %{private}s, errno: %{public}d", videoPath.c_str(), errno);
157         close(inputFileFd);
158         return E_HAS_FS_ERROR;
159     }
160     auto callBack = std::make_shared<VideoCompositionCallbackImpl>();
161     auto editor = VideoEditorFactory::CreateVideoEditor();
162     if (editor == nullptr) {
163         close(inputFileFd);
164         MEDIA_ERR_LOG("CreateEditor failed with error");
165         return E_ERR;
166     }
167     VEFError error = editor->AppendVideoFile(inputFileFd, effectDescription);
168     if (error != VEFError::ERR_OK) {
169         close(inputFileFd);
170         editor = nullptr;
171         MEDIA_ERR_LOG("AppendVideoFile failed with error: %{public}d", (int32_t)error);
172         return E_ERR;
173     }
174     callBack->tempFilters_ = GetTempFiltersPath(videoPath);
175     int32_t outputFileFd = open(callBack->tempFilters_.c_str(), O_WRONLY|O_CREAT, CHOWN_RW_USR_GRP);
176     if (outputFileFd == -1) {
177         close(inputFileFd);
178         MEDIA_ERR_LOG("Open failed for outputFileFd file, errno: %{public}d", errno);
179         return E_ERR;
180     }
181 
182     InitCallbackImpl(callBack, inputFileFd, outputFileFd, videoPath, absSourceVideoPath, assetPath, isNeedScan);
183 
184     auto compositionOptions = std::make_shared<CompositionOptions>(outputFileFd, callBack);
185     error = editor->StartComposite(compositionOptions);
186     if (error != VEFError::ERR_OK) {
187         close(inputFileFd);
188         close(outputFileFd);
189         CleanTempFilters(callBack->tempFilters_);
190         editor = nullptr;
191         MEDIA_ERR_LOG("StartComposite failed with error: %{public}d", (int32_t)error);
192         return E_ERR;
193     }
194     callBack->editorMap_[inputFileFd] = editor;
195     return E_OK;
196 }
197 
AddCompositionTask(const std::string & assetPath,std::string & editData,bool isNeedScan)198 void VideoCompositionCallbackImpl::AddCompositionTask(const std::string& assetPath,
199     std::string& editData, bool isNeedScan)
200 {
201     string sourceImagePath = PhotoFileUtils::GetEditDataSourcePath(assetPath);
202     string videoPath = MediaFileUtils::GetMovingPhotoVideoPath(assetPath);
203     string sourceVideoPath = MediaFileUtils::GetMovingPhotoVideoPath(sourceImagePath);
204 
205     mutex_.lock();
206     if (curWorkerNum_ < MAX_CONCURRENT_NUM) {
207         ++curWorkerNum_;
208         mutex_.unlock();
209         if (CallStartComposite(sourceVideoPath, videoPath, editData, assetPath, isNeedScan) != E_OK) {
210             mutex_.lock();
211             --curWorkerNum_;
212             mutex_.unlock();
213             MEDIA_ERR_LOG("Failed to CallStartComposite, path:%{private}s", videoPath.c_str());
214             CHECK_AND_RETURN_LOG(HandleAddFiltersError(sourceVideoPath, videoPath),
215                 "Copy sourceVideoPath to videoPath, path:%{private}s", sourceVideoPath.c_str());
216         }
217     } else {
218         MEDIA_WARN_LOG("Failed to CallStartComposite, curWorkerNum over MAX_CONCURRENT_NUM");
219         Task newWaitTask{sourceVideoPath, videoPath, editData, assetPath, isNeedScan};
220         waitQueue_.push(std::move(newWaitTask));
221         mutex_.unlock();
222     }
223 }
224 
EraseStickerField(std::string & editData,size_t index,bool isTimingSticker)225 void VideoCompositionCallbackImpl::EraseStickerField(std::string& editData, size_t index, bool isTimingSticker)
226 {
227     auto begin = index - START_DISTANCE;
228     auto end = index;
229     while (editData[end] != '}') {
230         ++end;
231     }
232     if (!isTimingSticker) {
233         ++end;
234     }
235     auto len = end - begin + 1;
236     editData.erase(begin, len);
237 }
238 
EraseWatermarkTag(std::string & editData)239 void VideoCompositionCallbackImpl::EraseWatermarkTag(std::string& editData)
240 {
241     CHECK_AND_RETURN_LOG(nlohmann::json::accept(editData),
242         "Failed to verify the editData format, editData is: %{public}s", editData.c_str());
243     nlohmann::json data = nlohmann::json::parse(editData);
244     if (data.contains(IMAGE_EFFECT) && data[IMAGE_EFFECT].contains(FILTERS_FIELD)) {
245         nlohmann::json filters = data[IMAGE_EFFECT][FILTERS_FIELD];
246         nlohmann::json newFilters;
247         for (const auto& filter : filters) {
248             if (!filter.contains(FILTER_CATEGORY) || filter[FILTER_CATEGORY] != BORDER_WATERMARK) {
249                 newFilters.push_back(filter);
250             }
251         }
252         nlohmann::json newData = data;
253         newData[IMAGE_EFFECT][FILTERS_FIELD] = newFilters;
254         editData = newData.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
255     }
256 }
257 
InitCallbackImpl(std::shared_ptr<VideoCompositionCallbackImpl> & callBack,int32_t inputFileFd,int32_t outputFileFd,const std::string & videoPath,std::string & absSourceVideoPath,const std::string & assetPath,bool isNeedScan)258 void VideoCompositionCallbackImpl::InitCallbackImpl(std::shared_ptr<VideoCompositionCallbackImpl>& callBack,
259     int32_t inputFileFd, int32_t outputFileFd, const std::string& videoPath, std::string& absSourceVideoPath,
260     const std::string& assetPath, bool isNeedScan)
261 {
262     callBack->inputFileFd_ = inputFileFd;
263     callBack->outputFileFd_ = outputFileFd;
264     callBack->videoPath_ = videoPath;
265     callBack->sourceVideoPath_ = absSourceVideoPath;
266     callBack->assetPath_ = assetPath;
267     callBack->isNeedScan_ = isNeedScan;
268 }
269 // LCOV_EXCL_STOP
270 } // end of namespace
271 }