1
0

ffmpeg-utils.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /******************************************************************************
  2. Copyright (C) 2023 by Dennis Sädtler <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "ffmpeg-utils.hpp"
  15. #include <unordered_map>
  16. #include <unordered_set>
  17. extern "C" {
  18. #include <libavformat/avformat.h>
  19. }
  20. using namespace std;
  21. static void GetCodecsForId(const FFmpegFormat &format,
  22. vector<FFmpegCodec> &codecs, enum AVCodecID id,
  23. bool ignore_compaibility)
  24. {
  25. const AVCodec *codec = nullptr;
  26. void *i = 0;
  27. while ((codec = av_codec_iterate(&i)) != nullptr) {
  28. if (codec->id != id)
  29. continue;
  30. // Not an encoding codec
  31. if (!av_codec_is_encoder(codec))
  32. continue;
  33. // Skip if not supported and compatibility check not disabled
  34. if (!ignore_compaibility &&
  35. !av_codec_get_tag(format.codec_tags, codec->id)) {
  36. continue;
  37. }
  38. FFmpegCodec d{codec->name, codec->long_name, codec->id};
  39. const AVCodec *base_codec = avcodec_find_encoder(codec->id);
  40. if (strcmp(base_codec->name, codec->name) != 0) {
  41. d.alias = true;
  42. d.base_name = base_codec->name;
  43. }
  44. switch (codec->type) {
  45. case AVMEDIA_TYPE_AUDIO:
  46. d.type = FFmpegCodecType::AUDIO;
  47. break;
  48. case AVMEDIA_TYPE_VIDEO:
  49. d.type = FFmpegCodecType::VIDEO;
  50. break;
  51. default:
  52. d.type = FFmpegCodecType::UNKNOWN;
  53. }
  54. codecs.push_back(d);
  55. }
  56. }
  57. static std::vector<const AVCodecDescriptor *> GetCodecDescriptors()
  58. {
  59. std::vector<const AVCodecDescriptor *> codecs;
  60. const AVCodecDescriptor *desc = nullptr;
  61. while ((desc = avcodec_descriptor_next(desc)) != nullptr)
  62. codecs.push_back(desc);
  63. return codecs;
  64. }
  65. vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format,
  66. bool ignore_compatibility)
  67. {
  68. vector<FFmpegCodec> codecs;
  69. auto codecDescriptors = GetCodecDescriptors();
  70. if (codecDescriptors.empty())
  71. return codecs;
  72. for (const AVCodecDescriptor *codec : codecDescriptors)
  73. GetCodecsForId(format, codecs, codec->id, ignore_compatibility);
  74. return codecs;
  75. }
  76. static inline bool is_output_device(const AVClass *avclass)
  77. {
  78. if (!avclass)
  79. return 0;
  80. switch (avclass->category) {
  81. case AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT:
  82. case AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT:
  83. case AV_CLASS_CATEGORY_DEVICE_OUTPUT:
  84. return true;
  85. default:
  86. return false;
  87. }
  88. }
  89. vector<FFmpegFormat> GetSupportedFormats()
  90. {
  91. vector<FFmpegFormat> formats;
  92. const AVOutputFormat *output_format;
  93. void *i = 0;
  94. while ((output_format = av_muxer_iterate(&i)) != nullptr) {
  95. if (is_output_device(output_format->priv_class))
  96. continue;
  97. formats.push_back({
  98. output_format->name,
  99. output_format->long_name,
  100. output_format->mime_type,
  101. output_format->extensions,
  102. output_format->audio_codec,
  103. output_format->video_codec,
  104. output_format->codec_tag,
  105. });
  106. }
  107. return formats;
  108. }
  109. static const char *get_encoder_name(const char *format_name,
  110. enum AVCodecID codec_id)
  111. {
  112. const AVCodec *codec = avcodec_find_encoder(codec_id);
  113. if (codec == nullptr && codec_id == AV_CODEC_ID_NONE)
  114. return nullptr;
  115. else if (codec == nullptr)
  116. return format_name;
  117. else
  118. return codec->name;
  119. }
  120. const char *FFmpegFormat::GetDefaultName(FFmpegCodecType codec_type) const
  121. {
  122. switch (codec_type) {
  123. case FFmpegCodecType::AUDIO:
  124. return get_encoder_name(name, audio_codec);
  125. case FFmpegCodecType::VIDEO:
  126. return get_encoder_name(name, video_codec);
  127. default:
  128. return nullptr;
  129. }
  130. }
  131. bool FFCodecAndFormatCompatible(const char *codec, const char *format)
  132. {
  133. if (!codec || !format)
  134. return false;
  135. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100)
  136. AVOutputFormat *output_format;
  137. #else
  138. const AVOutputFormat *output_format;
  139. #endif
  140. output_format = av_guess_format(format, NULL, NULL);
  141. if (!output_format)
  142. return false;
  143. const AVCodecDescriptor *codec_desc =
  144. avcodec_descriptor_get_by_name(codec);
  145. if (!codec_desc)
  146. return false;
  147. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100)
  148. return avformat_query_codec(output_format, codec_desc->id,
  149. FF_COMPLIANCE_EXPERIMENTAL) == 1;
  150. #else
  151. return avformat_query_codec(output_format, codec_desc->id,
  152. FF_COMPLIANCE_NORMAL) == 1;
  153. #endif
  154. }
  155. static const unordered_set<string> builtin_codecs = {
  156. "h264", "hevc", "av1", "prores", "aac", "opus",
  157. "alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
  158. };
  159. bool IsBuiltinCodec(const char *codec)
  160. {
  161. return builtin_codecs.count(codec) > 0;
  162. }
  163. static const unordered_map<string, unordered_set<string>> codec_compat = {
  164. // Technically our muxer supports HEVC and AV1 as well, but nothing else does
  165. {"flv",
  166. {
  167. "h264",
  168. "aac",
  169. }},
  170. {"mpegts",
  171. {
  172. "h264",
  173. "hevc",
  174. "aac",
  175. "opus",
  176. }},
  177. {"hls",
  178. // Also using MPEG-TS in our case, but no Opus support
  179. {
  180. "h264",
  181. "hevc",
  182. "aac",
  183. }},
  184. {"mp4",
  185. {
  186. "h264",
  187. "hevc",
  188. "av1",
  189. "aac",
  190. "opus",
  191. "alac",
  192. "flac",
  193. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  194. // PCM in MP4 is only supported in FFmpeg > 6.0
  195. "pcm_s16le",
  196. "pcm_s24le",
  197. "pcm_f32le",
  198. #endif
  199. }},
  200. {"fragmented_mp4",
  201. {
  202. "h264",
  203. "hevc",
  204. "av1",
  205. "aac",
  206. "opus",
  207. "alac",
  208. "flac",
  209. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  210. "pcm_s16le",
  211. "pcm_s24le",
  212. "pcm_f32le",
  213. #endif
  214. }},
  215. {"mov",
  216. {
  217. "h264",
  218. "hevc",
  219. "prores",
  220. "aac",
  221. "alac",
  222. "pcm_s16le",
  223. "pcm_s24le",
  224. "pcm_f32le",
  225. }},
  226. {"fragmented_mov",
  227. {
  228. "h264",
  229. "hevc",
  230. "prores",
  231. "aac",
  232. "alac",
  233. "pcm_s16le",
  234. "pcm_s24le",
  235. "pcm_f32le",
  236. }},
  237. // MKV supports everything
  238. {"mkv", {}},
  239. };
  240. bool ContainerSupportsCodec(const string &container, const string &codec)
  241. {
  242. auto iter = codec_compat.find(container);
  243. if (iter == codec_compat.end())
  244. return false;
  245. auto codecs = iter->second;
  246. // Assume everything is supported
  247. if (codecs.empty())
  248. return true;
  249. return codecs.count(codec) > 0;
  250. }