1 /*
2  * Copyright (c) 2022-2022 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 #if defined(RECORDER_SUPPORT) && defined(VIDEO_SUPPORT)
17 
18 #define HST_LOG_TAG "Fmpeg_Vid_Enc_Config"
19 #include "ffmpeg_vid_enc_config.h"
20 
21 #include <functional>
22 #include "foundation/log.h"
23 #include "plugin/common/plugin_video_tags.h"
24 #include "plugins/ffmpeg_adapter/utils/ffmpeg_utils.h"
25 
26 namespace {
27 #define ASSIGN_IF_NOT_NULL(exec, target) \
28 do { \
29     auto tmp = (exec); \
30     if (tmp != nullptr) { \
31         (target) = *tmp; \
32     } \
33 } while (0) \
34 
35 using namespace OHOS::Media::Plugin;
36 using namespace Ffmpeg;
37 
38 const size_t DEFAULT_BITRATE = 12004000;
39 const size_t DEFAULT_FRAMERATE = 60;
40 const size_t DEFAULT_GOP_SIZE = 10;
41 const size_t DEFAULT_BIT_PER_CODED_SAMPLE = 24;
42 
43 template <typename T>
FindTagInMap(Tag tag,const std::map<Tag,ValueType> & tagStore)44 const T* FindTagInMap(Tag tag, const std::map<Tag, ValueType>& tagStore)
45 {
46     auto ite = tagStore.find(tag);
47     if (ite != tagStore.end() && Any::IsSameTypeWith<T>(ite->second)) {
48         return AnyCast<T>(&ite->second);
49     } else {
50         MEDIA_LOG_D("parameter " PUBLIC_LOG_D32 " is not found or type mismatch", static_cast<int32_t>(tag));
51         return nullptr;
52     }
53 }
54 
SetVideoResolution(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)55 Status SetVideoResolution(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
56 {
57     uint32_t width = 0;
58     uint32_t height = 0;
59     ASSIGN_IF_NOT_NULL(FindTagInMap<uint32_t>(Tag::VIDEO_WIDTH, tagStore), width);
60     ASSIGN_IF_NOT_NULL(FindTagInMap<uint32_t>(Tag::VIDEO_HEIGHT, tagStore), height);
61     if (width == 0 || height == 0) {
62         MEDIA_LOG_E("width or height is invalid");
63         return Status::ERROR_INVALID_PARAMETER;
64     }
65     codecContext.width = static_cast<int>(width);
66     codecContext.height = static_cast<int>(height);
67     return Status::OK;
68 }
69 
SetVideoFrameRateAndTimeBase(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)70 void SetVideoFrameRateAndTimeBase(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
71 {
72     uint32_t frameRate = 0;
73     ASSIGN_IF_NOT_NULL(FindTagInMap<uint32_t>(Tag::VIDEO_FRAME_RATE, tagStore), frameRate);
74     if (frameRate > 0) {
75         codecContext.framerate.num = static_cast<int32_t>(frameRate);
76         codecContext.framerate.den = 1;
77         codecContext.time_base.num = 1;
78         codecContext.time_base.den = static_cast<int32_t>(frameRate);
79     } else {
80         codecContext.framerate.num = DEFAULT_FRAMERATE;
81         codecContext.framerate.den = 1;
82         codecContext.time_base.num = 1;
83         codecContext.time_base.den = DEFAULT_FRAMERATE;
84     }
85 }
86 
SetVideoPixelFormat(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)87 Status SetVideoPixelFormat(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
88 {
89     VideoPixelFormat pixelFormat = VideoPixelFormat::UNKNOWN;
90     ASSIGN_IF_NOT_NULL(FindTagInMap<VideoPixelFormat>(Tag::VIDEO_PIXEL_FORMAT, tagStore), pixelFormat);
91     FALSE_RETURN_V_MSG_E(pixelFormat != VideoPixelFormat::UNKNOWN,
92         Status::ERROR_INVALID_PARAMETER,  "pixel format is invalid");
93     codecContext.pix_fmt = ConvertPixelFormatToFFmpeg(pixelFormat);
94     int32_t bpp = 0;
95     const AVPixFmtDescriptor* desc = av_pix_fmt_desc_get(codecContext.pix_fmt);
96     if (desc) {
97         for (auto i = 0; i < desc->nb_components; i++) {
98             bpp += desc->comp[i].depth;
99         }
100     }
101     codecContext.bits_per_coded_sample = (bpp > 0) ? bpp : DEFAULT_BIT_PER_CODED_SAMPLE;
102     codecContext.ticks_per_frame = 1;
103     if (codecContext.codec_id == AV_CODEC_ID_H264) {
104         codecContext.ticks_per_frame = 2; // Default 1, e.g., H.264/MPEG-2 set it to 2.
105     }
106     // pixel aspect ratio
107     codecContext.sample_aspect_ratio.num = 1;
108     codecContext.sample_aspect_ratio.den = 1;
109     return Status::OK;
110 }
111 
SetDefaultColorimetry(AVCodecContext & codecContext)112 void SetDefaultColorimetry(AVCodecContext& codecContext)
113 {
114     if (IsYuvFormat(codecContext.pix_fmt)) {
115         if (codecContext.height >= 2160) { // 2160: UHD
116             codecContext.color_primaries = AVCOL_PRI_BT2020;
117             codecContext.color_trc = AVCOL_TRC_BT2020_12;
118             codecContext.colorspace = AVCOL_SPC_BT2020_NCL;
119         } else if (codecContext.height > 576) { // 576: HD
120             codecContext.color_primaries = AVCOL_PRI_BT709;
121             codecContext.color_trc = AVCOL_TRC_BT709;
122             codecContext.colorspace = AVCOL_SPC_BT709;
123         } else { // SD
124             codecContext.color_primaries = AVCOL_PRI_SMPTE170M;
125             codecContext.color_trc = AVCOL_TRC_SMPTE170M;
126             codecContext.colorspace = AVCOL_SPC_SMPTE170M;
127         }
128         codecContext.color_range = AVCOL_RANGE_MPEG;
129     } else if (IsRgbFormat(codecContext.pix_fmt)) {
130         codecContext.color_primaries = AVCOL_PRI_BT709;
131         codecContext.color_trc = AVCOL_TRC_IEC61966_2_1;
132         codecContext.colorspace = AVCOL_SPC_RGB;
133         codecContext.color_range = AVCOL_RANGE_JPEG;
134     } else {
135         codecContext.color_primaries = AVCOL_PRI_UNSPECIFIED;
136         codecContext.color_trc = AVCOL_TRC_UNSPECIFIED;
137         codecContext.colorspace = AVCOL_SPC_UNSPECIFIED;
138         codecContext.color_range = AVCOL_RANGE_MPEG;
139     }
140     // location of chroma samples
141     codecContext.chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED;
142 }
143 
SetDefaultEncodeParams(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)144 void SetDefaultEncodeParams(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
145 {
146     int64_t bitRate = 0;
147     ASSIGN_IF_NOT_NULL(FindTagInMap<int64_t>(Tag::MEDIA_BITRATE, tagStore), bitRate);
148     codecContext.bit_rate = (bitRate > 0) ? bitRate : DEFAULT_BITRATE;
149     codecContext.gop_size = DEFAULT_GOP_SIZE;
150     codecContext.max_b_frames = 1;
151     SetDefaultColorimetry(codecContext);
152 }
153 
ConfigVideoCommonAttr(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)154 Status ConfigVideoCommonAttr(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
155 {
156     auto ret = SetVideoResolution(codecContext, tagStore);
157     FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "SetVideoResolution() fail");
158     ret = SetVideoPixelFormat(codecContext, tagStore);
159     FALSE_RETURN_V_MSG_E(ret == Status::OK, ret, "SetVideoPixelFormat() fail");
160     SetVideoFrameRateAndTimeBase(codecContext, tagStore);
161     SetDefaultEncodeParams(codecContext, tagStore);
162     return ret;
163 }
164 
ConfigH264Codec(AVCodecContext & codecContext,const std::map<Tag,ValueType> & tagStore)165 void ConfigH264Codec(AVCodecContext& codecContext, const std::map<Tag, ValueType>& tagStore)
166 {
167     ASSIGN_IF_NOT_NULL(FindTagInMap<uint32_t>(Tag::VIDEO_H264_LEVEL, tagStore), codecContext.level);
168     if (codecContext.level == FF_LEVEL_UNKNOWN) {
169         codecContext.level = 41; // 41
170     }
171     auto profilePtr = FindTagInMap<VideoH264Profile>(Tag::VIDEO_H264_PROFILE, tagStore);
172     if (profilePtr != nullptr) {
173         auto profile = ConvH264ProfileToFfmpeg(*profilePtr);
174         if (profile != FF_PROFILE_UNKNOWN) {
175             codecContext.profile = profile;
176         }
177     }
178     if (codecContext.profile == FF_PROFILE_UNKNOWN) {
179         codecContext.profile = FF_PROFILE_H264_BASELINE;
180     }
181     MEDIA_LOG_D("profile: " PUBLIC_LOG_D32, codecContext.profile);
182     av_opt_set(codecContext.priv_data, "preset", "slow", 0);
183     av_opt_set(codecContext.priv_data, "tune", "zerolatency", 0);
184     codecContext.flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
185 }
186 using ConfigFunc = std::function<void(AVCodecContext&, const std::map<Tag, ValueType>&)>;
187 std::map<AVCodecID, ConfigFunc> g_videoConfigFuncMap = {
188     {AV_CODEC_ID_H264, ConfigH264Codec},
189 };
190 
GetVideoCommonAttr(const AVCodecContext & codecContext,Tag tag,ValueType & outVal)191 void GetVideoCommonAttr(const AVCodecContext& codecContext, Tag tag, ValueType& outVal)
192 {
193     switch (tag) {
194         case Tag::MEDIA_CODEC_CONFIG: {
195             std::vector<uint8_t> extra(codecContext.extradata_size);
196             extra.assign(codecContext.extradata, codecContext.extradata + codecContext.extradata_size);
197             outVal = extra;
198             break;
199         }
200         default:
201             break;
202     }
203 }
204 
GetH264Attr(const AVCodecContext & codecContext,Tag tag,ValueType & outVal)205 void GetH264Attr(const AVCodecContext& codecContext, Tag tag, ValueType& outVal)
206 {
207     switch (tag) {
208         case Tag::VIDEO_H264_PROFILE:
209             outVal = ConvH264ProfileFromFfmpeg(codecContext.profile);
210             break;
211         case Tag::VIDEO_H264_LEVEL:
212             outVal = static_cast<uint32_t>(codecContext.level);
213             break;
214         default:
215             break;
216     }
217 }
218 
219 using GetAttrFunc = std::function<void(const AVCodecContext&, Tag, ValueType&)>;
220 std::map<AVCodecID, GetAttrFunc> g_getVideoAttrFuncMap = {
221     {AV_CODEC_ID_H264, GetH264Attr},
222 };
223 }
224 
225 namespace OHOS {
226 namespace Media {
227 namespace Plugin {
228 namespace Ffmpeg {
ConfigVideoEncoder(AVCodecContext & codecContext,const std::map<Tag,ValueType> & meta)229 void ConfigVideoEncoder(AVCodecContext& codecContext, const std::map<Tag, ValueType>& meta)
230 {
231     ConfigVideoCommonAttr(codecContext, meta);
232     if (g_videoConfigFuncMap.count(codecContext.codec_id) != 0) {
233         g_videoConfigFuncMap.at(codecContext.codec_id)(codecContext, meta);
234     }
235 }
236 
GetVideoEncoderParameters(const AVCodecContext & codecContext,Tag tag,Plugin::ValueType & outVal)237 Status GetVideoEncoderParameters(const AVCodecContext& codecContext, Tag tag, Plugin::ValueType& outVal)
238 {
239     outVal.Reset();
240     GetVideoCommonAttr(codecContext, tag, outVal);
241     if (!outVal.HasValue() && g_getVideoAttrFuncMap.count(codecContext.codec_id) != 0) {
242         g_getVideoAttrFuncMap.at(codecContext.codec_id)(codecContext, tag, outVal);
243     }
244     if (outVal.HasValue()) {
245         return Status::OK;
246     } else {
247         return Status::ERROR_INVALID_PARAMETER;
248     }
249 }
250 } // namespace Ffmpeg
251 } // namespace Plugin
252 } // namespace Media
253 } // namespace OHOS
254 
255 #endif