28 constexpr std::uintmax_t MAX_IMAGE_FILE_BYTES = 512U * 1024U * 1024U;
29 constexpr std::int64_t MAX_IMAGE_PIXELS = 67108864;
31 const cv::Mat source = cv::imread(filename, cv::IMREAD_UNCHANGED);
33 throw std::runtime_error(
"unable to load image: " + filename);
35 if (source.cols <= 0 || source.rows <= 0 ||
static_cast<std::int64_t
>(source.cols) * source.rows > MAX_IMAGE_PIXELS) {
36 throw std::runtime_error(
"graphic input dimensions exceed the supported limit");
40 switch (source.channels()) {
42 cv::cvtColor(source, rgba, cv::COLOR_GRAY2RGBA);
45 cv::cvtColor(source, rgba, cv::COLOR_BGR2RGBA);
48 cv::cvtColor(source, rgba, cv::COLOR_BGRA2RGBA);
51 throw std::runtime_error(
"unsupported image channel count: " + std::to_string(source.channels()));
57 if (filename.empty() || filename.find(
"://") != std::string::npos) {
61 AVFormatContext *format =
nullptr;
62 if (avformat_open_input(&format, filename.c_str(),
nullptr,
nullptr) < 0) {
65 const auto close_format = [&format] {
66 if (format !=
nullptr) {
67 avformat_close_input(&format);
70 if (avformat_find_stream_info(format,
nullptr) < 0) {
75 double duration = 0.0;
76 if (format->duration != AV_NOPTS_VALUE && format->duration > 0) {
77 duration =
static_cast<double>(format->duration) /
static_cast<double>(AV_TIME_BASE);
79 const int stream_index = av_find_best_stream(format, AVMEDIA_TYPE_VIDEO, -1, -1,
nullptr, 0);
80 if (stream_index >= 0) {
81 const AVStream *stream = format->streams[stream_index];
82 if (stream->duration != AV_NOPTS_VALUE && stream->duration > 0 && stream->time_base.num > 0 && stream->time_base.den > 0) {
83 duration =
static_cast<double>(stream->duration) *
static_cast<double>(stream->time_base.num) /
static_cast<double>(stream->time_base.den);
88 return std::isfinite(duration) && duration > 0.0 ? duration : 0.0;
93 if (filename.empty() || filename.find(
"://") != std::string::npos) {
97 AVFormatContext *format =
nullptr;
98 if (avformat_open_input(&format, filename.c_str(),
nullptr,
nullptr) < 0) {
101 const auto close_format = [&format] {
102 if (format !=
nullptr) {
103 avformat_close_input(&format);
106 if (avformat_find_stream_info(format,
nullptr) < 0) {
111 const int stream_index = av_find_best_stream(format, AVMEDIA_TYPE_VIDEO, -1, -1,
nullptr, 0);
112 if (stream_index < 0) {
117 const AVStream *stream = format->streams[stream_index];
118 const AVCodecParameters *parameters = stream->codecpar;
124 info.
bit_depth = parameters->bits_per_raw_sample;
125 if (info.
bit_depth <= 0 && parameters->format != AV_PIX_FMT_NONE) {
126 const auto pixel_format =
static_cast<AVPixelFormat
>(parameters->format);
127 const AVPixFmtDescriptor *descriptor = av_pix_fmt_desc_get(pixel_format);
128 if (descriptor !=
nullptr && descriptor->nb_components > 0) {
129 info.
bit_depth = descriptor->comp[0].depth;
133 const bool bt2020_primaries = parameters->color_primaries == AVCOL_PRI_BT2020;
134 const bool hdr_transfer = parameters->color_trc == AVCOL_TRC_SMPTE2084 || parameters->color_trc == AVCOL_TRC_ARIB_STD_B67 || parameters->color_trc == AVCOL_TRC_BT2020_10 || parameters->color_trc == AVCOL_TRC_BT2020_12;
135 const bool bt2020_space = parameters->color_space == AVCOL_SPC_BT2020_NCL || parameters->color_space == AVCOL_SPC_BT2020_CL;
136 info.
hdr = info.
bit_depth >= 10 && (bt2020_primaries || hdr_transfer || bt2020_space);
138 const auto copy_side_data = [&info](
const AVPacketSideData *side_data,
int side_data_count) {
139 for (
int index = 0; index < side_data_count; ++index) {
140 const AVPacketSideData &entry = side_data[index];
141 if (entry.type == AV_PKT_DATA_MASTERING_DISPLAY_METADATA && entry.size ==
sizeof(AVMasteringDisplayMetadata)) {
143 }
else if (entry.type == AV_PKT_DATA_CONTENT_LIGHT_LEVEL && entry.size ==
sizeof(AVContentLightMetadata)) {
144 info.
content_light.assign(entry.data, entry.data + entry.size);
148#if LIBAVCODEC_VERSION_MAJOR >= 60
149 copy_side_data(parameters->coded_side_data, parameters->nb_coded_side_data);
151 copy_side_data(stream->side_data, stream->nb_side_data);
155 const AVCodec *decoder = avcodec_find_decoder(parameters->codec_id);
156 AVCodecContext *decoder_context = decoder !=
nullptr ? avcodec_alloc_context3(decoder) :
nullptr;
157 AVPacket *packet = av_packet_alloc();
158 AVFrame *frame = av_frame_alloc();
159 const auto copy_frame_side_data = [&info](
const AVFrame *source) {
161 const AVFrameSideData *side_data = av_frame_get_side_data(source, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
162 if (side_data !=
nullptr && side_data->size ==
sizeof(AVMasteringDisplayMetadata)) {
163 info.
mastering_display.assign(side_data->data, side_data->data + side_data->size);
167 const AVFrameSideData *side_data = av_frame_get_side_data(source, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
168 if (side_data !=
nullptr && side_data->size ==
sizeof(AVContentLightMetadata)) {
169 info.
content_light.assign(side_data->data, side_data->data + side_data->size);
173 if (decoder_context !=
nullptr && packet !=
nullptr && frame !=
nullptr && avcodec_parameters_to_context(decoder_context, parameters) >= 0 && avcodec_open2(decoder_context, decoder,
nullptr) >= 0) {
174 constexpr int MAX_METADATA_PACKETS = 256;
175 bool decoded_frame =
false;
176 for (
int packet_count = 0; packet_count < MAX_METADATA_PACKETS && !decoded_frame && av_read_frame(format, packet) >= 0; ++packet_count) {
177 if (packet->stream_index == stream_index && avcodec_send_packet(decoder_context, packet) >= 0) {
178 while (avcodec_receive_frame(decoder_context, frame) >= 0) {
179 copy_frame_side_data(frame);
180 decoded_frame =
true;
181 av_frame_unref(frame);
184 av_packet_unref(packet);
186 if (!decoded_frame) {
187 avcodec_send_packet(decoder_context,
nullptr);
188 while (avcodec_receive_frame(decoder_context, frame) >= 0) {
189 copy_frame_side_data(frame);
190 decoded_frame =
true;
191 av_frame_unref(frame);
195 av_frame_free(&frame);
196 av_packet_free(&packet);
197 avcodec_free_context(&decoder_context);