ffmpeg-utils.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format,
  22. bool ignore_compatibility)
  23. {
  24. vector<FFmpegCodec> codecs;
  25. const AVCodec *codec;
  26. void *i = 0;
  27. while ((codec = av_codec_iterate(&i)) != nullptr) {
  28. // Not an encoding codec
  29. if (!av_codec_is_encoder(codec))
  30. continue;
  31. // Skip if not supported and compatibility check not disabled
  32. if (!ignore_compatibility &&
  33. !av_codec_get_tag(format.codec_tags, codec->id)) {
  34. continue;
  35. }
  36. codecs.emplace_back(codec);
  37. }
  38. return codecs;
  39. }
  40. static inline bool is_output_device(const AVClass *avclass)
  41. {
  42. if (!avclass)
  43. return 0;
  44. switch (avclass->category) {
  45. case AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT:
  46. case AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT:
  47. case AV_CLASS_CATEGORY_DEVICE_OUTPUT:
  48. return true;
  49. default:
  50. return false;
  51. }
  52. }
  53. vector<FFmpegFormat> GetSupportedFormats()
  54. {
  55. vector<FFmpegFormat> formats;
  56. const AVOutputFormat *output_format;
  57. void *i = 0;
  58. while ((output_format = av_muxer_iterate(&i)) != nullptr) {
  59. if (is_output_device(output_format->priv_class))
  60. continue;
  61. formats.emplace_back(output_format);
  62. }
  63. return formats;
  64. }
  65. static const char *get_encoder_name(const char *format_name,
  66. enum AVCodecID codec_id)
  67. {
  68. const AVCodec *codec = avcodec_find_encoder(codec_id);
  69. if (codec == nullptr && codec_id == AV_CODEC_ID_NONE)
  70. return nullptr;
  71. else if (codec == nullptr)
  72. return format_name;
  73. else
  74. return codec->name;
  75. }
  76. const char *FFmpegFormat::GetDefaultName(FFmpegCodecType codec_type) const
  77. {
  78. switch (codec_type) {
  79. case FFmpegCodecType::AUDIO:
  80. return get_encoder_name(name, audio_codec);
  81. case FFmpegCodecType::VIDEO:
  82. return get_encoder_name(name, video_codec);
  83. default:
  84. return nullptr;
  85. }
  86. }
  87. bool FFCodecAndFormatCompatible(const char *codec, const char *format)
  88. {
  89. if (!codec || !format)
  90. return false;
  91. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100)
  92. AVOutputFormat *output_format;
  93. #else
  94. const AVOutputFormat *output_format;
  95. #endif
  96. output_format = av_guess_format(format, NULL, NULL);
  97. if (!output_format)
  98. return false;
  99. const AVCodecDescriptor *codec_desc =
  100. avcodec_descriptor_get_by_name(codec);
  101. if (!codec_desc)
  102. return false;
  103. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100)
  104. return avformat_query_codec(output_format, codec_desc->id,
  105. FF_COMPLIANCE_EXPERIMENTAL) == 1;
  106. #else
  107. return avformat_query_codec(output_format, codec_desc->id,
  108. FF_COMPLIANCE_NORMAL) == 1;
  109. #endif
  110. }
  111. static const unordered_set<string> builtin_codecs = {
  112. "h264", "hevc", "av1", "prores", "aac", "opus",
  113. "alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
  114. };
  115. bool IsBuiltinCodec(const char *codec)
  116. {
  117. return builtin_codecs.count(codec) > 0;
  118. }
  119. static const unordered_map<string, unordered_set<string>> codec_compat = {
  120. // Technically our muxer supports HEVC and AV1 as well, but nothing else does
  121. {"flv",
  122. {
  123. "h264",
  124. "aac",
  125. }},
  126. {"mpegts",
  127. {
  128. "h264",
  129. "hevc",
  130. "aac",
  131. "opus",
  132. }},
  133. {"hls",
  134. // Also using MPEG-TS in our case, but no Opus support
  135. {
  136. "h264",
  137. "hevc",
  138. "aac",
  139. }},
  140. {"mp4",
  141. {
  142. "h264",
  143. "hevc",
  144. "av1",
  145. "aac",
  146. "opus",
  147. "alac",
  148. "flac",
  149. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  150. // PCM in MP4 is only supported in FFmpeg > 6.0
  151. "pcm_s16le",
  152. "pcm_s24le",
  153. "pcm_f32le",
  154. #endif
  155. }},
  156. {"fragmented_mp4",
  157. {
  158. "h264",
  159. "hevc",
  160. "av1",
  161. "aac",
  162. "opus",
  163. "alac",
  164. "flac",
  165. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  166. "pcm_s16le",
  167. "pcm_s24le",
  168. "pcm_f32le",
  169. #endif
  170. }},
  171. {"mov",
  172. {
  173. "h264",
  174. "hevc",
  175. "prores",
  176. "aac",
  177. "alac",
  178. "pcm_s16le",
  179. "pcm_s24le",
  180. "pcm_f32le",
  181. }},
  182. {"fragmented_mov",
  183. {
  184. "h264",
  185. "hevc",
  186. "prores",
  187. "aac",
  188. "alac",
  189. "pcm_s16le",
  190. "pcm_s24le",
  191. "pcm_f32le",
  192. }},
  193. // MKV supports everything
  194. {"mkv", {}},
  195. };
  196. bool ContainerSupportsCodec(const string &container, const string &codec)
  197. {
  198. auto iter = codec_compat.find(container);
  199. if (iter == codec_compat.end())
  200. return false;
  201. auto codecs = iter->second;
  202. // Assume everything is supported
  203. if (codecs.empty())
  204. return true;
  205. return codecs.count(codec) > 0;
  206. }