obs-x264.c 13 KB

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