1 /*
2 * Copyright (c) 2023-2023 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 #include "ffmpeg_convert.h"
16 #include "common/log.h"
17 #include "securec.h"
18
19 namespace OHOS {
20 namespace Media {
21 namespace Plugins {
22 namespace Ffmpeg {
Init(const ResamplePara & resamplePara)23 Status Resample::Init(const ResamplePara &resamplePara)
24 {
25 resamplePara_ = resamplePara;
26 #if defined(_WIN32) || !defined(OHOS_LITE)
27 if (resamplePara_.bitsPerSample != 8 && resamplePara_.bitsPerSample != 24) { // 8 24
28 auto destFrameSize = av_samples_get_buffer_size(nullptr, resamplePara_.channels,
29 resamplePara_.destSamplesPerFrame, resamplePara_.destFmt, 0);
30 resampleCache_.reserve(destFrameSize);
31 resampleChannelAddr_.reserve(resamplePara_.channels);
32 auto tmp = resampleChannelAddr_.data();
33 av_samples_fill_arrays(tmp, nullptr, resampleCache_.data(), resamplePara_.channels,
34 resamplePara_.destSamplesPerFrame, resamplePara_.destFmt, 0);
35 auto swrContext = swr_alloc();
36 if (swrContext == nullptr) {
37 MEDIA_LOG_E("cannot allocate swr context");
38 return Status::ERROR_NO_MEMORY;
39 }
40 swrContext = swr_alloc_set_opts(swrContext, resamplePara_.channelLayout, resamplePara_.destFmt,
41 resamplePara_.sampleRate, resamplePara_.channelLayout, resamplePara_.srcFfFmt,
42 resamplePara_.sampleRate, 0, nullptr);
43 if (swr_init(swrContext) != 0) {
44 MEDIA_LOG_E("swr init error");
45 return Status::ERROR_UNKNOWN;
46 }
47 swrCtx_ = std::shared_ptr<SwrContext>(swrContext, [](SwrContext *ptr) {
48 if (ptr) {
49 swr_free(&ptr);
50 }
51 });
52 }
53 #endif
54 return Status::OK;
55 }
56
InitSwrContext(const ResamplePara & resamplePara)57 Status Resample::InitSwrContext(const ResamplePara &resamplePara)
58 {
59 resamplePara_ = resamplePara;
60 auto swrContext = swr_alloc();
61 if (swrContext == nullptr) {
62 MEDIA_LOG_E("cannot allocate swr context");
63 return Status::ERROR_NO_MEMORY;
64 }
65 swrContext =
66 swr_alloc_set_opts(swrContext, resamplePara_.channelLayout, resamplePara_.destFmt, resamplePara_.sampleRate,
67 resamplePara_.channelLayout, resamplePara_.srcFfFmt, resamplePara_.sampleRate, 0, nullptr);
68 if (swr_init(swrContext) != 0) {
69 MEDIA_LOG_E("swr init error");
70 return Status::ERROR_UNKNOWN;
71 }
72 swrCtx_ = std::shared_ptr<SwrContext>(swrContext, [](SwrContext *ptr) {
73 if (ptr) {
74 swr_free(&ptr);
75 }
76 });
77 return Status::OK;
78 }
79
Convert(const uint8_t * srcBuffer,const size_t srcLength,uint8_t * & destBuffer,size_t & destLength)80 Status Resample::Convert(const uint8_t *srcBuffer, const size_t srcLength, uint8_t *&destBuffer, size_t &destLength)
81 {
82 #if defined(_WIN32) || !defined(OHOS_LITE)
83 if (resamplePara_.bitsPerSample == 8) { // 8
84 FALSE_RETURN_V_MSG(resamplePara_.destFmt == AV_SAMPLE_FMT_S16, Status::ERROR_UNIMPLEMENTED,
85 "resample 8bit to other format can not support");
86 destLength = srcLength * 2; // 2
87 resampleCache_.reserve(destLength);
88 resampleCache_.assign(destLength, 0);
89 for (size_t i{0}; i < destLength / 2; i++) { // 2
90 auto resCode = memcpy_s(&resampleCache_[0] + i * 2 + 1, sizeof(uint8_t), srcBuffer + i, 1); // 0 2 1
91 FALSE_RETURN_V_MSG_E(resCode == EOK, Status::ERROR_INVALID_OPERATION, "Memcpy failed at 8 bits/sample.");
92 *(&resampleCache_[0] + i * 2 + 1) += 0x80; // 2 0x80
93 }
94 destBuffer = resampleCache_.data();
95 } else if (resamplePara_.bitsPerSample == 24) { // 24
96 FALSE_RETURN_V_MSG(resamplePara_.destFmt == AV_SAMPLE_FMT_S16, Status::ERROR_UNIMPLEMENTED,
97 "resample 24bit to other format can not support");
98 destLength = srcLength / 3 * 2; // 3 2
99 resampleCache_.reserve(destLength);
100 resampleCache_.assign(destLength, 0);
101 for (size_t i = 0; i < destLength / 2; i++) { // 2
102 auto resCode = memcpy_s(&resampleCache_[0] + i * 2, sizeof(uint8_t) * 2, srcBuffer + i * 3 + 1, 2); // 2 3 1
103 FALSE_RETURN_V_MSG_E(resCode == EOK, Status::ERROR_INVALID_OPERATION, "Memcpy failed at 24 bits/sample.");
104 }
105 destBuffer = resampleCache_.data();
106 } else {
107 size_t lineSize = srcLength / resamplePara_.channels;
108 std::vector<const uint8_t *> tmpInput(resamplePara_.channels);
109 tmpInput[0] = srcBuffer;
110 if (av_sample_fmt_is_planar(resamplePara_.srcFfFmt)) {
111 for (size_t i = 1; i < tmpInput.size(); ++i) {
112 tmpInput[i] = tmpInput[i - 1] + lineSize;
113 }
114 }
115 auto samples = lineSize / static_cast<size_t>(av_get_bytes_per_sample(resamplePara_.srcFfFmt));
116 auto res = swr_convert(swrCtx_.get(), resampleChannelAddr_.data(), resamplePara_.destSamplesPerFrame,
117 tmpInput.data(), samples);
118 if (res < 0) {
119 MEDIA_LOG_E("resample input failed");
120 destLength = 0;
121 } else {
122 destBuffer = resampleCache_.data();
123 size_t bytesPerSample = static_cast<size_t>(av_get_bytes_per_sample(resamplePara_.destFmt));
124 destLength = static_cast<size_t>(res) * bytesPerSample * resamplePara_.channels;
125 }
126 }
127 #endif
128 return Status::OK;
129 }
130
AVStrError(int errnum)131 static std::string AVStrError(int errnum)
132 {
133 char errbuf[AV_ERROR_MAX_STRING_SIZE] = {0};
134 av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
135 return std::string(errbuf);
136 }
137
ConvertFrame(AVFrame * outputFrame,const AVFrame * inputFrame)138 Status Resample::ConvertFrame(AVFrame *outputFrame, const AVFrame *inputFrame)
139 {
140 if (outputFrame == nullptr || inputFrame == nullptr) {
141 MEDIA_LOG_E("Frame null pointer");
142 return Status::ERROR_NO_MEMORY;
143 }
144
145 outputFrame->channel_layout = static_cast<uint64_t>(resamplePara_.channelLayout);
146 outputFrame->format = resamplePara_.destFmt;
147 outputFrame->sample_rate = static_cast<int>(resamplePara_.sampleRate);
148
149 auto ret = swr_convert_frame(swrCtx_.get(), outputFrame, inputFrame);
150 if (ret < 0) {
151 MEDIA_LOG_E("convert frame failed, %{public}s", AVStrError(ret).c_str());
152 return Status::ERROR_UNKNOWN;
153 }
154 return Status::OK;
155 }
156
157 #if defined(VIDEO_SUPPORT)
Init(const ScalePara & scalePara,uint8_t ** dstData,int32_t * dstLineSize)158 Status Scale::Init(const ScalePara &scalePara, uint8_t **dstData, int32_t *dstLineSize)
159 {
160 scalePara_ = scalePara;
161 if (swsCtx_ != nullptr) {
162 return Status::OK;
163 }
164 auto swsContext =
165 sws_getContext(scalePara_.srcWidth, scalePara_.srcHeight, scalePara_.srcFfFmt, scalePara_.dstWidth,
166 scalePara_.dstHeight, scalePara_.dstFfFmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
167 FALSE_RETURN_V_MSG_E(swsContext != nullptr, Status::ERROR_UNKNOWN, "sws_getContext fail");
168 swsCtx_ = std::shared_ptr<SwsContext>(swsContext, [](struct SwsContext *ptr) {
169 if (ptr != nullptr) {
170 sws_freeContext(ptr);
171 }
172 });
173 auto ret = av_image_alloc(dstData, dstLineSize, scalePara_.dstWidth, scalePara_.dstHeight, scalePara_.dstFfFmt,
174 scalePara_.align);
175 FALSE_RETURN_V_MSG_E(ret >= 0, Status::ERROR_UNKNOWN, "could not allocate destination image" PUBLIC_LOG_D32, ret);
176 MEDIA_LOG_D("av_image_alloc call, ret: " PUBLIC_LOG_U32 "dstPixelFormat_: " PUBLIC_LOG_U32, ret,
177 scalePara_.dstFfFmt);
178 // av_image_alloc can make sure that dstLineSize last element is 0
179 for (int32_t i = 0; dstLineSize[i] > 0; i++) {
180 MEDIA_LOG_D("dstLineSize[" PUBLIC_LOG_D32 "]: " PUBLIC_LOG_D32, i, dstLineSize[i]);
181 if (dstData[i] && !dstLineSize[i]) {
182 MEDIA_LOG_E("scale frame is broken, i: " PUBLIC_LOG_D32, i);
183 return Status::ERROR_UNKNOWN;
184 }
185 }
186 return Status::OK;
187 }
188
Convert(uint8_t ** srcData,const int32_t * srcLineSize,uint8_t ** dstData,int32_t * dstLineSize)189 Status Scale::Convert(uint8_t **srcData, const int32_t *srcLineSize, uint8_t **dstData, int32_t *dstLineSize)
190 {
191 auto res = sws_scale(swsCtx_.get(), srcData, srcLineSize, 0, scalePara_.srcHeight, dstData, dstLineSize);
192 FALSE_RETURN_V_MSG_E(res >= 0, Status::ERROR_UNKNOWN, "sws_scale fail: " PUBLIC_LOG_D32, res);
193 return Status::OK;
194 }
195 #endif
196 } // namespace Ffmpeg
197 } // namespace Plugins
198 } // namespace Media
199 } // namespace OHOS