obs-ffmpeg-openh264.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /******************************************************************************
  2. Copyright (C) 2023 by Neal Gompa <[email protected]>
  3. Partly derived from obs-ffmpeg-av1.c by Lain Bailey <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. ******************************************************************************/
  15. #include "obs-ffmpeg-video-encoders.h"
  16. #define do_log(level, format, ...) \
  17. blog(level, "[H.264 encoder: '%s'] " format, obs_encoder_get_name(enc->ffve.encoder), ##__VA_ARGS__)
  18. #define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)
  19. #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
  20. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
  21. #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
  22. enum openh264_encoder_type {
  23. H264_ENCODER_TYPE_OH264,
  24. };
  25. struct openh264_encoder {
  26. struct ffmpeg_video_encoder ffve;
  27. enum openh264_encoder_type type;
  28. DARRAY(uint8_t) header;
  29. };
  30. static const char *openh264_getname(void *unused)
  31. {
  32. UNUSED_PARAMETER(unused);
  33. return "OpenH264";
  34. }
  35. static void openh264_video_info(void *data, struct video_scale_info *info)
  36. {
  37. UNUSED_PARAMETER(data);
  38. // OpenH264 only supports I420
  39. info->format = VIDEO_FORMAT_I420;
  40. }
  41. static bool openh264_update(struct openh264_encoder *enc, obs_data_t *settings)
  42. {
  43. const char *profile = obs_data_get_string(settings, "profile");
  44. int bitrate = (int)obs_data_get_int(settings, "bitrate");
  45. int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
  46. const char *rc_mode = "quality"; // We only want to use quality mode
  47. int allow_skip_frames = 1; // This is required for quality mode
  48. video_t *video = obs_encoder_video(enc->ffve.encoder);
  49. const struct video_output_info *voi = video_output_get_info(video);
  50. struct video_scale_info info;
  51. info.format = voi->format;
  52. info.colorspace = voi->colorspace;
  53. info.range = voi->range;
  54. enc->ffve.context->thread_count = 0;
  55. openh264_video_info(enc, &info);
  56. av_opt_set(enc->ffve.context->priv_data, "rc_mode", rc_mode, 0);
  57. av_opt_set(enc->ffve.context->priv_data, "profile", profile, 0);
  58. av_opt_set_int(enc->ffve.context->priv_data, "allow_skip_frames", allow_skip_frames, 0);
  59. const char *ffmpeg_opts = obs_data_get_string(settings, "ffmpeg_opts");
  60. ffmpeg_video_encoder_update(&enc->ffve, bitrate, keyint_sec, voi, &info, ffmpeg_opts);
  61. info("settings:\n"
  62. "\tencoder: %s\n"
  63. "\trc_mode: %s\n"
  64. "\tbitrate: %d\n"
  65. "\tkeyint_sec: %d\n"
  66. "\tprofile: %s\n"
  67. "\twidth: %d\n"
  68. "\theight: %d\n"
  69. "\tffmpeg opts: %s\n",
  70. enc->ffve.enc_name, rc_mode, bitrate, keyint_sec, profile, enc->ffve.context->width, enc->ffve.height,
  71. ffmpeg_opts);
  72. enc->ffve.context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  73. return ffmpeg_video_encoder_init_codec(&enc->ffve);
  74. }
  75. static void openh264_destroy(void *data)
  76. {
  77. struct openh264_encoder *enc = data;
  78. ffmpeg_video_encoder_free(&enc->ffve);
  79. da_free(enc->header);
  80. bfree(enc);
  81. }
  82. static void on_first_packet(void *data, AVPacket *pkt, struct darray *da)
  83. {
  84. struct openh264_encoder *enc = data;
  85. da_copy_array(enc->header, enc->ffve.context->extradata, enc->ffve.context->extradata_size);
  86. darray_copy_array(1, da, pkt->data, pkt->size);
  87. }
  88. static void *h264_create_internal(obs_data_t *settings, obs_encoder_t *encoder, const char *enc_lib,
  89. const char *enc_name)
  90. {
  91. video_t *video = obs_encoder_video(encoder);
  92. const struct video_output_info *voi = video_output_get_info(video);
  93. switch (voi->format) {
  94. // planar 4:2:0 formats
  95. case VIDEO_FORMAT_I420: // three-plane
  96. case VIDEO_FORMAT_NV12: // two-plane, luma and packed chroma
  97. // packed 4:2:2 formats
  98. case VIDEO_FORMAT_YVYU:
  99. case VIDEO_FORMAT_YUY2: // YUYV
  100. case VIDEO_FORMAT_UYVY:
  101. // packed uncompressed formats
  102. case VIDEO_FORMAT_RGBA:
  103. case VIDEO_FORMAT_BGRA:
  104. case VIDEO_FORMAT_BGRX:
  105. case VIDEO_FORMAT_BGR3:
  106. case VIDEO_FORMAT_Y800: // grayscale
  107. // planar 4:4:4
  108. case VIDEO_FORMAT_I444:
  109. // planar 4:2:2
  110. case VIDEO_FORMAT_I422:
  111. // planar 4:2:0 with alpha
  112. case VIDEO_FORMAT_I40A:
  113. // planar 4:2:2 with alpha
  114. case VIDEO_FORMAT_I42A:
  115. // planar 4:4:4 with alpha
  116. case VIDEO_FORMAT_YUVA:
  117. // packed 4:4:4 with alpha
  118. case VIDEO_FORMAT_AYUV:
  119. break;
  120. default:; // Make the compiler do the right thing
  121. const char *const text = obs_module_text("H264.UnsupportedVideoFormat");
  122. obs_encoder_set_last_error(encoder, text);
  123. blog(LOG_ERROR, "[H.264 encoder] %s", text);
  124. return NULL;
  125. }
  126. switch (voi->colorspace) {
  127. case VIDEO_CS_DEFAULT:
  128. case VIDEO_CS_709:
  129. break;
  130. default:; // Make the compiler do the right thing
  131. const char *const text = obs_module_text("H264.UnsupportedColorSpace");
  132. obs_encoder_set_last_error(encoder, text);
  133. blog(LOG_ERROR, "[H.264 encoder] %s", text);
  134. return NULL;
  135. }
  136. struct openh264_encoder *enc = bzalloc(sizeof(*enc));
  137. if (strcmp(enc_lib, "libopenh264") == 0)
  138. enc->type = H264_ENCODER_TYPE_OH264;
  139. if (!ffmpeg_video_encoder_init(&enc->ffve, enc, encoder, enc_lib, NULL, enc_name, NULL, on_first_packet))
  140. goto fail;
  141. if (!openh264_update(enc, settings))
  142. goto fail;
  143. return enc;
  144. fail:
  145. openh264_destroy(enc);
  146. return NULL;
  147. }
  148. static void *openh264_create(obs_data_t *settings, obs_encoder_t *encoder)
  149. {
  150. return h264_create_internal(settings, encoder, "libopenh264", "OpenH264");
  151. }
  152. static bool openh264_encode(void *data, struct encoder_frame *frame, struct encoder_packet *packet,
  153. bool *received_packet)
  154. {
  155. struct openh264_encoder *enc = data;
  156. return ffmpeg_video_encode(&enc->ffve, frame, packet, received_packet);
  157. }
  158. void openh264_defaults(obs_data_t *settings)
  159. {
  160. obs_data_set_default_int(settings, "bitrate", 2500);
  161. obs_data_set_default_string(settings, "profile", "main");
  162. }
  163. obs_properties_t *h264_properties(enum openh264_encoder_type type)
  164. {
  165. UNUSED_PARAMETER(type); // Only one encoder right now...
  166. obs_properties_t *props = obs_properties_create();
  167. obs_property_t *p;
  168. p = obs_properties_add_list(props, "profile", obs_module_text("Profile"), OBS_COMBO_TYPE_LIST,
  169. OBS_COMBO_FORMAT_STRING);
  170. obs_property_list_add_string(p, "constrained_baseline", "constrained_baseline");
  171. obs_property_list_add_string(p, "main", "main");
  172. obs_property_list_add_string(p, "high", "high");
  173. p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 50, 300000, 50);
  174. obs_property_int_set_suffix(p, " Kbps");
  175. p = obs_properties_add_int(props, "keyint_sec", obs_module_text("KeyframeIntervalSec"), 0, 20, 1);
  176. obs_property_int_set_suffix(p, " s");
  177. obs_properties_add_text(props, "ffmpeg_opts", obs_module_text("FFmpegOpts"), OBS_TEXT_DEFAULT);
  178. return props;
  179. }
  180. obs_properties_t *openh264_properties(void *unused)
  181. {
  182. UNUSED_PARAMETER(unused);
  183. return h264_properties(H264_ENCODER_TYPE_OH264);
  184. }
  185. static bool openh264_extra_data(void *data, uint8_t **extra_data, size_t *size)
  186. {
  187. struct openh264_encoder *enc = data;
  188. *extra_data = enc->header.array;
  189. *size = enc->header.num;
  190. return true;
  191. }
  192. struct obs_encoder_info openh264_encoder_info = {
  193. .id = "ffmpeg_openh264",
  194. .type = OBS_ENCODER_VIDEO,
  195. .codec = "h264",
  196. .get_name = openh264_getname,
  197. .create = openh264_create,
  198. .destroy = openh264_destroy,
  199. .encode = openh264_encode,
  200. .get_defaults = openh264_defaults,
  201. .get_properties = openh264_properties,
  202. .get_extra_data = openh264_extra_data,
  203. .get_video_info = openh264_video_info,
  204. };