react-native-alpha-video 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +908 -2
  3. package/THIRD_PARTY_NOTICES.md +30 -0
  4. package/android/av1/build.gradle +67 -0
  5. package/android/av1/consumer-rules.pro +4 -0
  6. package/android/av1/src/main/cpp/Av1Codec_jni.cpp +8 -0
  7. package/android/av1/src/main/cpp/CMakeLists.txt +34 -0
  8. package/android/av1/src/main/java/dev/mediacache/alphavideo/av1/Av1Codec.java +21 -0
  9. package/android/build.gradle +90 -0
  10. package/android/consumer-rules.pro +2 -0
  11. package/android/gradle.properties +1 -0
  12. package/android/publish.gradle +82 -0
  13. package/android/settings.gradle +5 -0
  14. package/android/src/main/AndroidManifest.xml +1 -0
  15. package/android/src/main/cpp/CMakeLists.txt +59 -0
  16. package/android/src/main/java/dev/mediacache/alphavideo/AlphaVideoCodecs.java +73 -0
  17. package/android/src/main/java/dev/mediacache/alphavideo/AlphaVideoView.java +1982 -0
  18. package/android/src/main/java/dev/mediacache/alphavideo/BufferedDualStreamPlayer.java +978 -0
  19. package/android/src/main/java/dev/mediacache/alphavideo/Fetcher.java +37 -0
  20. package/android/src/main/java/dev/mediacache/alphavideo/HttpFetcher.java +156 -0
  21. package/android/src/main/java/dev/mediacache/alphavideo/MediaCodecColor.java +47 -0
  22. package/android/src/main/java/dev/mediacache/alphavideo/MediaCodecDecoders.java +135 -0
  23. package/android/src/main/java/dev/mediacache/alphavideo/RangeSourceLink.java +54 -0
  24. package/android/src/main/java/dev/mediacache/alphavideo/SourceReader.java +174 -0
  25. package/android/src/main/java/dev/mediacache/alphavideo/SurfaceTextureVideoDecoder.java +802 -0
  26. package/android/src/main/java/dev/mediacache/alphavideo/WebMAlphaPlayerNative.java +95 -0
  27. package/android/src/main/java/dev/mediacache/alphavideo/WebMFrame.java +54 -0
  28. package/android/src/main/java/dev/mediacache/alphavideo/WebMSource.java +160 -0
  29. package/android/src/rangesource/java/dev/mediacache/alphavideo/AppDataSource.java +50 -0
  30. package/android/vpx/build.gradle +67 -0
  31. package/android/vpx/consumer-rules.pro +4 -0
  32. package/android/vpx/src/main/cpp/CMakeLists.txt +34 -0
  33. package/android/vpx/src/main/cpp/VpxCodec_jni.cpp +8 -0
  34. package/android/vpx/src/main/java/dev/mediacache/alphavideo/vpx/VpxCodec.java +21 -0
  35. package/codecs/av1/Dav1dDecoderPlugin.cpp +157 -0
  36. package/codecs/av1/Dav1dDecoderPlugin.h +17 -0
  37. package/codecs/vpx/VpxDecoderPlugin.cpp +103 -0
  38. package/codecs/vpx/VpxDecoderPlugin.h +17 -0
  39. package/cpp/AlphaDemuxer.cpp +984 -0
  40. package/cpp/AlphaDemuxer.h +123 -0
  41. package/cpp/AlphaVideoDecoderPlugin.h +84 -0
  42. package/cpp/Av1SequenceHeader.cpp +175 -0
  43. package/cpp/Av1SequenceHeader.h +25 -0
  44. package/cpp/ByteSource.cpp +116 -0
  45. package/cpp/ByteSource.h +59 -0
  46. package/cpp/RangeSourceClient.h +40 -0
  47. package/cpp/VideoDecoder.cpp +157 -0
  48. package/cpp/VideoDecoder.h +83 -0
  49. package/cpp/WebMAlphaPlayer.cpp +1666 -0
  50. package/cpp/WebMAlphaPlayer.h +389 -0
  51. package/cpp/WebMAlphaPlayer_jni.cpp +1380 -0
  52. package/cpp/YuvToRgb.cpp +39 -0
  53. package/cpp/YuvToRgb.h +40 -0
  54. package/ios/Sources/AlphaVideo/AlphaVideoCodecs.mm +63 -0
  55. package/ios/Sources/AlphaVideo/AlphaVideoInternal.h +70 -0
  56. package/ios/Sources/AlphaVideo/AlphaVideoPlayer.mm +994 -0
  57. package/ios/Sources/AlphaVideo/AlphaVideoSource.mm +476 -0
  58. package/ios/Sources/AlphaVideo/AlphaVideoView.mm +374 -0
  59. package/ios/Sources/AlphaVideo/include/AlphaVideo.h +303 -0
  60. package/ios/Sources/AlphaVideoAV1/AlphaVideoAV1Codec.mm +16 -0
  61. package/ios/Sources/AlphaVideoAV1/include/AlphaVideoAV1.h +18 -0
  62. package/ios/Sources/AlphaVideoVPX/AlphaVideoVPXCodec.mm +16 -0
  63. package/ios/Sources/AlphaVideoVPX/include/AlphaVideoVPX.h +17 -0
  64. package/lib/commonjs/AlphaVideoView.js +187 -0
  65. package/lib/commonjs/AlphaVideoView.js.map +1 -0
  66. package/lib/commonjs/AlphaVideoViewNativeComponent.js +47 -0
  67. package/lib/commonjs/AlphaVideoViewNativeComponent.js.map +1 -0
  68. package/lib/commonjs/AlphaVideoViewNativeComponent.web.js +11 -0
  69. package/lib/commonjs/AlphaVideoViewNativeComponent.web.js.map +1 -0
  70. package/lib/commonjs/NativeAlphaVideoModule.js +10 -0
  71. package/lib/commonjs/NativeAlphaVideoModule.js.map +1 -0
  72. package/lib/commonjs/NativeAlphaVideoModule.web.js +9 -0
  73. package/lib/commonjs/NativeAlphaVideoModule.web.js.map +1 -0
  74. package/lib/commonjs/WebPlayer.js +12 -0
  75. package/lib/commonjs/WebPlayer.js.map +1 -0
  76. package/lib/commonjs/WebPlayer.web.js +115 -0
  77. package/lib/commonjs/WebPlayer.web.js.map +1 -0
  78. package/lib/commonjs/index.js +124 -0
  79. package/lib/commonjs/index.js.map +1 -0
  80. package/lib/module/AlphaVideoView.js +187 -0
  81. package/lib/module/AlphaVideoView.js.map +1 -0
  82. package/lib/module/AlphaVideoViewNativeComponent.js +47 -0
  83. package/lib/module/AlphaVideoViewNativeComponent.js.map +1 -0
  84. package/lib/module/AlphaVideoViewNativeComponent.web.js +11 -0
  85. package/lib/module/AlphaVideoViewNativeComponent.web.js.map +1 -0
  86. package/lib/module/NativeAlphaVideoModule.js +10 -0
  87. package/lib/module/NativeAlphaVideoModule.js.map +1 -0
  88. package/lib/module/NativeAlphaVideoModule.web.js +9 -0
  89. package/lib/module/NativeAlphaVideoModule.web.js.map +1 -0
  90. package/lib/module/WebPlayer.js +12 -0
  91. package/lib/module/WebPlayer.js.map +1 -0
  92. package/lib/module/WebPlayer.web.js +115 -0
  93. package/lib/module/WebPlayer.web.js.map +1 -0
  94. package/lib/module/index.js +124 -0
  95. package/lib/module/index.js.map +1 -0
  96. package/lib/typescript/react-native/src/AlphaVideoView.d.ts +189 -0
  97. package/lib/typescript/react-native/src/AlphaVideoView.d.ts.map +1 -0
  98. package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.d.ts +109 -0
  99. package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.d.ts.map +1 -0
  100. package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.web.d.ts +5 -0
  101. package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.web.d.ts.map +1 -0
  102. package/lib/typescript/react-native/src/NativeAlphaVideoModule.d.ts +21 -0
  103. package/lib/typescript/react-native/src/NativeAlphaVideoModule.d.ts.map +1 -0
  104. package/lib/typescript/react-native/src/NativeAlphaVideoModule.web.d.ts +3 -0
  105. package/lib/typescript/react-native/src/NativeAlphaVideoModule.web.d.ts.map +1 -0
  106. package/lib/typescript/react-native/src/WebPlayer.d.ts +3 -0
  107. package/lib/typescript/react-native/src/WebPlayer.d.ts.map +1 -0
  108. package/lib/typescript/react-native/src/WebPlayer.web.d.ts +9 -0
  109. package/lib/typescript/react-native/src/WebPlayer.web.d.ts.map +1 -0
  110. package/lib/typescript/react-native/src/index.d.ts +77 -0
  111. package/lib/typescript/react-native/src/index.d.ts.map +1 -0
  112. package/licenses/dav1d-COPYING.txt +23 -0
  113. package/licenses/libvpx-LICENSE.txt +31 -0
  114. package/licenses/libvpx-PATENTS.txt +23 -0
  115. package/licenses/libwebm-LICENSE.txt +30 -0
  116. package/licenses/libwebm-PATENTS.txt +23 -0
  117. package/package.json +152 -4
  118. package/prebuilt/android/arm64-v8a/lib/libdav1d.a +0 -0
  119. package/prebuilt/android/arm64-v8a/lib/libvpx.a +0 -0
  120. package/prebuilt/android/armeabi-v7a/lib/libdav1d.a +0 -0
  121. package/prebuilt/android/armeabi-v7a/lib/libvpx.a +0 -0
  122. package/prebuilt/android/x86_64/lib/libdav1d.a +0 -0
  123. package/prebuilt/android/x86_64/lib/libvpx.a +0 -0
  124. package/prebuilt/include/dav1d/common.h +94 -0
  125. package/prebuilt/include/dav1d/data.h +117 -0
  126. package/prebuilt/include/dav1d/dav1d.h +329 -0
  127. package/prebuilt/include/dav1d/headers.h +440 -0
  128. package/prebuilt/include/dav1d/picture.h +157 -0
  129. package/prebuilt/include/dav1d/version.h +50 -0
  130. package/prebuilt/include/vpx/vp8.h +136 -0
  131. package/prebuilt/include/vpx/vp8dx.h +228 -0
  132. package/prebuilt/include/vpx/vpx_codec.h +475 -0
  133. package/prebuilt/include/vpx/vpx_decoder.h +365 -0
  134. package/prebuilt/include/vpx/vpx_frame_buffer.h +83 -0
  135. package/prebuilt/include/vpx/vpx_image.h +209 -0
  136. package/prebuilt/include/vpx/vpx_integer.h +40 -0
  137. package/prebuilt/ios/lib/libdav1d.a +0 -0
  138. package/prebuilt/ios/lib/libvpx.a +0 -0
  139. package/prebuilt/ios-simulator/include/dav1d/common.h +94 -0
  140. package/prebuilt/ios-simulator/include/dav1d/data.h +117 -0
  141. package/prebuilt/ios-simulator/include/dav1d/dav1d.h +329 -0
  142. package/prebuilt/ios-simulator/include/dav1d/headers.h +440 -0
  143. package/prebuilt/ios-simulator/include/dav1d/picture.h +157 -0
  144. package/prebuilt/ios-simulator/include/dav1d/version.h +50 -0
  145. package/prebuilt/ios-simulator/include/vpx/vp8.h +136 -0
  146. package/prebuilt/ios-simulator/include/vpx/vp8dx.h +228 -0
  147. package/prebuilt/ios-simulator/include/vpx/vpx_codec.h +475 -0
  148. package/prebuilt/ios-simulator/include/vpx/vpx_decoder.h +365 -0
  149. package/prebuilt/ios-simulator/include/vpx/vpx_frame_buffer.h +83 -0
  150. package/prebuilt/ios-simulator/include/vpx/vpx_image.h +209 -0
  151. package/prebuilt/ios-simulator/include/vpx/vpx_integer.h +40 -0
  152. package/prebuilt/ios-simulator/lib/libdav1d.a +0 -0
  153. package/prebuilt/ios-simulator/lib/libvpx.a +0 -0
  154. package/react-native/android/CMakeLists.txt +14 -0
  155. package/react-native/android/build.gradle +113 -0
  156. package/react-native/android/src/main/AndroidManifest.xml +1 -0
  157. package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModule.java +102 -0
  158. package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoPackage.java +50 -0
  159. package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManager.java +317 -0
  160. package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModuleSpec.java +10 -0
  161. package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManagerSpec.java +23 -0
  162. package/react-native/android/src/norangesource/java/dev/mediacache/alphavideo/AppDataSource.java +15 -0
  163. package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModuleSpec.java +30 -0
  164. package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManagerSpec.java +61 -0
  165. package/react-native/ios/RNAlphaVideoViewComponentView.mm +173 -0
  166. package/react-native/ios/RNAlphaVideoViewManager.mm +147 -0
  167. package/react-native/src/AlphaVideoView.tsx +329 -0
  168. package/react-native/src/AlphaVideoViewNativeComponent.ts +132 -0
  169. package/react-native/src/AlphaVideoViewNativeComponent.web.ts +5 -0
  170. package/react-native/src/NativeAlphaVideoModule.ts +22 -0
  171. package/react-native/src/NativeAlphaVideoModule.web.ts +2 -0
  172. package/react-native/src/WebPlayer.tsx +7 -0
  173. package/react-native/src/WebPlayer.web.tsx +87 -0
  174. package/react-native/src/index.tsx +144 -0
  175. package/react-native-alpha-video.podspec +99 -0
  176. package/react-native.config.js +12 -0
  177. package/scripts/build_codecs_android.sh +246 -0
  178. package/scripts/build_codecs_ios.sh +31 -0
  179. package/scripts/build_codecs_ios_device.sh +210 -0
  180. package/scripts/build_codecs_ios_simulator.sh +256 -0
  181. package/scripts/build_wasm.sh +230 -0
  182. package/scripts/build_xcframework.sh +49 -0
  183. package/scripts/fetch-prebuilt.sh +48 -0
  184. package/scripts/package_release.sh +107 -0
  185. package/scripts/publish-android.sh +85 -0
  186. package/scripts/vendor_range_source.sh +47 -0
  187. package/third_party/libwebm/AUTHORS.TXT +5 -0
  188. package/third_party/libwebm/LICENSE.TXT +30 -0
  189. package/third_party/libwebm/PATENTS.TXT +23 -0
  190. package/third_party/libwebm/README.md +7 -0
  191. package/third_party/libwebm/webm_parser/include/webm/buffer_reader.h +138 -0
  192. package/third_party/libwebm/webm_parser/include/webm/callback.h +363 -0
  193. package/third_party/libwebm/webm_parser/include/webm/dom_types.h +1781 -0
  194. package/third_party/libwebm/webm_parser/include/webm/element.h +208 -0
  195. package/third_party/libwebm/webm_parser/include/webm/file_reader.h +103 -0
  196. package/third_party/libwebm/webm_parser/include/webm/id.h +1085 -0
  197. package/third_party/libwebm/webm_parser/include/webm/istream_reader.h +99 -0
  198. package/third_party/libwebm/webm_parser/include/webm/reader.h +94 -0
  199. package/third_party/libwebm/webm_parser/include/webm/status.h +166 -0
  200. package/third_party/libwebm/webm_parser/include/webm/webm_parser.h +133 -0
  201. package/third_party/libwebm/webm_parser/src/ancestory.cc +334 -0
  202. package/third_party/libwebm/webm_parser/src/ancestory.h +83 -0
  203. package/third_party/libwebm/webm_parser/src/audio_parser.h +84 -0
  204. package/third_party/libwebm/webm_parser/src/bit_utils.cc +26 -0
  205. package/third_party/libwebm/webm_parser/src/bit_utils.h +24 -0
  206. package/third_party/libwebm/webm_parser/src/block_additions_parser.h +30 -0
  207. package/third_party/libwebm/webm_parser/src/block_group_parser.h +71 -0
  208. package/third_party/libwebm/webm_parser/src/block_header_parser.cc +76 -0
  209. package/third_party/libwebm/webm_parser/src/block_header_parser.h +65 -0
  210. package/third_party/libwebm/webm_parser/src/block_more_parser.h +32 -0
  211. package/third_party/libwebm/webm_parser/src/block_parser.cc +285 -0
  212. package/third_party/libwebm/webm_parser/src/block_parser.h +126 -0
  213. package/third_party/libwebm/webm_parser/src/bool_parser.h +95 -0
  214. package/third_party/libwebm/webm_parser/src/buffer_reader.cc +110 -0
  215. package/third_party/libwebm/webm_parser/src/byte_parser.h +144 -0
  216. package/third_party/libwebm/webm_parser/src/callback.cc +155 -0
  217. package/third_party/libwebm/webm_parser/src/chapter_atom_parser.h +42 -0
  218. package/third_party/libwebm/webm_parser/src/chapter_display_parser.h +34 -0
  219. package/third_party/libwebm/webm_parser/src/chapters_parser.h +28 -0
  220. package/third_party/libwebm/webm_parser/src/cluster_parser.h +48 -0
  221. package/third_party/libwebm/webm_parser/src/colour_parser.h +55 -0
  222. package/third_party/libwebm/webm_parser/src/content_enc_aes_settings_parser.h +32 -0
  223. package/third_party/libwebm/webm_parser/src/content_encoding_parser.h +38 -0
  224. package/third_party/libwebm/webm_parser/src/content_encodings_parser.h +30 -0
  225. package/third_party/libwebm/webm_parser/src/content_encryption_parser.h +37 -0
  226. package/third_party/libwebm/webm_parser/src/cue_point_parser.h +38 -0
  227. package/third_party/libwebm/webm_parser/src/cue_track_positions_parser.h +39 -0
  228. package/third_party/libwebm/webm_parser/src/cues_parser.h +27 -0
  229. package/third_party/libwebm/webm_parser/src/date_parser.cc +72 -0
  230. package/third_party/libwebm/webm_parser/src/date_parser.h +64 -0
  231. package/third_party/libwebm/webm_parser/src/ebml_parser.h +48 -0
  232. package/third_party/libwebm/webm_parser/src/edition_entry_parser.h +35 -0
  233. package/third_party/libwebm/webm_parser/src/element_parser.h +102 -0
  234. package/third_party/libwebm/webm_parser/src/file_reader.cc +142 -0
  235. package/third_party/libwebm/webm_parser/src/float_parser.cc +77 -0
  236. package/third_party/libwebm/webm_parser/src/float_parser.h +65 -0
  237. package/third_party/libwebm/webm_parser/src/id_element_parser.cc +49 -0
  238. package/third_party/libwebm/webm_parser/src/id_element_parser.h +56 -0
  239. package/third_party/libwebm/webm_parser/src/id_parser.cc +75 -0
  240. package/third_party/libwebm/webm_parser/src/id_parser.h +45 -0
  241. package/third_party/libwebm/webm_parser/src/info_parser.h +44 -0
  242. package/third_party/libwebm/webm_parser/src/int_parser.h +121 -0
  243. package/third_party/libwebm/webm_parser/src/istream_reader.cc +133 -0
  244. package/third_party/libwebm/webm_parser/src/master_parser.cc +298 -0
  245. package/third_party/libwebm/webm_parser/src/master_parser.h +230 -0
  246. package/third_party/libwebm/webm_parser/src/master_value_parser.h +533 -0
  247. package/third_party/libwebm/webm_parser/src/mastering_metadata_parser.h +57 -0
  248. package/third_party/libwebm/webm_parser/src/parser.h +34 -0
  249. package/third_party/libwebm/webm_parser/src/parser_utils.cc +34 -0
  250. package/third_party/libwebm/webm_parser/src/parser_utils.h +64 -0
  251. package/third_party/libwebm/webm_parser/src/projection_parser.h +40 -0
  252. package/third_party/libwebm/webm_parser/src/recursive_parser.h +93 -0
  253. package/third_party/libwebm/webm_parser/src/seek_head_parser.h +27 -0
  254. package/third_party/libwebm/webm_parser/src/seek_parser.h +37 -0
  255. package/third_party/libwebm/webm_parser/src/segment_parser.cc +86 -0
  256. package/third_party/libwebm/webm_parser/src/segment_parser.h +53 -0
  257. package/third_party/libwebm/webm_parser/src/simple_tag_parser.h +37 -0
  258. package/third_party/libwebm/webm_parser/src/size_parser.cc +56 -0
  259. package/third_party/libwebm/webm_parser/src/size_parser.h +43 -0
  260. package/third_party/libwebm/webm_parser/src/skip_callback.h +63 -0
  261. package/third_party/libwebm/webm_parser/src/skip_parser.cc +59 -0
  262. package/third_party/libwebm/webm_parser/src/skip_parser.h +35 -0
  263. package/third_party/libwebm/webm_parser/src/slices_parser.h +30 -0
  264. package/third_party/libwebm/webm_parser/src/tag_parser.h +37 -0
  265. package/third_party/libwebm/webm_parser/src/tags_parser.h +27 -0
  266. package/third_party/libwebm/webm_parser/src/targets_parser.h +35 -0
  267. package/third_party/libwebm/webm_parser/src/time_slice_parser.h +30 -0
  268. package/third_party/libwebm/webm_parser/src/track_entry_parser.h +65 -0
  269. package/third_party/libwebm/webm_parser/src/tracks_parser.h +27 -0
  270. package/third_party/libwebm/webm_parser/src/unknown_parser.cc +49 -0
  271. package/third_party/libwebm/webm_parser/src/unknown_parser.h +38 -0
  272. package/third_party/libwebm/webm_parser/src/var_int_parser.cc +71 -0
  273. package/third_party/libwebm/webm_parser/src/var_int_parser.h +56 -0
  274. package/third_party/libwebm/webm_parser/src/video_parser.h +122 -0
  275. package/third_party/libwebm/webm_parser/src/virtual_block_parser.cc +72 -0
  276. package/third_party/libwebm/webm_parser/src/virtual_block_parser.h +69 -0
  277. package/third_party/libwebm/webm_parser/src/void_parser.cc +49 -0
  278. package/third_party/libwebm/webm_parser/src/void_parser.h +41 -0
  279. package/third_party/libwebm/webm_parser/src/webm_parser.cc +282 -0
  280. package/third_party/range-source/LICENSE +21 -0
  281. package/third_party/range-source/README.md +8 -0
  282. package/third_party/range-source/include/range_source.h +395 -0
  283. package/third_party/range-source/include/range_source_download.h +309 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * AlphaVideoSource.mm
3
+ * Reading URLs and data sources: the built-in URLSession fetcher, and the reader it feeds, the
4
+ * range-source add-on's through its C table, or without the add-on the basic input's download.
5
+ * A data source is read by the add-on, which the player finds at runtime.
6
+ */
7
+
8
+ #import "AlphaVideoInternal.h"
9
+
10
+
11
+ #include <condition_variable>
12
+ #include <functional>
13
+ #include <mutex>
14
+ #include <string>
15
+
16
+ namespace {
17
+
18
+ /**
19
+ * Bytes handed from callbacks on any thread to the reader's fetch thread: a response, then data,
20
+ * then the end or an error. With flow control, the producer is paused while more than kHighBytes
21
+ * wait and resumed under kLowBytes.
22
+ */
23
+ class Pipe {
24
+ public:
25
+ static constexpr size_t kHighBytes = 1024 * 1024;
26
+ static constexpr size_t kLowBytes = 256 * 1024;
27
+
28
+ /** Called under the pipe's lock with true to pause the producer, false to resume it. */
29
+ void setFlowControl(std::function<void(bool)> pause) {
30
+ std::lock_guard<std::mutex> lock(mutex_);
31
+ pause_ = std::move(pause);
32
+ }
33
+
34
+ /** `skip`: bytes of the data to drop first, for a server that answered from 0. */
35
+ void respond(const webm::SourceInfo& info, int64_t skip = 0) {
36
+ std::lock_guard<std::mutex> lock(mutex_);
37
+ if (responded_ || done_) return;
38
+ responded_ = true;
39
+ info_ = info;
40
+ skip_ = skip;
41
+ changed_.notify_all();
42
+ }
43
+
44
+ void push(const uint8_t* data, size_t size) {
45
+ std::lock_guard<std::mutex> lock(mutex_);
46
+ if (done_ || !responded_) return;
47
+ if (skip_ > 0) {
48
+ size_t dropped = static_cast<size_t>(std::min<int64_t>(skip_, static_cast<int64_t>(size)));
49
+ skip_ -= static_cast<int64_t>(dropped);
50
+ data += dropped;
51
+ size -= dropped;
52
+ }
53
+ if (size == 0) return;
54
+ buffer_.append(reinterpret_cast<const char*>(data), size);
55
+ if (pause_ && !paused_ && bufferedLocked() > kHighBytes) {
56
+ paused_ = true;
57
+ pause_(true);
58
+ }
59
+ changed_.notify_all();
60
+ }
61
+
62
+ /** An empty error is the end of the data. Only the first call counts. */
63
+ void finish(const std::string& error) {
64
+ std::lock_guard<std::mutex> lock(mutex_);
65
+ if (done_) return;
66
+ done_ = true;
67
+ error_ = error;
68
+ changed_.notify_all();
69
+ }
70
+
71
+ void cancel() { finish("Cancelled"); }
72
+
73
+ bool waitResponse(webm::SourceInfo& info, std::string& error) {
74
+ std::unique_lock<std::mutex> lock(mutex_);
75
+ changed_.wait(lock, [&] { return responded_ || done_; });
76
+ if (!responded_) {
77
+ error = error_.empty() ? "The source ended without a response" : error_;
78
+ return false;
79
+ }
80
+ info = info_;
81
+ return true;
82
+ }
83
+
84
+ /** Bytes read, 0 at the end, or -1 with `error` set; the data before an error comes first. */
85
+ int64_t read(uint8_t* out, int64_t size, std::string& error) {
86
+ std::unique_lock<std::mutex> lock(mutex_);
87
+ changed_.wait(lock, [&] { return bufferedLocked() > 0 || done_; });
88
+ size_t available = bufferedLocked();
89
+ if (available > 0) {
90
+ size_t n = std::min(available, static_cast<size_t>(size));
91
+ memcpy(out, buffer_.data() + offset_, n);
92
+ offset_ += n;
93
+ if (offset_ == buffer_.size()) {
94
+ buffer_.clear();
95
+ offset_ = 0;
96
+ } else if (offset_ > kHighBytes) {
97
+ buffer_.erase(0, offset_);
98
+ offset_ = 0;
99
+ }
100
+ if (paused_ && bufferedLocked() < kLowBytes) {
101
+ paused_ = false;
102
+ pause_(false);
103
+ }
104
+ return static_cast<int64_t>(n);
105
+ }
106
+ if (!error_.empty()) {
107
+ error = error_;
108
+ return -1;
109
+ }
110
+ return 0;
111
+ }
112
+
113
+ private:
114
+ size_t bufferedLocked() const { return buffer_.size() - offset_; }
115
+
116
+ std::mutex mutex_;
117
+ std::condition_variable changed_;
118
+ std::function<void(bool)> pause_;
119
+ bool paused_ = false;
120
+ bool responded_ = false;
121
+ bool done_ = false;
122
+ webm::SourceInfo info_;
123
+ int64_t skip_ = 0;
124
+ std::string buffer_;
125
+ size_t offset_ = 0;
126
+ std::string error_;
127
+ };
128
+
129
+ std::string stdString(NSString* text) {
130
+ return text ? std::string(text.UTF8String) : std::string();
131
+ }
132
+
133
+ NSString* nsString(const std::string& text) {
134
+ return [NSString stringWithUTF8String:text.c_str()] ?: @"";
135
+ }
136
+
137
+ std::string describe(NSError* error) {
138
+ return stdString(error.localizedDescription ?: @"The read failed");
139
+ }
140
+
141
+ } // namespace
142
+
143
+ #pragma mark - The built-in URLSession fetcher
144
+
145
+ /** One task's pipe, and where its read starts. */
146
+ @interface AlphaVideoTaskState : NSObject {
147
+ @public
148
+ std::shared_ptr<Pipe> pipe;
149
+ int64_t position;
150
+ }
151
+ @end
152
+
153
+ @implementation AlphaVideoTaskState
154
+ @end
155
+
156
+ /** Routes a session's task callbacks to each task's pipe. Callbacks run on the session's queue. */
157
+ @interface AlphaVideoSessionDelegate : NSObject <NSURLSessionDataDelegate>
158
+ - (void)track:(NSURLSessionTask *)task state:(AlphaVideoTaskState *)state;
159
+ @end
160
+
161
+ @implementation AlphaVideoSessionDelegate {
162
+ // @synchronized (self)
163
+ NSMutableDictionary<NSNumber *, AlphaVideoTaskState *> *_tasks;
164
+ }
165
+
166
+ - (instancetype)init {
167
+ if ((self = [super init])) _tasks = [NSMutableDictionary dictionary];
168
+ return self;
169
+ }
170
+
171
+ - (void)track:(NSURLSessionTask *)task state:(AlphaVideoTaskState *)state {
172
+ @synchronized (self) {
173
+ _tasks[@(task.taskIdentifier)] = state;
174
+ }
175
+ }
176
+
177
+ - (nullable AlphaVideoTaskState *)stateOf:(NSURLSessionTask *)task remove:(BOOL)remove {
178
+ @synchronized (self) {
179
+ AlphaVideoTaskState *state = _tasks[@(task.taskIdentifier)];
180
+ if (remove) [_tasks removeObjectForKey:@(task.taskIdentifier)];
181
+ return state;
182
+ }
183
+ }
184
+
185
+ static int64_t AlphaVideoTotalFromContentRange(NSString *contentRange, int64_t *start) {
186
+ // "bytes 100-199/1000", "bytes 100-199/*" or, answering 416, "bytes */1000"
187
+ NSScanner *scanner = [NSScanner scannerWithString:contentRange ?: @""];
188
+ long long first = -1, total = -1;
189
+ if (![scanner scanString:@"bytes" intoString:nil]) return -1;
190
+ if (![scanner scanString:@"*" intoString:nil]) {
191
+ if (![scanner scanLongLong:&first]) return -1;
192
+ [scanner scanUpToString:@"/" intoString:nil];
193
+ }
194
+ if (start) *start = first;
195
+ if (![scanner scanString:@"/" intoString:nil] || ![scanner scanLongLong:&total]) return -1;
196
+ return total;
197
+ }
198
+
199
+ - (void)URLSession:(NSURLSession *)session
200
+ dataTask:(NSURLSessionDataTask *)dataTask
201
+ didReceiveResponse:(NSURLResponse *)response
202
+ completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
203
+ AlphaVideoTaskState *state = [self stateOf:dataTask remove:NO];
204
+ if (!state) {
205
+ completionHandler(NSURLSessionResponseCancel);
206
+ return;
207
+ }
208
+ NSHTTPURLResponse *http = [response isKindOfClass:[NSHTTPURLResponse class]] ? (NSHTTPURLResponse *)response : nil;
209
+ webm::SourceInfo info;
210
+ info.etag = stdString([http valueForHTTPHeaderField:@"ETag"]);
211
+ info.lastModified = stdString([http valueForHTTPHeaderField:@"Last-Modified"]);
212
+ NSString *encoding = [http valueForHTTPHeaderField:@"Content-Encoding"];
213
+ if (encoding.length > 0 && [encoding caseInsensitiveCompare:@"identity"] != NSOrderedSame) {
214
+ // URLSession decodes it, and the offsets would be into the decoded bytes
215
+ state->pipe->finish(stdString([NSString stringWithFormat:@"%@ came compressed (%@)", response.URL, encoding]));
216
+ completionHandler(NSURLSessionResponseCancel);
217
+ return;
218
+ }
219
+ NSInteger status = http ? http.statusCode : 200;
220
+ NSString *contentRange = [http valueForHTTPHeaderField:@"Content-Range"];
221
+ if (status == 206) {
222
+ int64_t start = -1;
223
+ info.size = AlphaVideoTotalFromContentRange(contentRange, &start);
224
+ if (start != state->position) {
225
+ state->pipe->finish(stdString([NSString stringWithFormat:@"Asked %@ for bytes from %lld and got %@",
226
+ response.URL, (long long)state->position, contentRange]));
227
+ completionHandler(NSURLSessionResponseCancel);
228
+ return;
229
+ }
230
+ state->pipe->respond(info);
231
+ } else if (status == 200) {
232
+ // No ranges: the whole file, read past the bytes before the position
233
+ info.size = response.expectedContentLength >= 0 ? response.expectedContentLength : -1;
234
+ state->pipe->respond(info, state->position);
235
+ } else if (status == 416) {
236
+ // At or past the end: a read that ends at once
237
+ info.size = AlphaVideoTotalFromContentRange(contentRange, nullptr);
238
+ state->pipe->respond(info);
239
+ state->pipe->finish("");
240
+ completionHandler(NSURLSessionResponseCancel);
241
+ return;
242
+ } else {
243
+ state->pipe->finish(stdString([NSString stringWithFormat:@"HTTP %ld for %@", (long)status, response.URL]));
244
+ completionHandler(NSURLSessionResponseCancel);
245
+ return;
246
+ }
247
+ completionHandler(NSURLSessionResponseAllow);
248
+ }
249
+
250
+ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
251
+ AlphaVideoTaskState *state = [self stateOf:dataTask remove:NO];
252
+ if (!state) return;
253
+ [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange range, BOOL *stop) {
254
+ state->pipe->push(static_cast<const uint8_t *>(bytes), range.length);
255
+ }];
256
+ }
257
+
258
+ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
259
+ AlphaVideoTaskState *state = [self stateOf:task remove:YES];
260
+ if (!state) return;
261
+ state->pipe->finish(error ? describe(error) : "");
262
+ }
263
+
264
+ @end
265
+
266
+ namespace {
267
+
268
+ /**
269
+ * Reads a URL with URLSession: a `Range: bytes=N-` request per open, kept open as the reader
270
+ * reads on, and suspended while the reader has a megabyte waiting.
271
+ */
272
+ class URLSessionUpstream : public webm::Upstream {
273
+ public:
274
+ URLSessionUpstream(NSURL* url, NSDictionary<NSString*, NSString*>* headers)
275
+ : url_(url), headers_([headers copy]), delegate_([AlphaVideoSessionDelegate new]) {
276
+ NSURLSessionConfiguration* configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
277
+ // The reader's store is the cache
278
+ configuration.URLCache = nil;
279
+ configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
280
+ configuration.timeoutIntervalForRequest = 30;
281
+ session_ = [NSURLSession sessionWithConfiguration:configuration delegate:delegate_ delegateQueue:nil];
282
+ }
283
+
284
+ ~URLSessionUpstream() override {
285
+ close();
286
+ // The session holds its delegate until invalidated
287
+ [session_ invalidateAndCancel];
288
+ }
289
+
290
+ bool open(int64_t position, webm::SourceInfo& info, std::string& error) override {
291
+ close();
292
+ NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url_];
293
+ [headers_ enumerateKeysAndObjectsUsingBlock:^(NSString* name, NSString* value, BOOL* stop) {
294
+ [request setValue:value forHTTPHeaderField:name];
295
+ }];
296
+ // Offsets are into the file as stored, so no transparent gzip
297
+ [request setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"];
298
+ [request setValue:[NSString stringWithFormat:@"bytes=%lld-", (long long)position] forHTTPHeaderField:@"Range"];
299
+ NSURLSessionDataTask* task = [session_ dataTaskWithRequest:request];
300
+
301
+ auto pipe = std::make_shared<Pipe>();
302
+ pipe->setFlowControl([task](bool pause) {
303
+ if (pause) [task suspend];
304
+ else [task resume];
305
+ });
306
+ {
307
+ std::lock_guard<std::mutex> lock(mutex_);
308
+ if (cancelled_) {
309
+ error = "Cancelled";
310
+ return false;
311
+ }
312
+ task_ = task;
313
+ pipe_ = pipe;
314
+ }
315
+ AlphaVideoTaskState* state = [AlphaVideoTaskState new];
316
+ state->pipe = pipe;
317
+ state->position = position;
318
+ [delegate_ track:task state:state];
319
+ [task resume];
320
+ return pipe->waitResponse(info, error);
321
+ }
322
+
323
+ int64_t read(uint8_t* out, int64_t size, std::string& error) override {
324
+ std::shared_ptr<Pipe> pipe = currentPipe();
325
+ if (!pipe) {
326
+ error = "Not open";
327
+ return -1;
328
+ }
329
+ return pipe->read(out, size, error);
330
+ }
331
+
332
+ void close() override {
333
+ NSURLSessionDataTask* task;
334
+ std::shared_ptr<Pipe> pipe;
335
+ {
336
+ std::lock_guard<std::mutex> lock(mutex_);
337
+ task = task_;
338
+ pipe = std::move(pipe_);
339
+ task_ = nil;
340
+ }
341
+ if (pipe) pipe->cancel();
342
+ [task cancel];
343
+ }
344
+
345
+ void cancel() override {
346
+ {
347
+ std::lock_guard<std::mutex> lock(mutex_);
348
+ cancelled_ = true;
349
+ }
350
+ close();
351
+ }
352
+
353
+ private:
354
+ std::shared_ptr<Pipe> currentPipe() {
355
+ std::lock_guard<std::mutex> lock(mutex_);
356
+ return pipe_;
357
+ }
358
+
359
+ NSURL* const url_;
360
+ NSDictionary<NSString*, NSString*>* const headers_;
361
+ AlphaVideoSessionDelegate* const delegate_;
362
+ NSURLSession* session_;
363
+ std::mutex mutex_;
364
+ NSURLSessionDataTask* task_ = nil;
365
+ std::shared_ptr<Pipe> pipe_;
366
+ bool cancelled_ = false;
367
+ };
368
+
369
+ } // namespace
370
+
371
+ #pragma mark - AlphaVideoSourceReader
372
+
373
+ NSString *const AlphaVideoErrorNoSourceModule = @"NO_SOURCE_MODULE";
374
+
375
+ /**
376
+ * The add-on's class, as the player uses it (range-source's RangeSource.h). The player doesn't
377
+ * link the add-on, so it finds the class by name.
378
+ */
379
+ @protocol AlphaVideoRangeSourceAddOn
380
+ @property (class, nonatomic, readonly) const void *plugin;
381
+ + (BOOL)getUpstream:(void *)upstream
382
+ forDataSourceNamed:(NSString *)name
383
+ URL:(NSURL *)url
384
+ headers:(NSDictionary<NSString *, NSString *> *)headers;
385
+ @end
386
+
387
+ static Class<AlphaVideoRangeSourceAddOn> AlphaVideoRangeSourceClass(void) {
388
+ Class addOn = NSClassFromString(@"RangeSource");
389
+ return [addOn respondsToSelector:@selector(plugin)]
390
+ && [addOn respondsToSelector:@selector(getUpstream:forDataSourceNamed:URL:headers:)]
391
+ ? addOn : Nil;
392
+ }
393
+
394
+ const RangeSourcePlugin *AlphaVideoRangeSourcePlugin(void) {
395
+ static const RangeSourcePlugin *plugin = [] {
396
+ Class<AlphaVideoRangeSourceAddOn> addOn = AlphaVideoRangeSourceClass();
397
+ if (!addOn) return static_cast<const RangeSourcePlugin *>(nullptr);
398
+ std::string why;
399
+ const RangeSourcePlugin *usable = webm::usablePlugin(static_cast<const RangeSourcePlugin *>([addOn plugin]), why);
400
+ if (!usable) NSLog(@"[AlphaVideo] Not using range-source: %s", why.c_str());
401
+ return usable;
402
+ }();
403
+ return plugin;
404
+ }
405
+
406
+ // Caches/alpha-video on disk, or the memory store's name
407
+ static NSString *const AlphaVideoStoreName = @"alpha-video";
408
+
409
+ static std::shared_ptr<webm_rs::Store> AlphaVideoStore(const RangeSourcePlugin *plugin, NSString *storage,
410
+ int64_t maxStorageBytes) {
411
+ if ([storage isEqualToString:@"disk"]) {
412
+ NSString *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
413
+ return webm_rs::Store::disk(plugin, stdString([caches stringByAppendingPathComponent:AlphaVideoStoreName]),
414
+ maxStorageBytes);
415
+ }
416
+ if ([storage isEqualToString:@"memory"]) return webm_rs::Store::memory(plugin, stdString(AlphaVideoStoreName), maxStorageBytes);
417
+ return nullptr;
418
+ }
419
+
420
+ @implementation AlphaVideoSourceReader {
421
+ std::shared_ptr<webm::SourceReader> _reader;
422
+ }
423
+
424
+ + (instancetype)openURL:(NSURL *)url
425
+ dataSource:(NSString *)dataSource
426
+ headers:(NSDictionary<NSString *, NSString *> *)headers
427
+ storage:(NSString *)storage
428
+ key:(NSString *)key
429
+ maxStorageBytes:(int64_t)maxStorageBytes
430
+ error:(NSString **)error {
431
+ const RangeSourcePlugin *plugin = AlphaVideoRangeSourcePlugin();
432
+ std::string openError;
433
+ std::shared_ptr<webm::SourceReader> reader;
434
+ if (dataSource) {
435
+ RangeSourceUpstream upstream{};
436
+ if (!plugin || ![AlphaVideoRangeSourceClass() getUpstream:&upstream forDataSourceNamed:dataSource URL:url
437
+ headers:headers]) {
438
+ if (error) *error = [NSString stringWithFormat:@"No data source is registered as \"%@\" (RangeDataSources)", dataSource];
439
+ return nil;
440
+ }
441
+ reader = webm_rs::RangeReader::open(plugin, upstream, AlphaVideoStore(plugin, storage, maxStorageBytes),
442
+ stdString(key), webm_rs::ReaderOptions(), openError);
443
+ } else if ([url.scheme isEqualToString:@"https"] || [url.scheme isEqualToString:@"http"]) {
444
+ auto upstream = std::make_unique<URLSessionUpstream>(url, headers);
445
+ reader = plugin
446
+ ? std::shared_ptr<webm::SourceReader>(webm_rs::RangeReader::open(
447
+ plugin, std::move(upstream), AlphaVideoStore(plugin, storage, maxStorageBytes), stdString(key),
448
+ webm_rs::ReaderOptions(), openError))
449
+ : webm_rs::DownloadReader::open(std::move(upstream), stdString(NSTemporaryDirectory()), openError);
450
+ } else {
451
+ if (error) *error = [NSString stringWithFormat:@"Not an http(s) URL: %@", url];
452
+ return nil;
453
+ }
454
+ if (!reader) {
455
+ if (error) *error = nsString(openError);
456
+ return nil;
457
+ }
458
+ AlphaVideoSourceReader *result = [AlphaVideoSourceReader new];
459
+ result->_reader = std::move(reader);
460
+ return result;
461
+ }
462
+
463
+ - (BOOL)fetchAll:(NSString **)error {
464
+ if (_reader->fetchAll()) return YES;
465
+ if (error) {
466
+ std::string message = _reader->lastError();
467
+ *error = message.empty() ? @"Reading the video failed" : nsString(message);
468
+ }
469
+ return NO;
470
+ }
471
+
472
+ - (std::shared_ptr<webm::SourceReader>)rangeReader {
473
+ return _reader;
474
+ }
475
+
476
+ @end