streamcache.lua 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. -- streamcache.lua
  2. -- Configurable parameters:
  3. --
  4. -- speed will slowly be decreased towards streamcache_min_speed
  5. -- as long as there is less data cached than required to bridge
  6. -- "streamcache_cache_seconds_low" seconds of stalled transmission.
  7. --
  8. -- speed will slowly be increased towards 1.0 while there is more data
  9. -- cached than required to bridge "streamcache_cache_seconds_high"
  10. -- seconds of stalled transmission.
  11. --
  12. -- To keep replay speed adjustments below your attention threshold,
  13. -- the replay speed factor will never be set below "streamcache_min_speed",
  14. -- and this script will not change this factor by more than a factor of
  15. -- "streamcache_adjust_factor" per second.
  16. streamcache_cache_seconds_low = 75
  17. streamcache_cache_seconds_high = 90
  18. streamcache_min_speed = 0.98
  19. streamcache_adjust_factor = 1.0005
  20. streamcache_verbose = false
  21. -- Changing replay speed by < 2% seems to cause less
  22. -- distortion when not correcting audio pitch (via the scaletempo filter).
  23. -- But you can try setting this to "yes" if you like -
  24. -- might be useful if you want to toy around with very low min_speed values.
  25. mp.set_property("options/audio-pitch-correction", "no")
  26. -- Notice that using this script with non-live streams, like podcasts,
  27. -- does usually not improve buffering, as the server will usually
  28. -- send pre-buffering data individually to clients, anyway. But you can
  29. -- still use this script for podcasts where the server sends less than
  30. -- your desired amount of pre-buffering data.
  31. -- Anything below this line is not meant for configuration.
  32. streamcache_cache_low = 100
  33. streamcache_cache_high = 750
  34. function streamcache_log(level, msg)
  35. if (streamcache_verbose) then
  36. mp.msg.log(level, msg)
  37. end
  38. end
  39. function streamcache_compute_cache_sizes()
  40. local vb = mp.get_property_native("video-bitrate")
  41. if (vb == nil) then
  42. vb = 0
  43. end
  44. local ab = mp.get_property_native("audio-bitrate")
  45. if (ab == nil) then
  46. ab = 0
  47. end
  48. local br = vb + ab
  49. if (br == 0) then
  50. -- when we do not have plausible information on the bitrate, we
  51. -- first make some midly pessimistic guess of 2 MBit/s
  52. br = 2000000
  53. end
  54. -- compute the cache size required per second in kb
  55. local kb_per_sec = br / (8 * 1024)
  56. streamcache_log("info", "ab=" .. ab .. " vb=" .. vb .. " br=" .. br .. " kb_per_sec=" .. kb_per_sec)
  57. streamcache_cache_low = streamcache_cache_seconds_low * kb_per_sec
  58. streamcache_cache_high = streamcache_cache_seconds_high * kb_per_sec
  59. if (streamcache_verbose or ab > 0 or vb > 0) then
  60. mp.msg.log("info", "assuming bitrate=" .. br/1024 .. "kbit/s, thresholds: cache_low=" .. streamcache_cache_low .. "kb cache_high=" .. streamcache_cache_high .. "kb")
  61. end
  62. if (ab > 0 or vb > 0) then
  63. -- possibly increase cache-size property only if at least some
  64. -- information on audio or video bitrate exists
  65. local new_cache_size = streamcache_cache_high * 2
  66. local cs = mp.get_property_native("cache-size")
  67. if (cs == nil) then
  68. cs = 0
  69. end
  70. if (new_cache_size > cs) then
  71. -- we do not shrink the cache, only expand it when necessary
  72. streamcache_log("info", "increased the cache-size to " .. new_cache_size .. "kb")
  73. mp.set_property("cache-size", new_cache_size)
  74. end
  75. end
  76. end
  77. mp.observe_property("audio-bitrate", "native", streamcache_compute_cache_sizes)
  78. mp.observe_property("video-bitrate", "native", streamcache_compute_cache_sizes)
  79. function streamcache_check_fill()
  80. local cache_used = mp.get_property_native("cache-used")
  81. if cache_used == nil then
  82. cache_used = 0
  83. end
  84. local current_speed = mp.get_property_native("speed")
  85. if current_speed == nil then
  86. current_speed = streamcache_min_speed
  87. end
  88. if (cache_used < streamcache_cache_low) then
  89. -- not enough in cache - so slowly deccelerate
  90. local new_speed = current_speed * 0.9995
  91. if (new_speed < streamcache_min_speed) then
  92. new_speed = streamcache_min_speed
  93. end
  94. streamcache_log("info", "cache_used=" .. cache_used .. " (< low), new_speed=" .. new_speed)
  95. mp.set_property("speed", new_speed)
  96. return
  97. end
  98. if (cache_used > streamcache_cache_high) then
  99. if (current_speed >= 1.0) then
  100. -- all fine, nothing to do
  101. streamcache_log("info", "cache_used=" .. cache_used .. " (> high) current_speed >= 1.0 - do nothing")
  102. return
  103. end
  104. -- current speed is < 1.0, but there's really enough in the cache, so slowly accelerate
  105. local new_speed = current_speed * 1.0005
  106. if (1.0 - new_speed < 0.0001) then
  107. new_speed = 1.0
  108. end
  109. streamcache_log("info", "cache_used=" .. cache_used .. " (> high), new_speed=" .. new_speed)
  110. mp.set_property("speed", new_speed)
  111. return
  112. end
  113. -- cache_used is between low and high - don't change speed
  114. streamcache_log("info", "cache_used=" .. cache_used .. " (> low < high) no speed change")
  115. end
  116. streamcache_timer = mp.add_periodic_timer(1.0, streamcache_check_fill)
  117. function streamcache_on_loaded()
  118. mp.set_property("speed", streamcache_min_speed)
  119. streamcache_compute_cache_sizes()
  120. end
  121. mp.register_event("file-loaded", streamcache_on_loaded)