nvenc-opts-parser.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #include "nvenc-internal.h"
  2. #include <stdio.h>
  3. #include <inttypes.h>
  4. /* NVIDIA uses bitfields for a variety of options. As it is not possible to
  5. * use offsetof() or similar with those we resort to macros here to avoid too
  6. * much boilerplate. */
  7. #define APPLY_BIT_OPT(opt_name, bits) \
  8. if (strcmp(opt->name, #opt_name) == 0) { \
  9. uint32_t old_val = nv_conf->opt_name; \
  10. nv_conf->opt_name = strtol(opt->value, NULL, 10); \
  11. blog(LOG_DEBUG, "[obs-nvenc] Changing parameter: \"%s\": %u -> %u (%d bit(s))", #opt_name, old_val, \
  12. nv_conf->opt_name, bits); \
  13. return true; \
  14. }
  15. #define APPLY_INT_OPT(opt_name, type, format) \
  16. if (strcmp(opt->name, #opt_name) == 0) { \
  17. type old_val = nv_conf->opt_name; \
  18. nv_conf->opt_name = (type)strtol(opt->value, NULL, 10); \
  19. blog(LOG_DEBUG, "[obs-nvenc] Changing parameter: \"%s\": %" format " -> %" format " (%s)", #opt_name, \
  20. old_val, nv_conf->opt_name, #type); \
  21. return true; \
  22. }
  23. static void parse_qp_opt(const char *name, const char *val, NV_ENC_QP *qp_opt)
  24. {
  25. /* QP options can be passed in either as a single value to apply to all
  26. * or as three values separated by ":". */
  27. int32_t p, b, i;
  28. if (sscanf(val, "%d:%d:%d", &p, &b, &i) != 3) {
  29. p = b = i = atoi(val);
  30. }
  31. blog(LOG_DEBUG, "[obs-nvenc] Applying custom %s = %d / %d / %d (P / B / I)", name, p, b, i);
  32. /* Values should be treated as int32_t but are passed in as uint32_t
  33. * for legacy reasons, see comment in nvEncodeAPI.h */
  34. qp_opt->qpInterP = (uint32_t)p;
  35. qp_opt->qpInterB = (uint32_t)b;
  36. qp_opt->qpIntra = (uint32_t)i;
  37. }
  38. #define APPLY_QP_OPT(opt_name) \
  39. if (strcmp(opt->name, #opt_name) == 0) { \
  40. parse_qp_opt(#opt_name, opt->value, &nv_conf->opt_name); \
  41. return true; \
  42. }
  43. static bool apply_rc_opt(const struct obs_option *opt, NV_ENC_RC_PARAMS *nv_conf)
  44. {
  45. APPLY_QP_OPT(constQP)
  46. APPLY_QP_OPT(minQP)
  47. APPLY_QP_OPT(maxQP)
  48. APPLY_QP_OPT(initialRCQP)
  49. APPLY_INT_OPT(averageBitRate, uint32_t, PRIu32)
  50. APPLY_INT_OPT(maxBitRate, uint32_t, PRIu32)
  51. APPLY_INT_OPT(vbvBufferSize, uint32_t, PRIu32)
  52. APPLY_INT_OPT(vbvInitialDelay, uint32_t, PRIu32)
  53. APPLY_INT_OPT(targetQuality, uint8_t, PRIu8)
  54. APPLY_INT_OPT(targetQualityLSB, uint8_t, PRIu8)
  55. APPLY_INT_OPT(cbQPIndexOffset, int8_t, PRIi8)
  56. APPLY_INT_OPT(crQPIndexOffset, int8_t, PRIi8)
  57. APPLY_BIT_OPT(enableMinQP, 1)
  58. APPLY_BIT_OPT(enableMaxQP, 1)
  59. APPLY_BIT_OPT(enableInitialRCQP, 1)
  60. APPLY_BIT_OPT(enableAQ, 1)
  61. APPLY_BIT_OPT(enableLookahead, 1)
  62. APPLY_BIT_OPT(disableIadapt, 1)
  63. APPLY_BIT_OPT(disableBadapt, 1)
  64. APPLY_BIT_OPT(enableTemporalAQ, 1)
  65. APPLY_BIT_OPT(aqStrength, 4)
  66. #ifdef NVENC_12_2_OR_LATER
  67. APPLY_INT_OPT(lookaheadLevel, NV_ENC_LOOKAHEAD_LEVEL, PRIu32)
  68. #endif
  69. /* Macros above will return true if succesfully evaluated.
  70. * Otherwise, return false if option unknown/unsupported. */
  71. return false;
  72. }
  73. static bool apply_conf_opt(const struct obs_option *opt, NV_ENC_CONFIG *nv_conf)
  74. {
  75. APPLY_INT_OPT(gopLength, uint32_t, PRIu32)
  76. APPLY_INT_OPT(frameIntervalP, int32_t, PRIi32)
  77. return false;
  78. }
  79. static void parse_level_opt(const char *val, uint32_t *level, bool hevc)
  80. {
  81. /* Support for passing level both as raw value (e.g. "42")
  82. * and human-readable format (e.g. "4.2"). */
  83. uint32_t int_val = 0;
  84. if (strstr(val, ".") != NULL) {
  85. uint32_t high_val, low_val;
  86. if (sscanf(val, "%u.%u", &high_val, &low_val) == 2) {
  87. int_val = high_val * 10 + low_val;
  88. }
  89. } else {
  90. int_val = strtol(val, NULL, 10);
  91. }
  92. if (!int_val)
  93. return;
  94. if (hevc)
  95. int_val *= 3;
  96. blog(LOG_DEBUG, "[obs-nvenc] Applying custom level = %s (%u)", val, int_val);
  97. *level = int_val;
  98. }
  99. static bool apply_h264_opt(struct obs_option *opt, NV_ENC_CONFIG_H264 *nv_conf)
  100. {
  101. if (strcmp(opt->name, "level") == 0) {
  102. parse_level_opt(opt->value, &nv_conf->level, false);
  103. return true;
  104. }
  105. APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
  106. APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
  107. #ifdef NVENC_13_0_OR_LATER
  108. APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
  109. #endif
  110. APPLY_BIT_OPT(enableFillerDataInsertion, 1)
  111. return false;
  112. }
  113. static bool apply_hevc_opt(struct obs_option *opt, NV_ENC_CONFIG_HEVC *nv_conf)
  114. {
  115. if (strcmp(opt->name, "level") == 0) {
  116. parse_level_opt(opt->value, &nv_conf->level, true);
  117. return true;
  118. }
  119. APPLY_INT_OPT(tier, uint32_t, PRIu32)
  120. APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
  121. APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
  122. #ifdef NVENC_12_2_OR_LATER
  123. APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
  124. #endif
  125. APPLY_BIT_OPT(enableFillerDataInsertion, 1)
  126. return false;
  127. }
  128. static bool apply_av1_opt(struct obs_option *opt, NV_ENC_CONFIG_AV1 *nv_conf)
  129. {
  130. APPLY_INT_OPT(level, uint32_t, PRIu32)
  131. APPLY_INT_OPT(tier, uint32_t, PRIu32)
  132. APPLY_INT_OPT(numTileColumns, uint32_t, PRIu32)
  133. APPLY_INT_OPT(numTileRows, uint32_t, PRIu32)
  134. APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
  135. APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
  136. #ifdef NVENC_13_0_OR_LATER
  137. APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
  138. #endif
  139. APPLY_BIT_OPT(enableBitstreamPadding, 1)
  140. return false;
  141. }
  142. static bool apply_codec_opt(enum codec_type codec, struct obs_option *opt, NV_ENC_CODEC_CONFIG *enc_config)
  143. {
  144. if (codec == CODEC_H264)
  145. return apply_h264_opt(opt, &enc_config->h264Config);
  146. if (codec == CODEC_HEVC)
  147. return apply_hevc_opt(opt, &enc_config->hevcConfig);
  148. if (codec == CODEC_AV1)
  149. return apply_av1_opt(opt, &enc_config->av1Config);
  150. return false;
  151. }
  152. bool apply_user_args(struct nvenc_data *enc)
  153. {
  154. bool success = true;
  155. for (size_t idx = 0; idx < enc->props.opts.count; idx++) {
  156. struct obs_option *opt = &enc->props.opts.options[idx];
  157. /* Special options handled elsewhere */
  158. if (strcmp(opt->name, "lookaheadDepth") == 0 || strcmp(opt->name, "keyint") == 0)
  159. continue;
  160. if (apply_rc_opt(opt, &enc->config.rcParams))
  161. continue;
  162. if (apply_conf_opt(opt, &enc->config))
  163. continue;
  164. if (apply_codec_opt(enc->codec, opt, &enc->config.encodeCodecConfig))
  165. continue;
  166. warn("Unknown custom option: \"%s\"", opt->name);
  167. success = false;
  168. }
  169. return success;
  170. }
  171. bool get_user_arg_int(struct nvenc_data *enc, const char *name, int *val)
  172. {
  173. for (size_t idx = 0; idx < enc->props.opts.count; idx++) {
  174. struct obs_option *opt = &enc->props.opts.options[idx];
  175. if (strcmp(opt->name, name) != 0)
  176. continue;
  177. *val = strtol(opt->value, NULL, 10);
  178. return true;
  179. }
  180. return false;
  181. }