1fa7767c5Sopenharmony_ci/*
2fa7767c5Sopenharmony_ci * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
3fa7767c5Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4fa7767c5Sopenharmony_ci * you may not use this file except in compliance with the License.
5fa7767c5Sopenharmony_ci * You may obtain a copy of the License at
6fa7767c5Sopenharmony_ci *
7fa7767c5Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8fa7767c5Sopenharmony_ci *
9fa7767c5Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10fa7767c5Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11fa7767c5Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fa7767c5Sopenharmony_ci * See the License for the specific language governing permissions and
13fa7767c5Sopenharmony_ci * limitations under the License.
14fa7767c5Sopenharmony_ci */
15fa7767c5Sopenharmony_ci#include "foundation/log.h"
16fa7767c5Sopenharmony_ci#include "plugin/convert/ffmpeg_convert.h"
17fa7767c5Sopenharmony_ci#include "securec.h"
18fa7767c5Sopenharmony_ci
19fa7767c5Sopenharmony_cinamespace OHOS {
20fa7767c5Sopenharmony_cinamespace Media {
21fa7767c5Sopenharmony_cinamespace Plugin {
22fa7767c5Sopenharmony_cinamespace Ffmpeg {
23fa7767c5Sopenharmony_ciStatus Resample::Init(const ResamplePara& resamplePara)
24fa7767c5Sopenharmony_ci{
25fa7767c5Sopenharmony_ci    resamplePara_ = resamplePara;
26fa7767c5Sopenharmony_ci#if defined(_WIN32) || !defined(OHOS_LITE)
27fa7767c5Sopenharmony_ci    if (resamplePara_.bitsPerSample != 8 && resamplePara_.bitsPerSample != 24) { // 8 24
28fa7767c5Sopenharmony_ci        auto destFrameSize = av_samples_get_buffer_size(nullptr, resamplePara_.channels,
29fa7767c5Sopenharmony_ci                                                        resamplePara_.destSamplesPerFrame, resamplePara_.destFmt, 0);
30fa7767c5Sopenharmony_ci        resampleCache_.reserve(destFrameSize);
31fa7767c5Sopenharmony_ci        resampleChannelAddr_.reserve(resamplePara_.channels);
32fa7767c5Sopenharmony_ci        auto tmp = resampleChannelAddr_.data();
33fa7767c5Sopenharmony_ci        av_samples_fill_arrays(tmp, nullptr, resampleCache_.data(), resamplePara_.channels,
34fa7767c5Sopenharmony_ci                               resamplePara_.destSamplesPerFrame, resamplePara_.destFmt, 0);
35fa7767c5Sopenharmony_ci        auto swrContext = swr_alloc();
36fa7767c5Sopenharmony_ci        if (swrContext == nullptr) {
37fa7767c5Sopenharmony_ci            MEDIA_LOG_E("cannot allocate swr context");
38fa7767c5Sopenharmony_ci            return Status::ERROR_NO_MEMORY;
39fa7767c5Sopenharmony_ci        }
40fa7767c5Sopenharmony_ci        swrContext = swr_alloc_set_opts(swrContext, resamplePara_.channelLayout, resamplePara_.destFmt,
41fa7767c5Sopenharmony_ci                                        resamplePara_.sampleRate, resamplePara_.channelLayout,
42fa7767c5Sopenharmony_ci                                        resamplePara_.srcFfFmt, resamplePara_.sampleRate, 0, nullptr);
43fa7767c5Sopenharmony_ci        if (swr_init(swrContext) != 0) {
44fa7767c5Sopenharmony_ci            MEDIA_LOG_E("swr init error");
45fa7767c5Sopenharmony_ci            return Status::ERROR_UNKNOWN;
46fa7767c5Sopenharmony_ci        }
47fa7767c5Sopenharmony_ci        swrCtx_ = std::shared_ptr<SwrContext>(swrContext, [](SwrContext *ptr) {
48fa7767c5Sopenharmony_ci            if (ptr) {
49fa7767c5Sopenharmony_ci                swr_free(&ptr);
50fa7767c5Sopenharmony_ci            }
51fa7767c5Sopenharmony_ci        });
52fa7767c5Sopenharmony_ci    }
53fa7767c5Sopenharmony_ci#endif
54fa7767c5Sopenharmony_ci    return Status::OK;
55fa7767c5Sopenharmony_ci}
56fa7767c5Sopenharmony_ci
57fa7767c5Sopenharmony_ciStatus Resample::Convert(const uint8_t* srcBuffer, const size_t srcLength, uint8_t*& destBuffer, size_t& destLength)
58fa7767c5Sopenharmony_ci{
59fa7767c5Sopenharmony_ci#if defined(_WIN32) || !defined(OHOS_LITE)
60fa7767c5Sopenharmony_ci    if (resamplePara_.bitsPerSample == 8) { // 8
61fa7767c5Sopenharmony_ci        FALSE_RETURN_V_MSG(resamplePara_.destFmt == AV_SAMPLE_FMT_S16, Status::ERROR_UNIMPLEMENTED,
62fa7767c5Sopenharmony_ci                           "resample 8bit to other format can not support");
63fa7767c5Sopenharmony_ci        destLength = srcLength * 2;  // 2
64fa7767c5Sopenharmony_ci        resampleCache_.reserve(destLength);
65fa7767c5Sopenharmony_ci        resampleCache_.assign(destLength, 0);
66fa7767c5Sopenharmony_ci        for (size_t i {0}; i < destLength / 2; i++) { // 2
67fa7767c5Sopenharmony_ci            auto resCode = memcpy_s(&resampleCache_[0] + i * 2 + 1, sizeof(uint8_t), srcBuffer + i, 1); // 0 2 1
68fa7767c5Sopenharmony_ci            FALSE_RETURN_V_MSG_E(resCode == EOK, Status::ERROR_INVALID_OPERATION, "Memcpy failed at 8 bits/sample.");
69fa7767c5Sopenharmony_ci            *(&resampleCache_[0] + i * 2 + 1) += 0x80; // 2 0x80
70fa7767c5Sopenharmony_ci        }
71fa7767c5Sopenharmony_ci        destBuffer = resampleCache_.data();
72fa7767c5Sopenharmony_ci    } else if (resamplePara_.bitsPerSample == 24) {  // 24
73fa7767c5Sopenharmony_ci        FALSE_RETURN_V_MSG(resamplePara_.destFmt == AV_SAMPLE_FMT_S16, Status::ERROR_UNIMPLEMENTED,
74fa7767c5Sopenharmony_ci                           "resample 24bit to other format can not support");
75fa7767c5Sopenharmony_ci        destLength = srcLength / 3 * 2; // 3 2
76fa7767c5Sopenharmony_ci        resampleCache_.reserve(destLength);
77fa7767c5Sopenharmony_ci        resampleCache_.assign(destLength, 0);
78fa7767c5Sopenharmony_ci        for (size_t i = 0; i < destLength / 2; i++) { // 2
79fa7767c5Sopenharmony_ci            auto resCode
80fa7767c5Sopenharmony_ci                = memcpy_s(&resampleCache_[0] + i * 2, sizeof(uint8_t) * 2, srcBuffer + i * 3 + 1, 2); // 2 3 1
81fa7767c5Sopenharmony_ci            FALSE_RETURN_V_MSG_E(resCode == EOK, Status::ERROR_INVALID_OPERATION, "Memcpy failed at 24 bits/sample.");
82fa7767c5Sopenharmony_ci        }
83fa7767c5Sopenharmony_ci        destBuffer = resampleCache_.data();
84fa7767c5Sopenharmony_ci    } else {
85fa7767c5Sopenharmony_ci        size_t lineSize = srcLength / resamplePara_.channels;
86fa7767c5Sopenharmony_ci        std::vector<const uint8_t*> tmpInput(resamplePara_.channels);
87fa7767c5Sopenharmony_ci        tmpInput[0] = srcBuffer;
88fa7767c5Sopenharmony_ci        if (av_sample_fmt_is_planar(resamplePara_.srcFfFmt)) {
89fa7767c5Sopenharmony_ci            for (size_t i = 1; i < tmpInput.size(); ++i) {
90fa7767c5Sopenharmony_ci                tmpInput[i] = tmpInput[i-1] + lineSize;
91fa7767c5Sopenharmony_ci            }
92fa7767c5Sopenharmony_ci        }
93fa7767c5Sopenharmony_ci        auto samples = lineSize / static_cast<size_t>(av_get_bytes_per_sample(resamplePara_.srcFfFmt));
94fa7767c5Sopenharmony_ci        auto res = swr_convert(swrCtx_.get(), resampleChannelAddr_.data(), resamplePara_.destSamplesPerFrame,
95fa7767c5Sopenharmony_ci                               tmpInput.data(), samples);
96fa7767c5Sopenharmony_ci        if (res < 0) {
97fa7767c5Sopenharmony_ci            MEDIA_LOG_E("resample input failed");
98fa7767c5Sopenharmony_ci            destLength = 0;
99fa7767c5Sopenharmony_ci        } else {
100fa7767c5Sopenharmony_ci            destBuffer = resampleCache_.data();
101fa7767c5Sopenharmony_ci            size_t bytesPerSample = static_cast<size_t>(av_get_bytes_per_sample(resamplePara_.destFmt));
102fa7767c5Sopenharmony_ci            destLength = static_cast<size_t>(res) * bytesPerSample * resamplePara_.channels;
103fa7767c5Sopenharmony_ci        }
104fa7767c5Sopenharmony_ci    }
105fa7767c5Sopenharmony_ci#endif
106fa7767c5Sopenharmony_ci    return Status::OK;
107fa7767c5Sopenharmony_ci}
108fa7767c5Sopenharmony_ci
109fa7767c5Sopenharmony_ci#if defined(VIDEO_SUPPORT)
110fa7767c5Sopenharmony_ciStatus Scale::Init(const ScalePara& scalePara, uint8_t** dstData, int32_t* dstLineSize)
111fa7767c5Sopenharmony_ci{
112fa7767c5Sopenharmony_ci    scalePara_ = scalePara;
113fa7767c5Sopenharmony_ci    if (swsCtx_ != nullptr) {
114fa7767c5Sopenharmony_ci        return Status::OK;
115fa7767c5Sopenharmony_ci    }
116fa7767c5Sopenharmony_ci    auto swsContext = sws_getContext(scalePara_.srcWidth, scalePara_.srcHeight, scalePara_.srcFfFmt,
117fa7767c5Sopenharmony_ci                                     scalePara_.dstWidth, scalePara_.dstHeight, scalePara_.dstFfFmt,
118fa7767c5Sopenharmony_ci                                     SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
119fa7767c5Sopenharmony_ci    FALSE_RETURN_V_MSG_E(swsContext != nullptr, Status::ERROR_UNKNOWN, "sws_getContext fail");
120fa7767c5Sopenharmony_ci    swsCtx_ = std::shared_ptr<SwsContext>(swsContext, [](struct SwsContext *ptr) {
121fa7767c5Sopenharmony_ci        if (ptr != nullptr) {
122fa7767c5Sopenharmony_ci            sws_freeContext(ptr);
123fa7767c5Sopenharmony_ci        }
124fa7767c5Sopenharmony_ci    });
125fa7767c5Sopenharmony_ci    auto ret = av_image_alloc(dstData, dstLineSize, scalePara_.dstWidth, scalePara_.dstHeight,
126fa7767c5Sopenharmony_ci                              scalePara_.dstFfFmt, scalePara_.align);
127fa7767c5Sopenharmony_ci    FALSE_RETURN_V_MSG_E(ret >= 0, Status::ERROR_UNKNOWN, "could not allocate destination image" PUBLIC_LOG_D32, ret);
128fa7767c5Sopenharmony_ci    MEDIA_LOG_D("av_image_alloc call, ret: " PUBLIC_LOG_U32 "dstPixelFormat_: " PUBLIC_LOG_U32,
129fa7767c5Sopenharmony_ci            ret, scalePara_.dstFfFmt);
130fa7767c5Sopenharmony_ci    // av_image_alloc can make sure that dstLineSize last element is 0
131fa7767c5Sopenharmony_ci    for (int32_t i = 0; dstLineSize[i] > 0; i++) {
132fa7767c5Sopenharmony_ci        MEDIA_LOG_D("dstLineSize[" PUBLIC_LOG_D32 "]: " PUBLIC_LOG_D32, i, dstLineSize[i]);
133fa7767c5Sopenharmony_ci        if (dstData[i] && !dstLineSize[i]) {
134fa7767c5Sopenharmony_ci            MEDIA_LOG_E("scale frame is broken, i: " PUBLIC_LOG_D32, i);
135fa7767c5Sopenharmony_ci            return Status::ERROR_UNKNOWN;
136fa7767c5Sopenharmony_ci        }
137fa7767c5Sopenharmony_ci    }
138fa7767c5Sopenharmony_ci    return Status::OK;
139fa7767c5Sopenharmony_ci}
140fa7767c5Sopenharmony_ci
141fa7767c5Sopenharmony_ciStatus Scale::Convert(uint8_t** srcData, const int32_t* srcLineSize, uint8_t** dstData, int32_t* dstLineSize)
142fa7767c5Sopenharmony_ci{
143fa7767c5Sopenharmony_ci    auto res = sws_scale(swsCtx_.get(), srcData, srcLineSize, 0, scalePara_.srcHeight,
144fa7767c5Sopenharmony_ci                         dstData, dstLineSize);
145fa7767c5Sopenharmony_ci    FALSE_RETURN_V_MSG_E(res >= 0, Status::ERROR_UNKNOWN, "sws_scale fail: " PUBLIC_LOG_D32, res);
146fa7767c5Sopenharmony_ci    return Status::OK;
147fa7767c5Sopenharmony_ci}
148fa7767c5Sopenharmony_ci#endif
149fa7767c5Sopenharmony_ci} // namespace Ffmpeg
150fa7767c5Sopenharmony_ci} // namespace Plugin
151fa7767c5Sopenharmony_ci} // namespace Media
152fa7767c5Sopenharmony_ci} // namespace OHOS