async-delay-filter.c 6.8 KB

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