• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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