#include "util.h" #include namespace ffmpeg { namespace Serializer { // fixed size types template inline size_t getSize(const T& x) { return sizeof(x); } template inline bool serializeItem( uint8_t* dest, size_t len, size_t& pos, const T& src) { VLOG(6) << "Generic serializeItem"; const auto required = sizeof(src); if (len < pos + required) { return false; } memcpy(dest + pos, &src, required); pos += required; return true; } template inline bool deserializeItem( const uint8_t* src, size_t len, size_t& pos, T& dest) { const auto required = sizeof(dest); if (len < pos + required) { return false; } memcpy(&dest, src + pos, required); pos += required; return true; } // AVSubtitleRect specialization inline size_t getSize(const AVSubtitleRect& x) { auto rectBytes = [](const AVSubtitleRect& y) -> size_t { size_t s = 0; switch (y.type) { case SUBTITLE_BITMAP: for (int i = 0; i < y.nb_colors; ++i) { s += sizeof(y.linesize[i]); s += y.linesize[i]; } break; case SUBTITLE_TEXT: s += sizeof(size_t); s += strlen(y.text); break; case SUBTITLE_ASS: s += sizeof(size_t); s += strlen(y.ass); break; default: break; } return s; }; return getSize(x.x) + getSize(x.y) + getSize(x.w) + getSize(x.h) + getSize(x.nb_colors) + getSize(x.type) + getSize(x.flags) + rectBytes(x); } // AVSubtitle specialization inline size_t getSize(const AVSubtitle& x) { auto rectBytes = [](const AVSubtitle& y) -> size_t { size_t s = getSize(y.num_rects); for (unsigned i = 0; i < y.num_rects; ++i) { s += getSize(*y.rects[i]); } return s; }; return getSize(x.format) + getSize(x.start_display_time) + getSize(x.end_display_time) + getSize(x.pts) + rectBytes(x); } inline bool serializeItem( uint8_t* dest, size_t len, size_t& pos, const AVSubtitleRect& src) { auto rectSerialize = [](uint8_t* d, size_t l, size_t& p, const AVSubtitleRect& x) -> size_t { switch (x.type) { case SUBTITLE_BITMAP: for (int i = 0; i < x.nb_colors; ++i) { if (!serializeItem(d, l, p, x.linesize[i])) { return false; } if (p + x.linesize[i] > l) { return false; } memcpy(d + p, x.data[i], x.linesize[i]); p += x.linesize[i]; } return true; case SUBTITLE_TEXT: { const size_t s = strlen(x.text); if (!serializeItem(d, l, p, s)) { return false; } if (p + s > l) { return false; } memcpy(d + p, x.text, s); p += s; return true; } case SUBTITLE_ASS: { const size_t s = strlen(x.ass); if (!serializeItem(d, l, p, s)) { return false; } if (p + s > l) { return false; } memcpy(d + p, x.ass, s); p += s; return true; } default: return true; } }; return serializeItem(dest, len, pos, src.x) && serializeItem(dest, len, pos, src.y) && serializeItem(dest, len, pos, src.w) && serializeItem(dest, len, pos, src.h) && serializeItem(dest, len, pos, src.nb_colors) && serializeItem(dest, len, pos, src.type) && serializeItem(dest, len, pos, src.flags) && rectSerialize(dest, len, pos, src); } inline bool serializeItem( uint8_t* dest, size_t len, size_t& pos, const AVSubtitle& src) { auto rectSerialize = [](uint8_t* d, size_t l, size_t& p, const AVSubtitle& x) -> bool { bool res = serializeItem(d, l, p, x.num_rects); for (unsigned i = 0; res && i < x.num_rects; ++i) { res = serializeItem(d, l, p, *(x.rects[i])); } return res; }; VLOG(6) << "AVSubtitle serializeItem"; return serializeItem(dest, len, pos, src.format) && serializeItem(dest, len, pos, src.start_display_time) && serializeItem(dest, len, pos, src.end_display_time) && serializeItem(dest, len, pos, src.pts) && rectSerialize(dest, len, pos, src); } inline bool deserializeItem( const uint8_t* src, size_t len, size_t& pos, AVSubtitleRect& dest) { auto rectDeserialize = [](const uint8_t* y, size_t l, size_t& p, AVSubtitleRect& x) -> bool { switch (x.type) { case SUBTITLE_BITMAP: for (int i = 0; i < x.nb_colors; ++i) { if (!deserializeItem(y, l, p, x.linesize[i])) { return false; } if (p + x.linesize[i] > l) { return false; } x.data[i] = (uint8_t*)av_malloc(x.linesize[i]); memcpy(x.data[i], y + p, x.linesize[i]); p += x.linesize[i]; } return true; case SUBTITLE_TEXT: { size_t s = 0; if (!deserializeItem(y, l, p, s)) { return false; } if (p + s > l) { return false; } x.text = (char*)av_malloc(s + 1); memcpy(x.text, y + p, s); x.text[s] = 0; p += s; return true; } case SUBTITLE_ASS: { size_t s = 0; if (!deserializeItem(y, l, p, s)) { return false; } if (p + s > l) { return false; } x.ass = (char*)av_malloc(s + 1); memcpy(x.ass, y + p, s); x.ass[s] = 0; p += s; return true; } default: return true; } }; return deserializeItem(src, len, pos, dest.x) && deserializeItem(src, len, pos, dest.y) && deserializeItem(src, len, pos, dest.w) && deserializeItem(src, len, pos, dest.h) && deserializeItem(src, len, pos, dest.nb_colors) && deserializeItem(src, len, pos, dest.type) && deserializeItem(src, len, pos, dest.flags) && rectDeserialize(src, len, pos, dest); } inline bool deserializeItem( const uint8_t* src, size_t len, size_t& pos, AVSubtitle& dest) { auto rectDeserialize = [](const uint8_t* y, size_t l, size_t& p, AVSubtitle& x) -> bool { bool res = deserializeItem(y, l, p, x.num_rects); if (res && x.num_rects) { x.rects = (AVSubtitleRect**)av_malloc(x.num_rects * sizeof(AVSubtitleRect*)); } for (unsigned i = 0; res && i < x.num_rects; ++i) { x.rects[i] = (AVSubtitleRect*)av_malloc(sizeof(AVSubtitleRect)); memset(x.rects[i], 0, sizeof(AVSubtitleRect)); res = deserializeItem(y, l, p, *x.rects[i]); } return res; }; return deserializeItem(src, len, pos, dest.format) && deserializeItem(src, len, pos, dest.start_display_time) && deserializeItem(src, len, pos, dest.end_display_time) && deserializeItem(src, len, pos, dest.pts) && rectDeserialize(src, len, pos, dest); } } // namespace Serializer namespace Util { std::string generateErrorDesc(int errorCode) { std::array buffer; if (av_strerror(errorCode, buffer.data(), buffer.size()) < 0) { return std::string("Unknown error code: ") + std::to_string(errorCode); } buffer.back() = 0; return std::string(buffer.data()); } size_t serialize(const AVSubtitle& sub, ByteStorage* out) { const auto len = size(sub); TORCH_CHECK_LE(len, out->tail()); size_t pos = 0; if (!Serializer::serializeItem(out->writableTail(), len, pos, sub)) { return 0; } out->append(len); return len; } bool deserialize(const ByteStorage& buf, AVSubtitle* sub) { size_t pos = 0; return Serializer::deserializeItem(buf.data(), buf.length(), pos, *sub); } size_t size(const AVSubtitle& sub) { return Serializer::getSize(sub); } bool validateVideoFormat(const VideoFormat& f) { // clang-format off /* Valid parameters values for decoder ____________________________________________________________________________________ | W | H | minDimension | maxDimension | cropImage | algorithm | |__________________________________________________________________________________| | 0 | 0 | 0 | 0 | N/A | original | |__________________________________________________________________________________| | >0 | 0 | N/A | N/A | N/A | scale keeping W | |__________________________________________________________________________________| | 0 | >0 | N/A | N/A | N/A | scale keeping H | |__________________________________________________________________________________| | >0 | >0 | N/A | N/A | 0 | stretch/scale | |__________________________________________________________________________________| | >0 | >0 | N/A | N/A | >0 | scale/crop | |__________________________________________________________________________________| | 0 | 0 | >0 | 0 | N/A |scale to min dimension | |__________________________________________________________________________________| | 0 | 0 | 0 | >0 | N/A |scale to max dimension | |__________________________________________________________________________________| | 0 | 0 | >0 | >0 | N/A |stretch to min/max dimension| |_____|_____|______________|______________|___________|____________________________| */ // clang-format on return (f.width == 0 && // #1, #6, #7 and #8 f.height == 0 && f.cropImage == 0) || (f.width != 0 && // #4 and #5 f.height != 0 && f.minDimension == 0 && f.maxDimension == 0) || (((f.width != 0 && // #2 f.height == 0) || (f.width == 0 && // #3 f.height != 0)) && f.minDimension == 0 && f.maxDimension == 0 && f.cropImage == 0); } void setFormatDimensions( size_t& destW, size_t& destH, size_t userW, size_t userH, size_t srcW, size_t srcH, size_t minDimension, size_t maxDimension, size_t cropImage) { // rounding rules // int -> double -> round up // if fraction is >= 0.5 or round down if fraction is < 0.5 // int result = double(value) + 0.5 // here we rounding double to int according to the above rule // #1, #6, #7 and #8 if (userW == 0 && userH == 0) { if (minDimension > 0 && maxDimension == 0) { // #6 if (srcW > srcH) { // landscape destH = minDimension; destW = round(double(srcW * minDimension) / srcH); } else { // portrait destW = minDimension; destH = round(double(srcH * minDimension) / srcW); } } else if (minDimension == 0 && maxDimension > 0) { // #7 if (srcW > srcH) { // landscape destW = maxDimension; destH = round(double(srcH * maxDimension) / srcW); } else { // portrait destH = maxDimension; destW = round(double(srcW * maxDimension) / srcH); } } else if (minDimension > 0 && maxDimension > 0) { // #8 if (srcW > srcH) { // landscape destW = maxDimension; destH = minDimension; } else { // portrait destW = minDimension; destH = maxDimension; } } else { // #1 destW = srcW; destH = srcH; } } else if (userW != 0 && userH == 0) { // #2 destW = userW; destH = round(double(srcH * userW) / srcW); } else if (userW == 0 && userH != 0) { // #3 destW = round(double(srcW * userH) / srcH); destH = userH; } else { // userW != 0 && userH != 0 if (cropImage == 0) { // #4 destW = userW; destH = userH; } else { // #5 double userSlope = double(userH) / userW; double srcSlope = double(srcH) / srcW; if (srcSlope < userSlope) { destW = round(double(srcW * userH) / srcH); destH = userH; } else { destW = userW; destH = round(double(srcH * userW) / srcW); } } } // prevent zeros destW = std::max(destW, size_t(1UL)); destH = std::max(destH, size_t(1UL)); } } // namespace Util } // namespace ffmpeg