28 constexpr std::uintmax_t MAX_IMAGE_FILE_BYTES =
30 constexpr std::int64_t MAX_IMAGE_PIXELS = 67108864;
32 MAX_IMAGE_FILE_BYTES);
33 const cv::Mat source = cv::imread(filename, cv::IMREAD_UNCHANGED);
35 throw std::runtime_error(
"unable to load image: " + filename);
37 if (source.cols <= 0 || source.rows <= 0 ||
38 static_cast<std::int64_t
>(source.cols) * source.rows >
40 throw std::runtime_error(
41 "graphic input dimensions exceed the supported limit");
45 switch (source.channels()) {
47 cv::cvtColor(source, rgba, cv::COLOR_GRAY2RGBA);
50 cv::cvtColor(source, rgba, cv::COLOR_BGR2RGBA);
53 cv::cvtColor(source, rgba, cv::COLOR_BGRA2RGBA);
56 throw std::runtime_error(
"unsupported image channel count: " +
57 std::to_string(source.channels()));
63 if (filename.empty() || filename.find(
"://") != std::string::npos) {
67 AVFormatContext *format =
nullptr;
68 if (avformat_open_input(&format, filename.c_str(),
nullptr,
nullptr) < 0) {
71 const auto close_format = [&format] {
72 if (format !=
nullptr) {
73 avformat_close_input(&format);
76 if (avformat_find_stream_info(format,
nullptr) < 0) {
81 double duration = 0.0;
82 if (format->duration != AV_NOPTS_VALUE && format->duration > 0) {
83 duration =
static_cast<double>(format->duration) /
84 static_cast<double>(AV_TIME_BASE);
86 const int stream_index = av_find_best_stream(
87 format, AVMEDIA_TYPE_VIDEO, -1, -1,
nullptr, 0);
88 if (stream_index >= 0) {
89 const AVStream *stream = format->streams[stream_index];
90 if (stream->duration != AV_NOPTS_VALUE &&
91 stream->duration > 0 && stream->time_base.num > 0 &&
92 stream->time_base.den > 0) {
93 duration =
static_cast<double>(stream->duration) *
94 static_cast<double>(stream->time_base.num) /
95 static_cast<double>(stream->time_base.den);
100 return std::isfinite(duration) && duration > 0.0 ? duration : 0.0;
104 const std::string &filename) {
106 if (filename.empty() || filename.find(
"://") != std::string::npos) {
110 AVFormatContext *format =
nullptr;
111 if (avformat_open_input(&format, filename.c_str(),
nullptr,
nullptr) < 0) {
114 const auto close_format = [&format] {
115 if (format !=
nullptr) {
116 avformat_close_input(&format);
119 if (avformat_find_stream_info(format,
nullptr) < 0) {
124 const int stream_index = av_find_best_stream(
125 format, AVMEDIA_TYPE_VIDEO, -1, -1,
nullptr, 0);
126 if (stream_index < 0) {
131 const AVStream *stream = format->streams[stream_index];
132 const AVCodecParameters *parameters = stream->codecpar;
138 info.
bit_depth = parameters->bits_per_raw_sample;
139 if (info.
bit_depth <= 0 && parameters->format != AV_PIX_FMT_NONE) {
140 const auto pixel_format =
141 static_cast<AVPixelFormat
>(parameters->format);
142 const AVPixFmtDescriptor *descriptor =
143 av_pix_fmt_desc_get(pixel_format);
144 if (descriptor !=
nullptr && descriptor->nb_components > 0) {
145 info.
bit_depth = descriptor->comp[0].depth;
149 const bool bt2020_primaries =
150 parameters->color_primaries == AVCOL_PRI_BT2020;
151 const bool hdr_transfer =
152 parameters->color_trc == AVCOL_TRC_SMPTE2084 ||
153 parameters->color_trc == AVCOL_TRC_ARIB_STD_B67 ||
154 parameters->color_trc == AVCOL_TRC_BT2020_10 ||
155 parameters->color_trc == AVCOL_TRC_BT2020_12;
156 const bool bt2020_space =
157 parameters->color_space == AVCOL_SPC_BT2020_NCL ||
158 parameters->color_space == AVCOL_SPC_BT2020_CL;
160 (bt2020_primaries || hdr_transfer || bt2020_space);
162 const auto copy_side_data = [&info](
const AVPacketSideData *side_data,
163 int side_data_count) {
164 for (
int index = 0; index < side_data_count; ++index) {
165 const AVPacketSideData &entry = side_data[index];
166 if (entry.type == AV_PKT_DATA_MASTERING_DISPLAY_METADATA &&
167 entry.size ==
sizeof(AVMasteringDisplayMetadata)) {
169 entry.data + entry.size);
170 }
else if (entry.type ==
171 AV_PKT_DATA_CONTENT_LIGHT_LEVEL &&
172 entry.size ==
sizeof(AVContentLightMetadata)) {
174 entry.data + entry.size);
178#if LIBAVCODEC_VERSION_MAJOR >= 60
179 copy_side_data(parameters->coded_side_data,
180 parameters->nb_coded_side_data);
182 copy_side_data(stream->side_data, stream->nb_side_data);
186 const AVCodec *decoder =
187 avcodec_find_decoder(parameters->codec_id);
188 AVCodecContext *decoder_context =
189 decoder !=
nullptr ? avcodec_alloc_context3(decoder)
191 AVPacket *packet = av_packet_alloc();
192 AVFrame *frame = av_frame_alloc();
193 const auto copy_frame_side_data = [&info](
const AVFrame *source) {
195 const AVFrameSideData *side_data = av_frame_get_side_data(
196 source, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
197 if (side_data !=
nullptr &&
199 sizeof(AVMasteringDisplayMetadata)) {
202 side_data->data + side_data->size);
206 const AVFrameSideData *side_data = av_frame_get_side_data(
207 source, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
208 if (side_data !=
nullptr &&
209 side_data->size ==
sizeof(AVContentLightMetadata)) {
212 side_data->data + side_data->size);
216 if (decoder_context !=
nullptr && packet !=
nullptr &&
218 avcodec_parameters_to_context(decoder_context, parameters) >=
220 avcodec_open2(decoder_context, decoder,
nullptr) >= 0) {
221 constexpr int MAX_METADATA_PACKETS = 256;
222 bool decoded_frame =
false;
223 for (
int packet_count = 0;
224 packet_count < MAX_METADATA_PACKETS && !decoded_frame &&
225 av_read_frame(format, packet) >= 0;
227 if (packet->stream_index == stream_index &&
228 avcodec_send_packet(decoder_context, packet) >= 0) {
229 while (avcodec_receive_frame(decoder_context, frame) >=
231 copy_frame_side_data(frame);
232 decoded_frame =
true;
233 av_frame_unref(frame);
236 av_packet_unref(packet);
238 if (!decoded_frame) {
239 avcodec_send_packet(decoder_context,
nullptr);
240 while (avcodec_receive_frame(decoder_context, frame) >=
242 copy_frame_side_data(frame);
243 decoded_frame =
true;
244 av_frame_unref(frame);
248 av_frame_free(&frame);
249 av_packet_free(&packet);
250 avcodec_free_context(&decoder_context);