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