ffmpeg-utils.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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 bool is_output_device(const AVClass *avclass)
  41. {
  42. if (!avclass)
  43. return false;
  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. FFmpegCodec FFmpegFormat::GetDefaultEncoder(FFmpegCodecType codec_type) const
  66. {
  67. const AVCodecID codec_id = codec_type == VIDEO ? video_codec
  68. : audio_codec;
  69. if (codec_type == UNKNOWN || codec_id == AV_CODEC_ID_NONE)
  70. return {};
  71. if (auto codec = avcodec_find_encoder(codec_id))
  72. return {codec};
  73. /* Fall back to using the format name as the encoder,
  74. * this works for some formats such as FLV. */
  75. return FFmpegCodec{name, codec_id, codec_type};
  76. }
  77. bool FFCodecAndFormatCompatible(const char *codec, const char *format)
  78. {
  79. if (!codec || !format)
  80. return false;
  81. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100)
  82. AVOutputFormat *output_format;
  83. #else
  84. const AVOutputFormat *output_format;
  85. #endif
  86. output_format = av_guess_format(format, nullptr, nullptr);
  87. if (!output_format)
  88. return false;
  89. const AVCodecDescriptor *codec_desc =
  90. avcodec_descriptor_get_by_name(codec);
  91. if (!codec_desc)
  92. return false;
  93. #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100)
  94. return avformat_query_codec(output_format, codec_desc->id,
  95. FF_COMPLIANCE_EXPERIMENTAL) == 1;
  96. #else
  97. return avformat_query_codec(output_format, codec_desc->id,
  98. FF_COMPLIANCE_NORMAL) == 1;
  99. #endif
  100. }
  101. static const unordered_set<string> builtin_codecs = {
  102. "h264", "hevc", "av1", "prores", "aac", "opus",
  103. "alac", "flac", "pcm_s16le", "pcm_s24le", "pcm_f32le",
  104. };
  105. bool IsBuiltinCodec(const char *codec)
  106. {
  107. return builtin_codecs.count(codec) > 0;
  108. }
  109. static const unordered_map<string, unordered_set<string>> codec_compat = {
  110. // Technically our muxer supports HEVC and AV1 as well, but nothing else does
  111. {"flv",
  112. {
  113. "h264",
  114. "aac",
  115. }},
  116. {"mpegts",
  117. {
  118. "h264",
  119. "hevc",
  120. "aac",
  121. "opus",
  122. }},
  123. {"hls",
  124. // Also using MPEG-TS in our case, but no Opus support
  125. {
  126. "h264",
  127. "hevc",
  128. "aac",
  129. }},
  130. {"mp4",
  131. {
  132. "h264",
  133. "hevc",
  134. "av1",
  135. "aac",
  136. "opus",
  137. "alac",
  138. "flac",
  139. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  140. // PCM in MP4 is only supported in FFmpeg > 6.0
  141. "pcm_s16le",
  142. "pcm_s24le",
  143. "pcm_f32le",
  144. #endif
  145. }},
  146. {"fragmented_mp4",
  147. {
  148. "h264",
  149. "hevc",
  150. "av1",
  151. "aac",
  152. "opus",
  153. "alac",
  154. "flac",
  155. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100)
  156. "pcm_s16le",
  157. "pcm_s24le",
  158. "pcm_f32le",
  159. #endif
  160. }},
  161. {"mov",
  162. {
  163. "h264",
  164. "hevc",
  165. "prores",
  166. "aac",
  167. "alac",
  168. "pcm_s16le",
  169. "pcm_s24le",
  170. "pcm_f32le",
  171. }},
  172. {"fragmented_mov",
  173. {
  174. "h264",
  175. "hevc",
  176. "prores",
  177. "aac",
  178. "alac",
  179. "pcm_s16le",
  180. "pcm_s24le",
  181. "pcm_f32le",
  182. }},
  183. // MKV supports everything
  184. {"mkv", {}},
  185. };
  186. bool ContainerSupportsCodec(const string &container, const string &codec)
  187. {
  188. auto iter = codec_compat.find(container);
  189. if (iter == codec_compat.end())
  190. return false;
  191. auto codecs = iter->second;
  192. // Assume everything is supported
  193. if (codecs.empty())
  194. return true;
  195. return codecs.count(codec) > 0;
  196. }