obs-x264.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /******************************************************************************
  2. Copyright (C) 2014 by Hugh Bailey <[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 <util/dstr.h>
  15. #include <util/darray.h>
  16. #include <obs.h>
  17. #include <x264.h>
  18. struct obs_x264 {
  19. obs_encoder_t encoder;
  20. x264_param_t params;
  21. x264_t *context;
  22. DARRAY(uint8_t) packet_data;
  23. uint8_t *extra_data;
  24. uint8_t *sei;
  25. size_t extra_data_size;
  26. size_t sei_size;
  27. };
  28. /* ------------------------------------------------------------------------- */
  29. static const char *obs_x264_getname(void)
  30. {
  31. /* TODO locale lookup */
  32. return "x264";
  33. }
  34. static void obs_x264_stop(void *data);
  35. static void clear_data(struct obs_x264 *obsx264)
  36. {
  37. if (obsx264->context) {
  38. x264_encoder_close(obsx264->context);
  39. bfree(obsx264->sei);
  40. bfree(obsx264->extra_data);
  41. obsx264->context = NULL;
  42. obsx264->sei = NULL;
  43. obsx264->extra_data = NULL;
  44. }
  45. }
  46. static void obs_x264_destroy(void *data)
  47. {
  48. struct obs_x264 *obsx264 = data;
  49. if (obsx264) {
  50. clear_data(obsx264);
  51. da_free(obsx264->packet_data);
  52. bfree(obsx264);
  53. }
  54. }
  55. static void obs_x264_defaults(obs_data_t settings)
  56. {
  57. obs_data_set_default_int (settings, "bitrate", 1000);
  58. obs_data_set_default_int (settings, "buffer_size", 1000);
  59. obs_data_set_default_int (settings, "keyint_sec", 0);
  60. obs_data_set_default_int (settings, "crf", 23);
  61. obs_data_set_default_bool (settings, "cbr", false);
  62. obs_data_set_default_string(settings, "preset", "veryfast");
  63. obs_data_set_default_string(settings, "profile", "");
  64. obs_data_set_default_string(settings, "tune", "");
  65. obs_data_set_default_string(settings, "x264opts", "");
  66. }
  67. static inline void add_strings(obs_property_t list, const char *const *strings)
  68. {
  69. while (*strings) {
  70. obs_property_list_add_string(list, *strings, *strings);
  71. strings++;
  72. }
  73. }
  74. static obs_properties_t obs_x264_props(void)
  75. {
  76. /* TODO: locale */
  77. obs_properties_t props = obs_properties_create();
  78. obs_property_t list;
  79. obs_properties_add_int(props, "bitrate", "Bitrate", 50, 100000, 1);
  80. obs_properties_add_int(props, "buffer_size", "Buffer Size", 50, 100000,
  81. 1);
  82. obs_properties_add_int(props,
  83. "keyint_sec", "Keyframe interval (seconds, 0=auto)",
  84. 0, 20, 1);
  85. list = obs_properties_add_list(props,
  86. "preset", "CPU Usage Preset (encoder speed)",
  87. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
  88. add_strings(list, x264_preset_names);
  89. list = obs_properties_add_list(props, "profile", "Profile",
  90. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
  91. obs_property_list_add_string(list, "baseline", "baseline");
  92. obs_property_list_add_string(list, "main", "main");
  93. obs_property_list_add_string(list, "high", "high");
  94. list = obs_properties_add_list(props, "tune", "Tune",
  95. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
  96. add_strings(list, x264_tune_names);
  97. obs_properties_add_text(props, "x264opts",
  98. "x264 encoder options (separated by space)",
  99. OBS_TEXT_DEFAULT);
  100. return props;
  101. }
  102. static bool getparam(const char *param, char **name, const char **value)
  103. {
  104. const char *assign;
  105. if (!param || !*param || (*param == '='))
  106. return false;
  107. assign = strchr(param, '=');
  108. if (!assign || !*assign || !*(assign+1))
  109. return false;
  110. *name = bstrdup_n(param, assign-param);
  111. *value = assign+1;
  112. return true;
  113. }
  114. static void override_base_param(const char *param,
  115. char **preset, char **profile, char **tune)
  116. {
  117. char *name;
  118. const char *val;
  119. if (getparam(param, &name, &val)) {
  120. if (astrcmpi(name, "preset") == 0) {
  121. bfree(*preset);
  122. *preset = bstrdup(val);
  123. } else if (astrcmpi(name, "profile") == 0) {
  124. bfree(*profile);
  125. *profile = bstrdup(val);
  126. } else if (astrcmpi(name, "tune") == 0) {
  127. bfree(*tune);
  128. *tune = bstrdup(val);
  129. }
  130. bfree(name);
  131. }
  132. }
  133. static inline void override_base_params(char **params,
  134. char **preset, char **profile, char **tune)
  135. {
  136. while (*params)
  137. override_base_param(*(params++), preset, profile, tune);
  138. }
  139. static inline void set_param(struct obs_x264 *obsx264, const char *param)
  140. {
  141. char *name;
  142. const char *val;
  143. if (getparam(param, &name, &val)) {
  144. if (x264_param_parse(&obsx264->params, name, val) != 0)
  145. blog(LOG_WARNING, "x264 param: %s failed", param);
  146. bfree(name);
  147. }
  148. }
  149. static inline void apply_x264_profile(struct obs_x264 *obsx264,
  150. const char *profile)
  151. {
  152. if (!obsx264->context && profile) {
  153. int ret = x264_param_apply_profile(&obsx264->params, profile);
  154. if (ret != 0)
  155. blog(LOG_WARNING, "Failed to set x264 "
  156. "profile '%s'", profile);
  157. }
  158. }
  159. static bool reset_x264_params(struct obs_x264 *obsx264,
  160. const char *preset, const char *tune)
  161. {
  162. return x264_param_default_preset(&obsx264->params, preset, tune) == 0;
  163. }
  164. static void log_x264(void *param, int level, const char *format, va_list args)
  165. {
  166. blogva(LOG_INFO, format, args);
  167. UNUSED_PARAMETER(param);
  168. UNUSED_PARAMETER(level);
  169. }
  170. static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
  171. char **params)
  172. {
  173. video_t video = obs_encoder_video(obsx264->encoder);
  174. const struct video_output_info *voi = video_output_getinfo(video);
  175. int bitrate = (int)obs_data_getint(settings, "bitrate");
  176. int buffer_size = (int)obs_data_getint(settings, "buffer_size");
  177. int keyint_sec = (int)obs_data_getint(settings, "keyint_sec");
  178. int crf = (int)obs_data_getint(settings, "crf");
  179. bool cbr = obs_data_getbool(settings, "cbr");
  180. if (keyint_sec)
  181. obsx264->params.i_keyint_max =
  182. keyint_sec * voi->fps_num / voi->fps_den;
  183. obsx264->params.b_vfr_input = false;
  184. obsx264->params.rc.i_vbv_max_bitrate = bitrate;
  185. obsx264->params.rc.i_vbv_buffer_size = buffer_size;
  186. obsx264->params.rc.i_bitrate = bitrate;
  187. obsx264->params.i_width = voi->width;
  188. obsx264->params.i_height = voi->height;
  189. obsx264->params.i_fps_num = voi->fps_num;
  190. obsx264->params.i_fps_den = voi->fps_den;
  191. obsx264->params.pf_log = log_x264;
  192. obsx264->params.i_log_level = X264_LOG_WARNING;
  193. /* use the new filler method for CBR to allow real-time adjusting of
  194. * the bitrate */
  195. if (cbr) {
  196. obsx264->params.rc.f_rf_constant = 0.0f;
  197. #if X264_BUILD >= 139
  198. obsx264->params.rc.b_filler = true;
  199. obsx264->params.rc.i_rc_method = X264_RC_ABR;
  200. #else
  201. obsx264->params.i_nal_hrd = X264_NAL_HRD_CBR;
  202. #endif
  203. } else {
  204. obsx264->params.rc.i_rc_method = X264_RC_CRF;
  205. obsx264->params.rc.f_rf_constant = (float)crf;
  206. }
  207. if (voi->format == VIDEO_FORMAT_NV12)
  208. obsx264->params.i_csp = X264_CSP_NV12;
  209. else if (voi->format == VIDEO_FORMAT_I420)
  210. obsx264->params.i_csp = X264_CSP_I420;
  211. else
  212. obsx264->params.i_csp = X264_CSP_NV12;
  213. while (*params)
  214. set_param(obsx264, *(params++));
  215. }
  216. static bool update_settings(struct obs_x264 *obsx264, obs_data_t settings)
  217. {
  218. char *preset = bstrdup(obs_data_getstring(settings, "preset"));
  219. char *profile = bstrdup(obs_data_getstring(settings, "profile"));
  220. char *tune = bstrdup(obs_data_getstring(settings, "tune"));
  221. const char *opts = obs_data_getstring(settings, "x264opts");
  222. char **paramlist;
  223. bool success = true;
  224. paramlist = strlist_split(opts, ' ', false);
  225. if (!obsx264->context) {
  226. override_base_params(paramlist, &preset, &tune, &profile);
  227. success = reset_x264_params(obsx264, preset, tune);
  228. }
  229. if (success) {
  230. update_params(obsx264, settings, paramlist);
  231. if (!obsx264->context)
  232. apply_x264_profile(obsx264, profile);
  233. }
  234. obsx264->params.b_repeat_headers = false;
  235. strlist_free(paramlist);
  236. bfree(preset);
  237. bfree(profile);
  238. bfree(tune);
  239. return success;
  240. }
  241. static bool obs_x264_update(void *data, obs_data_t settings)
  242. {
  243. struct obs_x264 *obsx264 = data;
  244. bool success = update_settings(obsx264, settings);
  245. int ret;
  246. if (success) {
  247. ret = x264_encoder_reconfig(obsx264->context, &obsx264->params);
  248. if (ret != 0)
  249. blog(LOG_WARNING, "Failed to reconfigure x264: %d",
  250. ret);
  251. return ret == 0;
  252. }
  253. return false;
  254. }
  255. static void load_headers(struct obs_x264 *obsx264)
  256. {
  257. x264_nal_t *nals;
  258. int nal_count;
  259. DARRAY(uint8_t) header;
  260. DARRAY(uint8_t) sei;
  261. da_init(header);
  262. da_init(sei);
  263. x264_encoder_headers(obsx264->context, &nals, &nal_count);
  264. for (int i = 0; i < nal_count; i++) {
  265. x264_nal_t *nal = nals+i;
  266. if (nal->i_type == NAL_SEI)
  267. da_push_back_array(sei, nal->p_payload, nal->i_payload);
  268. else
  269. da_push_back_array(header, nal->p_payload,
  270. nal->i_payload);
  271. }
  272. obsx264->extra_data = header.array;
  273. obsx264->extra_data_size = header.num;
  274. obsx264->sei = sei.array;
  275. obsx264->sei_size = sei.num;
  276. }
  277. static void *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
  278. {
  279. struct obs_x264 *obsx264 = bzalloc(sizeof(struct obs_x264));
  280. obsx264->encoder = encoder;
  281. if (update_settings(obsx264, settings)) {
  282. obsx264->context = x264_encoder_open(&obsx264->params);
  283. if (obsx264->context == NULL)
  284. blog(LOG_WARNING, "x264 failed to load");
  285. else
  286. load_headers(obsx264);
  287. } else {
  288. blog(LOG_WARNING, "bad settings specified for x264");
  289. }
  290. if (!obsx264->context) {
  291. bfree(obsx264);
  292. return NULL;
  293. }
  294. return obsx264;
  295. }
  296. static void parse_packet(struct obs_x264 *obsx264,
  297. struct encoder_packet *packet, x264_nal_t *nals,
  298. int nal_count, x264_picture_t *pic_out)
  299. {
  300. if (!nal_count) return;
  301. da_resize(obsx264->packet_data, 0);
  302. for (int i = 0; i < nal_count; i++) {
  303. x264_nal_t *nal = nals+i;
  304. da_push_back_array(obsx264->packet_data, nal->p_payload,
  305. nal->i_payload);
  306. }
  307. packet->data = obsx264->packet_data.array;
  308. packet->size = obsx264->packet_data.num;
  309. packet->type = OBS_ENCODER_VIDEO;
  310. packet->pts = pic_out->i_pts;
  311. packet->dts = pic_out->i_dts;
  312. packet->keyframe = pic_out->b_keyframe != 0;
  313. }
  314. static inline void init_pic_data(struct obs_x264 *obsx264, x264_picture_t *pic,
  315. struct encoder_frame *frame)
  316. {
  317. x264_picture_init(pic);
  318. pic->i_pts = frame->pts;
  319. pic->img.i_csp = obsx264->params.i_csp;
  320. if (obsx264->params.i_csp == X264_CSP_NV12)
  321. pic->img.i_plane = 2;
  322. else if (obsx264->params.i_csp == X264_CSP_I420)
  323. pic->img.i_plane = 3;
  324. for (int i = 0; i < pic->img.i_plane; i++) {
  325. pic->img.i_stride[i] = (int)frame->linesize[i];
  326. pic->img.plane[i] = frame->data[i];
  327. }
  328. }
  329. static bool obs_x264_encode(void *data, struct encoder_frame *frame,
  330. struct encoder_packet *packet, bool *received_packet)
  331. {
  332. struct obs_x264 *obsx264 = data;
  333. x264_nal_t *nals;
  334. int nal_count;
  335. int ret;
  336. x264_picture_t pic, pic_out;
  337. if (!frame || !packet || !received_packet)
  338. return false;
  339. if (frame)
  340. init_pic_data(obsx264, &pic, frame);
  341. ret = x264_encoder_encode(obsx264->context, &nals, &nal_count,
  342. (frame ? &pic : NULL), &pic_out);
  343. if (ret < 0) {
  344. blog(LOG_WARNING, "x264 encode failed");
  345. return false;
  346. }
  347. *received_packet = (nal_count != 0);
  348. parse_packet(obsx264, packet, nals, nal_count, &pic_out);
  349. return true;
  350. }
  351. static bool obs_x264_extra_data(void *data, uint8_t **extra_data, size_t *size)
  352. {
  353. struct obs_x264 *obsx264 = data;
  354. if (!obsx264->context)
  355. return false;
  356. *extra_data = obsx264->extra_data;
  357. *size = obsx264->extra_data_size;
  358. return true;
  359. }
  360. static bool obs_x264_sei(void *data, uint8_t **sei, size_t *size)
  361. {
  362. struct obs_x264 *obsx264 = data;
  363. if (!obsx264->context)
  364. return false;
  365. *sei = obsx264->sei;
  366. *size = obsx264->sei_size;
  367. return true;
  368. }
  369. static bool obs_x264_video_info(void *data, struct video_scale_info *info)
  370. {
  371. struct obs_x264 *obsx264 = data;
  372. video_t video = obs_encoder_video(obsx264->encoder);
  373. const struct video_output_info *vid_info = video_output_getinfo(video);
  374. if (vid_info->format == VIDEO_FORMAT_I420 ||
  375. vid_info->format == VIDEO_FORMAT_NV12)
  376. return false;
  377. info->format = VIDEO_FORMAT_NV12;
  378. info->width = vid_info->width;
  379. info->height = vid_info->height;
  380. info->range = VIDEO_RANGE_DEFAULT;
  381. info->colorspace = VIDEO_CS_DEFAULT;
  382. return true;
  383. }
  384. struct obs_encoder_info obs_x264_encoder = {
  385. .id = "obs_x264",
  386. .type = OBS_ENCODER_VIDEO,
  387. .codec = "h264",
  388. .getname = obs_x264_getname,
  389. .create = obs_x264_create,
  390. .destroy = obs_x264_destroy,
  391. .encode = obs_x264_encode,
  392. .properties = obs_x264_props,
  393. .defaults = obs_x264_defaults,
  394. .update = obs_x264_update,
  395. .extra_data = obs_x264_extra_data,
  396. .sei_data = obs_x264_sei,
  397. .video_info = obs_x264_video_info
  398. };