async-delay-filter.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #include <obs-module.h>
  2. #include <util/deque.h>
  3. #include <util/util_uint64.h>
  4. /* NOTE: Delaying audio shouldn't be necessary because the audio subsystem will
  5. * automatically sync audio to video frames */
  6. /* #define DELAY_AUDIO */
  7. #ifndef SEC_TO_NSEC
  8. #define SEC_TO_NSEC 1000000000ULL
  9. #endif
  10. #ifndef MSEC_TO_NSEC
  11. #define MSEC_TO_NSEC 1000000ULL
  12. #endif
  13. #define SETTING_DELAY_MS "delay_ms"
  14. #define TEXT_DELAY_MS obs_module_text("DelayMs")
  15. struct async_delay_data {
  16. obs_source_t *context;
  17. /* contains struct obs_source_frame* */
  18. struct deque video_frames;
  19. #ifdef DELAY_AUDIO
  20. /* stores the audio data */
  21. struct deque audio_frames;
  22. struct obs_audio_data audio_output;
  23. #endif
  24. uint64_t last_video_ts;
  25. uint64_t last_audio_ts;
  26. uint64_t interval;
  27. uint64_t samplerate;
  28. bool video_delay_reached;
  29. bool audio_delay_reached;
  30. bool reset_video;
  31. bool reset_audio;
  32. };
  33. static const char *async_delay_filter_name(void *unused)
  34. {
  35. UNUSED_PARAMETER(unused);
  36. return obs_module_text("AsyncDelayFilter");
  37. }
  38. static void free_video_data(struct async_delay_data *filter,
  39. obs_source_t *parent)
  40. {
  41. while (filter->video_frames.size) {
  42. struct obs_source_frame *frame;
  43. deque_pop_front(&filter->video_frames, &frame,
  44. sizeof(struct obs_source_frame *));
  45. obs_source_release_frame(parent, frame);
  46. }
  47. }
  48. #ifdef DELAY_AUDIO
  49. static inline void free_audio_packet(struct obs_audio_data *audio)
  50. {
  51. for (size_t i = 0; i < MAX_AV_PLANES; i++)
  52. bfree(audio->data[i]);
  53. memset(audio, 0, sizeof(*audio));
  54. }
  55. static void free_audio_data(struct async_delay_data *filter)
  56. {
  57. while (filter->audio_frames.size) {
  58. struct obs_audio_data audio;
  59. deque_pop_front(&filter->audio_frames, &audio,
  60. sizeof(struct obs_audio_data));
  61. free_audio_packet(&audio);
  62. }
  63. }
  64. #endif
  65. static void async_delay_filter_update(void *data, obs_data_t *settings)
  66. {
  67. struct async_delay_data *filter = data;
  68. uint64_t new_interval =
  69. (uint64_t)obs_data_get_int(settings, SETTING_DELAY_MS) *
  70. MSEC_TO_NSEC;
  71. if (new_interval < filter->interval)
  72. free_video_data(filter, obs_filter_get_parent(filter->context));
  73. filter->reset_audio = true;
  74. filter->reset_video = true;
  75. filter->interval = new_interval;
  76. filter->video_delay_reached = false;
  77. filter->audio_delay_reached = false;
  78. }
  79. static void *async_delay_filter_create(obs_data_t *settings,
  80. obs_source_t *context)
  81. {
  82. struct async_delay_data *filter = bzalloc(sizeof(*filter));
  83. struct obs_audio_info oai;
  84. filter->context = context;
  85. async_delay_filter_update(filter, settings);
  86. obs_get_audio_info(&oai);
  87. filter->samplerate = oai.samples_per_sec;
  88. return filter;
  89. }
  90. static void async_delay_filter_destroy(void *data)
  91. {
  92. struct async_delay_data *filter = data;
  93. deque_free(&filter->video_frames);
  94. #ifdef DELAY_AUDIO
  95. free_audio_packet(&filter->audio_output);
  96. deque_free(&filter->audio_frames);
  97. #endif
  98. bfree(data);
  99. }
  100. static obs_properties_t *async_delay_filter_properties(void *data)
  101. {
  102. obs_properties_t *props = obs_properties_create();
  103. obs_property_t *p = obs_properties_add_int(props, SETTING_DELAY_MS,
  104. TEXT_DELAY_MS, 0, 20000, 1);
  105. obs_property_int_set_suffix(p, " ms");
  106. UNUSED_PARAMETER(data);
  107. return props;
  108. }
  109. static void async_delay_filter_remove(void *data, obs_source_t *parent)
  110. {
  111. struct async_delay_data *filter = data;
  112. free_video_data(filter, parent);
  113. #ifdef DELAY_AUDIO
  114. free_audio_data(filter);
  115. #endif
  116. }
  117. /* due to the fact that we need timing information to be consistent in order to
  118. * measure the current interval of data, if there is an unexpected hiccup or
  119. * jump with the timestamps, reset the cached delay data and start again to
  120. * ensure that the timing is consistent */
  121. static inline bool is_timestamp_jump(uint64_t ts, uint64_t prev_ts)
  122. {
  123. return ts < prev_ts || (ts - prev_ts) > SEC_TO_NSEC;
  124. }
  125. static struct obs_source_frame *
  126. async_delay_filter_video(void *data, struct obs_source_frame *frame)
  127. {
  128. struct async_delay_data *filter = data;
  129. obs_source_t *parent = obs_filter_get_parent(filter->context);
  130. struct obs_source_frame *output;
  131. uint64_t cur_interval;
  132. if (filter->reset_video ||
  133. is_timestamp_jump(frame->timestamp, filter->last_video_ts)) {
  134. free_video_data(filter, parent);
  135. filter->video_delay_reached = false;
  136. filter->reset_video = false;
  137. }
  138. filter->last_video_ts = frame->timestamp;
  139. deque_push_back(&filter->video_frames, &frame,
  140. sizeof(struct obs_source_frame *));
  141. deque_peek_front(&filter->video_frames, &output,
  142. sizeof(struct obs_source_frame *));
  143. cur_interval = frame->timestamp - output->timestamp;
  144. if (!filter->video_delay_reached && cur_interval < filter->interval)
  145. return NULL;
  146. deque_pop_front(&filter->video_frames, NULL,
  147. sizeof(struct obs_source_frame *));
  148. if (!filter->video_delay_reached)
  149. filter->video_delay_reached = true;
  150. return output;
  151. }
  152. #ifdef DELAY_AUDIO
  153. static struct obs_audio_data *
  154. async_delay_filter_audio(void *data, struct obs_audio_data *audio)
  155. {
  156. struct async_delay_data *filter = data;
  157. struct obs_audio_data cached = *audio;
  158. uint64_t cur_interval;
  159. uint64_t duration;
  160. uint64_t end_ts;
  161. if (filter->reset_audio ||
  162. is_timestamp_jump(audio->timestamp, filter->last_audio_ts)) {
  163. free_audio_data(filter);
  164. filter->audio_delay_reached = false;
  165. filter->reset_audio = false;
  166. }
  167. filter->last_audio_ts = audio->timestamp;
  168. duration =
  169. util_mul_div64(audio->frames, SEC_TO_NSEC, filter->samplerate);
  170. end_ts = audio->timestamp + duration;
  171. for (size_t i = 0; i < MAX_AV_PLANES; i++) {
  172. if (!audio->data[i])
  173. break;
  174. cached.data[i] =
  175. bmemdup(audio->data[i], audio->frames * sizeof(float));
  176. }
  177. free_audio_packet(&filter->audio_output);
  178. deque_push_back(&filter->audio_frames, &cached, sizeof(cached));
  179. deque_peek_front(&filter->audio_frames, &cached, sizeof(cached));
  180. cur_interval = end_ts - cached.timestamp;
  181. if (!filter->audio_delay_reached && cur_interval < filter->interval)
  182. return NULL;
  183. deque_pop_front(&filter->audio_frames, NULL, sizeof(cached));
  184. memcpy(&filter->audio_output, &cached, sizeof(cached));
  185. if (!filter->audio_delay_reached)
  186. filter->audio_delay_reached = true;
  187. return &filter->audio_output;
  188. }
  189. #endif
  190. struct obs_source_info async_delay_filter = {
  191. .id = "async_delay_filter",
  192. .type = OBS_SOURCE_TYPE_FILTER,
  193. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC,
  194. .get_name = async_delay_filter_name,
  195. .create = async_delay_filter_create,
  196. .destroy = async_delay_filter_destroy,
  197. .update = async_delay_filter_update,
  198. .get_properties = async_delay_filter_properties,
  199. .filter_video = async_delay_filter_video,
  200. #ifdef DELAY_AUDIO
  201. .filter_audio = async_delay_filter_audio,
  202. #endif
  203. .filter_remove = async_delay_filter_remove,
  204. };