540 Commits

Author SHA1 Message Date
b1b5fa1717 Add quality selector 2025-10-20 01:24:18 +02:00
7f04c16e3e Add video track handling on web 2025-10-20 00:25:36 +02:00
959c23d6f2 Add audio track support 2025-10-20 00:13:46 +02:00
958778e647 Fix media session for web 2025-10-19 22:00:26 +02:00
c49b2f17f2 Add error handling for the web 2025-10-19 16:47:15 +02:00
ccf9497313 Add optional mimeType string in source object 2025-10-14 12:09:16 +02:00
bcc084e083 Handle media sessions 2025-10-13 23:46:18 +02:00
c356fa10ec Rework everything to use videojs instead of shaka 2025-10-13 22:42:11 +02:00
f3fdb8b648 Fix event listeners this 2025-10-12 17:21:09 +02:00
52a22aa10e Fix typescript issues 2025-10-10 14:09:16 +02:00
771196dd0f Some cleanup 2025-10-10 14:05:32 +02:00
82ff34fa9c Fix package lock 2025-10-10 14:05:32 +02:00
b2cf9d63f1 Map source 2025-10-10 14:05:32 +02:00
937d34d73e Implement event handlers for web 2025-10-10 14:05:32 +02:00
a092578cab Implement html video properties using headless video 2025-10-10 14:05:32 +02:00
d6803b2b4c Implement VideoView for web 2025-10-10 14:05:32 +02:00
6703499e60 Scaffold web 2025-10-10 14:05:32 +02:00
ecf5849f2c fixup! Add shaka as an optional dependency and fix their type 2025-10-10 14:05:32 +02:00
8ad923750b wip: Biome config (TO DELETE LATER) 2025-10-10 14:05:32 +02:00
796c0edfa0 Add shaka as an optional dependency and fix their type 2025-10-10 14:05:32 +02:00
57039bb564 Add shell.nix & editorconfig 2025-10-10 14:05:32 +02:00
Krzysztof Moch
9b74665fb4 chore: update release script (#4727) 2025-10-08 13:39:19 +02:00
Krzysztof Moch
1671c63dab feat(android): support flexible page sizes (#4726) 2025-10-08 13:14:53 +02:00
Krzysztof Moch
02044de0e9 feat: add notification controls (#4721) 2025-10-06 16:36:52 +02:00
Krzysztof Moch
375fbeb0eb feat: bump nitro modules (#4720) 2025-10-03 16:14:47 +02:00
Krzysztof Moch
4ebc9b7f05 feat(android): add surface type prop (#4719) 2025-10-03 15:58:29 +02:00
Krzysztof Moch
a97581ab8a chore: release 7.0.0-alpha.5 2025-10-02 13:33:03 +02:00
Krzysztof Moch
58a268c022 chore: cleanup events code 2025-10-02 13:31:21 +02:00
07b55755d1 refactor: event handler to use add/remove style (#4708) 2025-10-02 12:53:32 +02:00
Krzysztof Moch
1ec3610aae fix(android): pip crash when entering fullscreen (#4706) 2025-09-25 14:39:35 +02:00
Krzysztof Moch
100aefc8d4 feat: add keepScreenAwake prop (#4700) 2025-09-21 13:12:33 +02:00
Krzysztof Moch
4052514cf2 feat: add buffer config (#4692) 2025-09-13 16:37:01 +02:00
Krzysztof Moch
a97b75b935 fix(android): system controls are not hidden when going back to app (#4675) 2025-09-07 21:31:59 +02:00
Krzysztof Moch
f929d56a87 refactor: load player on initialization (#4673) 2025-09-01 13:50:51 +02:00
Krzysztof Moch
4ed51e14ca chore: release 7.0.0-alpha.4 2025-08-26 18:04:58 +02:00
Krzysztof Moch
b7a4c57d90 chore: update release script 2025-08-26 18:02:16 +02:00
Krzysztof Moch
776bd4ab45 chore(android): move package to twg namespace (#4672) 2025-08-26 17:27:28 +02:00
Krzysztof Moch
5012373b7c feat: add drm interfaces (#4657) 2025-08-26 13:50:36 +02:00
Jakub Boguski
8e40502172 docs: Update copy around Offline Video SDK on v7 (#4652) 2025-08-12 10:25:56 +02:00
Krzysztof Moch
2da34ddfda chore: release 7.0.0-alpha.3 2025-08-04 13:48:41 +02:00
Krzysztof Moch
4de93f43f1 feat: bump nitro modules version (#4641) 2025-08-04 13:43:12 +02:00
Krzysztof Moch
fce0640b6f fix(android): add prefab script (#4640) 2025-08-04 12:04:41 +02:00
Krzysztof Moch
f48567b307 feat: add plugins (#4639) 2025-08-04 10:02:59 +02:00
Krzysztof Moch
3509b5d72c feat: add muted property to onVolumeChange event (#4633) 2025-07-29 11:22:36 +02:00
Krzysztof Moch
a9adbbea2e chore(android): code cleanup 2025-07-28 13:28:00 +02:00
Krzysztof Moch
bce453532a chore: release 7.0.0-alpha.2 2025-07-25 13:36:25 +02:00
Krzysztof Moch
38e5f0dcb6 chore(ci): update docs workflows (#4627) 2025-07-25 13:13:24 +02:00
Krzysztof Moch
bc2a8f0ca6 docs: upgrade documentation (#4622) 2025-07-24 17:31:01 +02:00
Krzysztof Moch
7b8d0b879d fix: codegen config (#4616) 2025-07-21 11:54:37 +02:00
Krzysztof Moch
0387ba66cd docs: update README 2025-07-14 13:07:39 +02:00
Krzysztof Moch
5b47cf5b61 fix: component linking method (#4601) 2025-07-13 17:55:43 +02:00
Krzysztof Moch
de8b9cb0cc chore: update release-it config 2025-07-07 14:29:25 +02:00
Krzysztof Moch
0a34889796 chore: release 7.0.0-alpha.1 2025-07-07 14:07:30 +02:00
Krzysztof Moch
a561d6b85d feat(ios): allow to side load subtitles when playing hls stream (#4593) 2025-07-07 10:58:20 +02:00
Bartłomiej Piekarz
1e0536ce4f feat(android): implement Picture-in-Picture (PiP) support for multiple videos playback (#4589) 2025-07-02 10:39:15 +02:00
Krzysztof Moch
e94d16cbcf chore: include README in npm package 2025-06-30 20:36:25 +02:00
Krzysztof Moch
c3c75b6d79 chore: release 7.0.0-alpha.0 2025-06-30 20:07:42 +02:00
Krzysztof Moch
0f55a372bf chore: prepare for release 2025-06-30 18:49:11 +02:00
Krzysztof Moch
2cc3430afb docs: update README (#21) 2025-06-30 18:17:28 +02:00
Krzysztof Moch
791c54e9e9 fix(ios): runtime crash 2025-06-28 16:30:13 +02:00
Krzysztof Moch
3df1ba56e9 fix(ci): bump node version 2025-06-28 15:47:45 +02:00
Krzysztof Moch
4e15523e82 chore: bump package version 2025-06-28 15:42:06 +02:00
pieczasz-thewidlarzgroup
b31f8f0732 fix(ios): Improves playback state and buffering events (#18)
Co-authored-by: Pieczasz <bartekp854@gmail.com>
Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
2025-06-25 22:38:26 +02:00
pieczasz-thewidlarzgroup
6baa1e4f4a feat(android): optimizes video controls for small video players (#17)
Co-authored-by: Pieczasz <bartekp854@gmail.com>
Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
2025-06-25 21:23:19 +02:00
Krzysztof Moch
146471d23c fix(android): stub files are included even if feature is enabled 2025-06-25 14:22:57 +02:00
Krzysztof Moch
cde3fdb9a4 fix(android): catch error when PiP is not supported in activity 2025-06-25 08:35:58 +02:00
Krzysztof Moch
f9b241bfba fix: bump dependencies (#19) 2025-06-23 12:15:57 +02:00
Krzysztof Moch
cf34670c0a feat(ios): set proper audiovisualBackgroundPlaybackPolicy for background mode (#16) 2025-06-18 08:45:12 +02:00
Krzysztof Moch
235bc3bea3 feat: implement background audio playback and video enhancements (#15) 2025-06-15 20:07:04 +02:00
Krzysztof Moch
ddca01fa9f refactor(android): use fragment instead of activity for fullscreen view 2025-06-02 16:29:53 +02:00
Krzysztof Moch
20fa017fc0 docs: add documentation (#14) 2025-06-02 15:04:22 +02:00
jboguskitwg
f5df436826 chore: readme and license chagnes (#13) 2025-05-22 13:49:30 +02:00
Krzysztof Moch
bae799523b fix(android): preload is never called 2025-05-15 15:25:09 +02:00
Krzysztof Moch
5de9b9a541 feat: add isPlaying property 2025-05-15 14:52:05 +02:00
Krzysztof Moch
f92e492923 chore: code cleanup 2025-05-15 09:45:42 +02:00
Krzysztof Moch
3002768834 chore: bump package version 2025-05-15 09:12:48 +02:00
Krzysztof Moch
d9406e9d44 feat: allow null in replaceSourceAsync (#12) 2025-05-15 09:11:35 +02:00
Krzysztof Moch
41a1d88265 fix: add react-native-nitro-modules patch (#11) 2025-05-10 19:03:02 +02:00
Krzysztof Moch
4eff3efbf2 chore: bump version 2025-05-09 13:56:58 +02:00
Krzysztof Moch
ad2da2996b feat: add expo plugin (#10) 2025-05-09 13:54:48 +02:00
Krzysztof Moch
742ddb3183 feat: events callbacks & external subtitles (#9) 2025-05-08 17:13:39 +02:00
Krzysztof Moch
e743c63a82 chore: bump version 2025-05-05 15:26:42 +02:00
Krzysztof Moch
1d97e63d42 fix: add static frameworks support (#8) 2025-05-05 15:19:34 +02:00
Krzysztof Moch
35375f47d5 fix(android): cannot disable native controls 2025-04-30 16:04:17 +02:00
Krzysztof Moch
afce52c88f fix(ios): name typo 2025-04-30 15:33:54 +02:00
Krzysztof Moch
f0989a51e1 chore: bump react-native-video package version 2025-04-30 15:26:14 +02:00
Krzysztof Moch
c165dfb3b0 feat: add fullscreen & Picture in Picture API (#7) 2025-04-30 15:20:39 +02:00
Krzysztof Moch
76a60b853a chore: add release action 2025-04-22 15:37:22 +02:00
Krzysztof Moch
c6793e5aa3 chore: update package.json 2025-04-22 14:45:21 +02:00
Krzysztof Moch
47e5a26819 chore: update license generation 2025-04-22 13:35:46 +02:00
Krzysztof Moch
474ad9ebed fix(package): adjust publish settings 2025-04-22 11:47:11 +02:00
Krzysztof Moch
bbe7524a33 fix(android): duplicated libreactnative.so error 2025-04-22 11:46:00 +02:00
Krzysztof Moch
c1bab6b946 feat(android): add support for RN 0.77 2025-04-22 11:44:54 +02:00
Krzysztof Moch
dcffd29f37 feat(android): add support for hls & dash (#6) 2025-04-17 16:48:51 +02:00
Krzysztof Moch
047d74de38 feat: add more basic functionality (#5) 2025-04-16 22:28:25 +02:00
Krzysztof Moch
950dde6a7d chore(example): update react-native version 2025-04-07 21:28:18 +02:00
Krzysztof Moch
a72c2d6078 chore: update nitro modules version 2025-04-07 19:59:45 +02:00
Krzysztof Moch
7e2ce4b9a7 feat: handle native errors 2025-02-05 14:17:11 +01:00
Krzysztof Moch
815d677127 chore(android): refactor & clean codebase 2025-01-23 21:38:26 +01:00
Krzysztof Moch
677b8545e1 chore(example): fix react native path 2025-01-23 17:05:18 +01:00
Krzysztof Moch
6adbba4c87 chore(ios): refactor & clean codebase 2025-01-23 15:15:08 +01:00
Krzysztof Moch
811d8169d2 feat: override dispose method in VideoPlayer to cleanup resources 2025-01-23 15:09:52 +01:00
Krzysztof Moch
30d58d3a39 chore(infra): move to monorepo 2025-01-22 23:18:23 +01:00
Krzysztof Moch
d6d7e714fe chore(ios): rename private _player to player 2025-01-21 19:52:14 +01:00
Krzysztof Moch
5f11ab0ca6 chore(git): add .lockb to git attributes 2025-01-21 12:49:44 +01:00
Krzysztof Moch
e1781e2e1a chore: rename from NitroVideo to ReactNativeVideo 2025-01-20 13:37:06 +01:00
Krzysztof Moch
85a9a1472f chore: lint code 2025-01-20 13:28:27 +01:00
Krzysztof Moch
2e6ac0d5a0 chore: update third party licenses 2025-01-20 13:26:18 +01:00
Krzysztof Moch
a172eac96f chore(js): clean code 2025-01-20 13:25:36 +01:00
Krzysztof Moch
90eb8e9154 docs: update readme 2025-01-20 12:00:58 +01:00
Krzysztof Moch
e5314d7c31 chore: update example 2025-01-18 23:56:27 +01:00
Krzysztof Moch
8de0a93b8b feat: refactor player initialization 2025-01-18 23:56:02 +01:00
Krzysztof Moch
cefee73076 feat: add getAssetInformationAsync function to source object 2025-01-18 23:55:03 +01:00
Krzysztof Moch
1f89daa460 chore(example): bump react-native version 2025-01-18 23:52:30 +01:00
Krzysztof Moch
cf17963709 chore: bump nitro version 2025-01-18 23:14:20 +01:00
Krzysztof Moch
c75be7f08b feat: add useVideoPlayer hook 2025-01-16 09:13:12 +01:00
Krzysztof Moch
d3c76142df chore: add package.json to exports
This fixes codegen not detecting library
2025-01-16 09:12:59 +01:00
Krzysztof Moch
ad0ce759f9 docs(jsdoc): add description to properties 2024-12-13 15:03:56 +01:00
Krzysztof Moch
8d830243a4 feat: add duration property to player 2024-12-13 14:55:25 +01:00
Krzysztof Moch
237cc6e157 fix(ios): currentTime scale 2024-12-09 13:26:08 +01:00
Krzysztof Moch
fa4098ae53 fix(ios): store view as week reference in HybridVideoViewViewManager 2024-11-27 15:59:16 +01:00
Krzysztof Moch
69be802292 chore: update known issues 2024-11-27 15:47:11 +01:00
Krzysztof Moch
275204f53f fix(android): no video after onDetachedFromWindow 2024-11-27 15:33:22 +01:00
Krzysztof Moch
501be406fc fix: export types 2024-11-27 13:47:37 +01:00
Krzysztof Moch
8e9e39b667 chore: update nitrogen files 2024-11-24 17:31:19 +01:00
Krzysztof Moch
402b1ce059 chore: update README.md 2024-11-11 13:27:31 +01:00
Krzysztof Moch
9474bb4fdf chore(example): add more platforms and fix metro resolve 2024-11-11 13:25:36 +01:00
Krzysztof Moch
b13a718393 chore: move to bun 2024-11-11 13:23:58 +01:00
Krzysztof Moch
2fff50c697 chore(deps): bump nitro modules version 2024-11-11 13:19:14 +01:00
Krzysztof Moch
e51873a3a4 fix(android): wait for view to initialize before emitting event (#3) 2024-10-31 12:07:42 +01:00
Krzysztof Moch
917b5f9470 chore: update README.md 2024-10-29 16:03:53 +01:00
Krzysztof Moch
136a8a7b94 chore: update README.md 2024-10-29 16:03:53 +01:00
Krzysztof Moch
4ba3b7a61f chore: publish package 2025-06-30 19:32:12 +02:00
Krzysztof Moch
18527fcc5c chore(infra): update validation script (#4586) 2025-06-30 13:21:35 +02:00
Tuan Luong
94967fc4a6 fix(android): create custom event class for dispatcher (#4575) 2025-06-23 09:40:14 +02:00
Mosab Anini
b56b647d85 fix(android): speed control is not applied (#4572) 2025-06-18 08:17:47 +02:00
Varuzh1
b51b579ff0 fix(ios): retain cycle and memory leak involving the RCTVideo instance when using Google IMA ads in react-native-video. (#4574)
Co-authored-by: Varuzhan Khachatryan <varuzhan.khachatryan23@gmail.com>
2025-06-18 08:12:21 +02:00
Krzysztof Moch
60c3a08ae3 chore: release v6.15.0 2025-06-12 13:44:37 +02:00
Krzysztof Moch
79d25bf1bd chore: fix repository url in package.json 2025-06-11 12:28:57 +02:00
Lukas
3576a134e6 feat: allow audio mixing if one of the video views require it (#4559) 2025-06-11 12:23:53 +02:00
Wojciech Ogrodowczyk
88c20d1c06 feat(ios): forward real fullscreen events from AVPlayer instead of guessing (#4509) 2025-06-11 09:33:38 +02:00
Kamil Moskała
45fd250adf chore(ios): make DRMParams properties public (#4568) 2025-06-11 09:32:58 +02:00
Krzysztof Moch
9f03cc5a09 fix(web): add missing component type 2025-06-11 08:36:37 +02:00
Kamil Moskała
9cf780276a feat(plugin): overrideMediaSourceFactory (#4566) 2025-06-05 12:36:55 +02:00
Krzysztof Moch
55449d9ec4 chore: release v6.14.1 2025-05-28 16:29:02 +02:00
Kamil Moskała
9449eb34f3 feat(android): allow plugins to override drm session manager (#4558) 2025-05-28 16:11:57 +02:00
Krzysztof Moch
e165cdefa1 docs: update readme (#4554) 2025-05-22 16:56:36 +02:00
Krzysztof Moch
6c5bb77def chore: release v6.14.0 2025-05-10 17:06:14 +02:00
Krzysztof Moch
701f777b6e chore: cleanup package.json (#4544) 2025-05-10 16:56:53 +02:00
Ben Winters
089e938aeb fix(ios): set _isBuffering = true only if newValue is also true (#4532) 2025-05-10 16:46:18 +02:00
Jonathan Jacobs
fc1e3f4fd1 feat(web): allow style prop overrides (#4528)
Co-authored-by: Jonathan Jacobs <jonathan@yoco.co.za>
2025-05-10 16:45:11 +02:00
jason
ecfe12aa81 fix(ios): default cropStart fallback (#4540) 2025-05-10 16:43:41 +02:00
BryanChemp
49173edd59 Changing switch sintaxe to preserve compatibility with java 11 or minus (#4521) 2025-05-07 14:27:34 +02:00
Krzysztof Moch
987be4b293 fix(ios): swfit modular headers (#4527) 2025-04-24 19:02:26 +02:00
Krzysztof Moch
1c95041a4a chore(example): bump react-native & expo version (#4500) 2025-04-23 12:42:36 +02:00
Kamil Moskała
368830954e docs: update links (#4525) 2025-04-20 22:24:00 +02:00
Kamil Moskała
dc4195e339 chore: add issue number to link (#4524) 2025-04-20 22:11:08 +02:00
Krzysztof Moch
735e35a2ea chore: release v6.13.0 2025-04-18 18:08:14 +02:00
Krzysztof Moch
b1b3db3010 feat(ios): add overridePlayerAsset to AVPlayerPlugin (#4522) 2025-04-18 18:02:55 +02:00
Kamil Moskała
2bdc0daf2e chore: update bot messages (#4517) 2025-04-16 09:30:52 +02:00
Ryan Fowler
b17c319c32 fix(macCatalyst): allowsVideoFrameAnalysis not available in catalyst until 18.0 (#4515) 2025-04-14 15:48:15 +02:00
Kamil Moskała
40340467d7 fix(tvos): build (#4511) 2025-04-14 15:47:15 +02:00
Krzysztof Moch
ab8ec0699b chore: release v6.12.0 2025-04-06 16:42:41 +02:00
Krzysztof Moch
8836362609 feat(ios): allow to disable audio sessions management (#4492)
* feat(ios): allow to disable audio sessions management

* chore: lint code

* chore: fix alphabetical order
2025-04-06 16:39:18 +02:00
Vijay Bindraban
a849cc19e8 fix: add extra checks to determine if a file is local (#4503)
* fix: beter isLocalAsset checks

* fix: add "asset://" to possible assets paths

* Update src/Video.tsx

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2025-04-06 16:18:34 +02:00
Kamil Moskała
41ddc5c27a feat(android): initial bitrate (#4480)
* feat(android): implement initialBitrate

* refactor: reorder type

* docs: add initialBitrate section

* fix: lint

* refactor: move `initialBitrate` into `source.bufferConfig`
2025-04-06 16:16:58 +02:00
Wojciech Ogrodowczyk
d2e5d9c64e feat(ios): set playback speed controls to initial playback rate (#4495)
Otherwise, the Video can be initiated with playback rate 0.5 and
play correctly (slower), but in native playback speed controls 1.0
will be marked as current playback speed.
2025-04-02 17:37:56 +02:00
Kamil Moskała
697afd52f6 feat(android): add asset management functions to plugin (#4494)
* feat: manage asset plugins

* fix: continue when plugin is not exoplayer plugin

* refactor: change functions plugin type

* docs: introduce new plugins
2025-03-28 13:41:13 +01:00
Kamil Moskała
f188a7fd48 refactor(ios): unpause after exiting fullscreen (#4472)
* refactor(ios): unpause after exiting fullscreen

* chore: lint code
2025-03-26 12:20:32 +01:00
Wojciech Ogrodowczyk
229a5764ea fix(ios): don't try to modify react view after unmount (#4474) 2025-03-22 15:17:41 +01:00
Kamil Moskała
4458627051 docs: update favico & add json-ld (#4477) 2025-03-19 10:57:41 +01:00
Krzysztof Moch
ede502bf74 chore: release v6.11.0 2025-03-16 14:15:57 +01:00
Emmanuel Ferdman
044e08a78d docs: update examples (#4468)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2025-03-13 20:19:49 +01:00
Lyqqt
d902c1bf43 feat(windows): add topSeek parms mentioned in docs (#4456) 2025-03-13 12:06:36 +01:00
Krzysztof Moch
9f02614f5d refactor(ios): add audio session manager (#4466) 2025-03-12 18:22:50 +01:00
Lyqqt
fa20223c44 fix(windows): event name not matches with SPEC (#4455) 2025-03-12 14:14:23 +01:00
Krzysztof Moch
6e6f91517c feat: enhance react-native-video plugins [Plugins API Breaking] (#4366)
* feat: allow plugins for providing custom DRM manager

This will allow plugins to provide DRM manger to create custom implementations, eg. using SKD from DRM providers

* chore(example): fix drm example on android

* chore: lint code

* fix: remove platform player logic & dependency from `RNVPlugin`

* chore: change warning to debug msg

* chore: lint code

* chore(example/bare): update Podfile.lock

* refactor: reorganize ReactNativeVideoManager plugin registration methods

* refactor: add helpers & clean code

* docs: update documentation

* lint code

* add comment

* docs: update plugins section
2025-03-12 14:13:46 +01:00
Marcin Dziewulski
b5103743e8 fix(android): call startForeground() immediately to prevent ForegroundServiceDidNotStartInTimeException (#4453)
* fix: call startForeground() immediately in onStartCommand() to prevent ForegroundServiceDidNotStartInTimeException

* fix: use string resources for notification text instead of hardcoded strings

* fix: add missing import

* fix: resolve CI issues

* fix: use getString() method to convert string resource IDs to actual strings
2025-03-12 13:42:02 +01:00
Olivier Bouillet
f9d3878ecc chore(ios): remove duplicated code (#4439) 2025-03-12 13:37:03 +01:00
Kamil Moskała
b11c1f315d docs: remove ga (#4462) 2025-03-11 17:42:22 +01:00
Alexander Todorov
bc533e53b0 fix(ios): invalid metadata handling (#4422)
- When the metadata has the tag "iTunSMPB" or "iTunNORM" then the metadata is not converted correctly and comes [nil, nil, ...] which leads to crash

Co-authored-by: Alexander <aleksandar.todorov@serviceos.com>
2025-03-10 22:03:05 +01:00
Krzysztof Moch
9055b14cf4 docs: update structure (#4454)
* [Preview] docs: update  (#9)

Co-authored-by: Bart Widlarz <bart@thewidlarzgroup.com>

* docs: update links

* docs: fix naming

* docs: update footer

* docs: update content

* docs: refine grammar

* fix(docs): update typo

* docs: update url

* refactor: sdk name

* docs: add issue boost link

* docs: add new roadmap to readme

* lint code

---------

Co-authored-by: Bart Widlarz <bart@thewidlarzgroup.com>
Co-authored-by: Kamil <moskalakamil07@gmail.com>
2025-03-06 12:38:25 +01:00
YangJH
82f5f3d21c fix(android): fix bugs related Android PIP listeners (#4441)
* fix(android): fixed bug where listeners were not removed properly

* fix(android): fixes a bug that causes PIP to work when player is null

* refactor(android): call pipUnsubscriber without handler

* chore(android): apply lint
2025-03-06 12:24:59 +01:00
Kamil Moskała
13a5da8955 chore: update utm links (#4447)
* chore: update utm links

* Update package.json
2025-03-04 15:32:40 +01:00
YangJH
235c281219 fix(android): prevent ratio calculations before loading video (#4442) 2025-03-04 15:11:13 +01:00
Philipp Mikheyev
057c287f12 fix(tvOS): handle allowsPictureInPicturePlayback for tvOS (#4448) 2025-03-04 15:08:24 +01:00
Olivier Bouillet
b8459ca71d chore: release v6.10.2 2025-02-22 15:05:17 +01:00
Szymon Rybczak
3edf511f9b docs: remove additional braces in config file (#4418) 2025-02-19 19:22:35 +01:00
Thomas Guenard
6697cbf5d0 fix(android): fix gradle exception text related to AndroidX version (#4420)
* fix(android): fix gradle exception text related to AndroidX version check
* Update android/build.gradle

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2025-02-19 19:19:41 +01:00
Konstantin
04eec42f1e fix(tvOS): fix tvos compile error for rotation handler (#4417) 2025-02-16 11:55:46 +01:00
Olivier Bouillet
451bafebd7 chore: release v6.10.1 2025-02-15 17:26:27 +01:00
Efstathios Ntonas
a8ca97f05f fix(ios): fix constraints when controls are enabled and video is inside a ScrollView (#4383) 2025-02-15 17:22:06 +01:00
Mihai
c8b800a508 fix(ios): the video has no audio by default (#4409)
This reverts the change in https://github.com/TheWidlarzGroup/react-native-video/pull/4319, I don't think that change is correct.

According to [docs](https://docs.thewidlarzgroup.com/react-native-video/component/props#selectedtexttrack) the default value for `selectedTextTrack.type` is "system" which means the video should respect user's preference in Settings > Accesibility > Subtitles & Captioning > Closed Captions + SDH. And this is exactly what happens by default without the changes in that PR. I think the author of that PR had this setting on, that's why it was showing the subtitles without setting something for `selectedTextTrack`, which IMO is the expected result. In order to explicitly turn off subtitles you have to set `selectedTextTrack={{ type: 'disabled' }}`. I tested it with this video and the settings option on and it works (disables the subtitles) -> https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8

Another reason why I think it should be reverted is because it also affects `selectedAudioTrack`. By default it should also have 'system' type according to [docs](https://docs.thewidlarzgroup.com/react-native-video/component/props#selectedaudiotrack), which means "Play the audio track that matches the system language. If none match, play the first track.", but the change in that PR turns the audio off by default unless you explicitly turn it on with `selectedAudioTrack={{ type: 'system' }}`.

Fixes #4400
2025-02-15 17:09:04 +01:00
Olivier Bouillet
faac5ad456 fix(sample): remove duplicate code in sample (#4391)
* fix: remove duplicated code in sample
2025-02-15 17:05:59 +01:00
Krzysztof Moch
2905b61a0d fix(infra): kotlin linter github action (#4408) 2025-02-09 14:14:40 +01:00
Olivier Bouillet
74b1d5b540 fix(android): remove transparent black overlay on android default controls (#4392) 2025-02-09 14:09:28 +01:00
Mehdi Taghdisi
ad52668d05 fix(android): check for valid width and height on video format data (#4394)
Co-authored-by: mehdi <m.taghdisi@digikala.com>
2025-01-27 18:43:30 +01:00
Olivier Bouillet
638f454a21 fix(android): check androidX version at early build process before really launching build (#4388) 2025-01-25 17:10:47 +01:00
Olivier Bouillet
43955037bb chore: release v6.10.0 2025-01-22 22:59:46 +01:00
Olivier Bouillet
3d3eba97e8 fix(ios): fix paused video by default (#4379)
* fix: update style to allow show control click
* fix: push missing revert
2025-01-22 22:56:50 +01:00
darth levi
d90bf47df5 fix(windows): conversion of string to Stretch enum (#4381) 2025-01-22 22:32:25 +01:00
Olivier Bouillet
d9f92b800e chore: add pre commit hook to lint and tsc the code (#4378) 2025-01-22 22:31:30 +01:00
Hugo EXTRAT
8b952e709a fix(android): support RN 0.77 (#4386) 2025-01-22 22:27:28 +01:00
YangJH
8dc10fd4b7 feat(web): implement web pip method and event (#4370)
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2025-01-18 15:19:55 +01:00
Olivier Bouillet
449dfb62b5 chore: fix linter (#4377) 2025-01-18 15:05:58 +01:00
YangJH
6c3af99979 fix(tvOS): fix tvos compile error (#4369)
* fix(tvOS): fix tvos compile error
2025-01-18 11:49:12 +01:00
Kamil Moskała
6cc1bff167 docs: fix analytics (#4368) 2025-01-13 15:01:15 +01:00
Krzysztof Moch
89ee02bdab chore: release v6.9.1 2025-01-10 12:09:07 +01:00
Joy
3924b5e295 fix: NPE in setEnterPictureInPictureOnLeave for unsupported Android versions (#4362) 2025-01-10 12:07:25 +01:00
Krzysztof Moch
eff8ea24af infra: fix bot comments (#4367) 2025-01-10 12:01:31 +01:00
Olivier Bouillet
c47d165668 chore(android): fix type in generic folder (#4323) 2025-01-04 13:48:59 +01:00
Vladimir
424f4eedde fix: avoid memory leak on iOS (#4355)
* fix: avoid memory leak on iOS

---------

Co-authored-by: Vladimir Vlasov <crivlaldo@gmail.com>
2025-01-04 13:47:22 +01:00
Krzysztof Moch
d31c72fc04 chore: release v6.9.0 2025-01-04 12:51:27 +01:00
YangJH
69a7bc2d26 feat: implement enterPictureInPictureOnLeave prop for both platform (Android, iOS) (#3385)
* docs: enable Android PIP

* chore: change comments

* feat(android): implement Android PictureInPicture

* refactor: minor refactor code and apply lint

* fix: rewrite pip action intent code for Android14

* fix: remove redundant codes

* feat: add isInPictureInPicture flag for lifecycle handling

- activity provide helper method for same purpose, but this flag makes code simple

* feat: add pipFullscreenPlayerView for makes PIP include video only

* fix: add manifest value checker for prevent crash

* docs: add pictureInPicture prop's Android guide

* fix: sync controller visibility

* refactor: refining variable name

* fix: check multi window mode when host pause

- some OS version call onPause on multi-window mode

* fix: handling when onStop is called while in multi-window mode

* refactor:  enhance PIP util codes

* fix: fix FullscreenPlayerView constructor

* refactor: add enterPictureInPictureOnLeave prop and pip methods

- remove pictureInPicture boolean prop
- add enterPictureInPictureOnLeave boolean prop
- add enterPictureInPicture method
- add exitPictureInPicture method

* fix: fix lint error

* fix: prevent audio play in background without playInBackground prop

* fix: fix onDetachedFromWindow

* docs: update docs for pip

* fix(android): sync pip controller with external controller state

- for media session

* fix(ios): fix pip active fn variable reference

* refactor(ios): refactor code

* refactor(android): refactor codes

* fix(android): fix lint error

* refactor(android): refactor android pip logics

* fix(android): fix flickering issue when stop picture in picture

* fix(android): fix import

* fix(android): fix picture in picture with fullscreen mode

* fix(ios): fix syntax error

* fix(android): fix Fragment managed code

* refactor(android): remove redundant override lifecycle

* fix(js): add PIP type definition for codegen

* fix(android): fix syntax

* chore(android): fix lint error

* fix(ios): fix enter background handler

* refactor(ios): remove redundant code

* fix(ios): fix applicationDidEnterBackground for PIP

* fix(android): fix onPictureInPictureStatusChanged

* fix(ios): fix RCTPictureInPicture

* refactor(android): Ignore exception for some device ignore pip checker

- some device ignore PIP availability check, so we need to handle exception to prevent crash

* fix(android): add hideWithoutPlayer fn into Kotlin ver

* refactor(android): remove redundant code

* fix(android): fix pip ratio to be calculated with correct ratio value

* fix(android): fix crash issue when unmounting in PIP mode

* fix(android): fix lint error

* Update android/src/main/java/com/brentvatne/react/VideoManagerModule.kt

* fix(android): fix lint error

* fix(ios): fix lint error

* fix(ios): fix lint error

* feat(expo): add android picture in picture config within expo plugin

* fix: Replace Fragment with androidx.activity

- remove code that uses Fragment, which is a tricky implementation

* fix: fix lint error

* fix(android): disable auto enter when player released

* fix(android): fix event handler to check based on Activity it's bound to

---------

Co-authored-by: jonghun <jonghun@toss.im>
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2025-01-04 12:37:33 +01:00
Kamil Moskała
a735a4a581 chore: update bot feature req message (#4341) 2024-12-23 16:28:25 +01:00
Kamil Moskała
78770d92f5 refactor: make twg badge visible on smaller screens (#4345) 2024-12-21 08:51:45 +01:00
Kamil Moskała
abc4d76099 docs: link tv example (#4342) 2024-12-18 09:24:21 +01:00
Kamil Moskała
16fa20411f docs: update useful projects list (#4337) 2024-12-14 16:15:18 +01:00
Giovanni P.
3da4f1ca97 fix(ios): _paused is updated when video playback pause (#4320) 2024-12-10 19:50:32 +01:00
Kamil Moskała
7b4bd9a016 fix(docs): bump next.js version & fix meta warnings (#4327)
* chore(docs): bump `next` version

* docs: fix meta warnings
2024-12-10 19:46:55 +01:00
proohit
1033c9d4f3 fix(ios): disables subtitles for none and empty track types (#4319) 2024-12-04 15:35:51 +01:00
Chris Wood
d757a44bb1 Docs(README): correct Expo heading link (#4316) 2024-12-03 16:32:13 +01:00
YangJH
64c222df44 chore(android): bump default androidx.activity version from v1.8.2 to v1.9.3 (#4314) 2024-12-01 13:46:56 +01:00
Kamil Moskała
621a80299c fix: hiding poster (#4308)
* fix: hiding poster

* fix: hiding poster

* remove zIndex: 1

* fix: remove showPoster from dependency array
2024-12-01 13:41:03 +01:00
Olivier Bouillet
63c592f7cd fix(android): disable caching on local asset files (#4304) 2024-12-01 13:29:24 +01:00
Olivier Bouillet
569a79c510 chore: release v6.8.2 2024-11-25 21:36:30 +01:00
Olivier Bouillet
f37dc9e33e fix: playback restart without bufferingConfig (#4305) 2024-11-25 21:33:56 +01:00
Olivier Bouillet
dd78241b0d chore: release v6.8.1 2024-11-24 21:25:46 +01:00
Olivier Bouillet
2b7c215e66 Fix(android): restart issue react76 (#4302)
* fix: upgrade to expo 54
* fix: more bufferConfig inside source
- restart issue on react 0.76
- fix constness
- deprecate bufferConfig in root props
- update documentation
2024-11-24 21:19:46 +01:00
Tarık
daaac9740a fix(ios): handle async player access in text track selection (#4293)
* fix(ios): add null check to setSelectedTextTrack for player instance

* Revert "fix(ios): add null check to setSelectedTextTrack for player instance"

This reverts commit 447c83423cdd77b0cfa9cc171b231327a2cf1586.

* fix(ios): ensure strong reference to player during async operation

* fix: linter

* fix: linter formatting

* fix: revert typo
2024-11-22 14:19:09 +01:00
Krzysztof Moch
d934f214f5 chore: release v6.8.0 2024-11-17 15:42:49 +01:00
Islam
d277c5e946 Add support for HLS videos caching on android (#4272)
* Add support for HLS videos caching on android
2024-11-15 21:18:25 +01:00
Kamil Moskała
de81829d73 chore: cleanup ref (#4288)
* refactor: cleanup ref
* fix: video-ref type
* refactor: cleanup code
2024-11-15 21:17:56 +01:00
Olivier Bouillet
681aed0aed chore(sample): fix run of expo sample (#4284)
* chore: fix expo sample tun
* chore: update ios sample files
2024-11-15 21:17:29 +01:00
5fa77c4562 Add react-native-web support (#3958)
Co-authored-by: Kamil Moskała <91079590+moskalakamil@users.noreply.github.com>
2024-11-13 21:19:57 +01:00
Krzysztof Moch
d45300270e chore: add FUNDING.yml 2024-11-08 16:09:49 +01:00
Kamil Moskała
0288d61e46 chore: update docs link (#4242) 2024-11-07 09:28:20 +01:00
Krzysztof Moch
f78a407434 chore(ci): bump actions versions (#4275) 2024-11-06 11:51:27 +01:00
Kamil Moskała
6080e96e82 docs: update drm token generator links (#4263) 2024-10-29 10:31:50 +01:00
Krzysztof Moch
5b3add4e7a chore: update CONTRIBUTING.md 2024-10-26 15:16:41 +02:00
Krzysztof Moch
f850b7a71c chore: update DRM example (#4259) 2024-10-26 14:55:11 +02:00
Olivier Bouillet
3b4bfd3936 fix(android): add helper to avoid type error (#4257)
* fix(android): add helper to avoid type error
2024-10-24 11:43:10 +02:00
Krzysztof Moch
21e2da3bfb chore: update README 2024-10-21 15:22:46 +02:00
Krzysztof Moch
5d532f5982 chore: update README 2024-10-21 15:20:23 +02:00
Krzysztof Moch
324fc2ac65 chore: update examples README 2024-10-20 20:31:30 +02:00
Krzysztof Moch
9eb5502076 chore: rework examples (#4225)
* remove unused examples

* init bare example with test app

* add react-native-video

* add test app suport in expo plugin

* expo plugin: skip keys that are already in pod file

* fix podfile

* add src files

* fix metro config

* finalize react native test app configuration

* init expo example

* remove old examples

* add guide for example

* Add link to examples apps in docs

* adopt bare example to CI tests

* update CI workflows

* CI build lib after node_modules install

* fix examples readme

* fix iOS CI

* Add Example for DRM

* Update examples/README.md

* fix links

* update examples README

* sync example code

* update README
2024-10-20 20:04:02 +02:00
Olivier Bouillet
7c7d83b6e5 chore: release v6.7.0 2024-10-17 21:58:53 +02:00
Olivier Bouillet
7501880062 fix: remove warning and refactor & fix ad workflow (#4235) 2024-10-17 21:56:06 +02:00
Olivier Bouillet
f04b233a40 chore: move minLoadRetryCount into source property (#4233) 2024-10-16 23:41:22 +02:00
Olivier Bouillet
a8d5841c7c fix: ensure aspect ratio from video is handled in a coherent way (#4219)
* fix: ensure aspect ratio from video is handled in a coherent way
2024-10-12 13:51:57 +02:00
Krzysztof Moch
352dfbbc9b fix(android): sideloaded subtitles (#4232) 2024-10-11 22:50:22 +02:00
HyunWoo Lee (Nunu Lee)
78f4f0480d feat(exoplayerview): Migrate ExoPlayerView to kotlin (#4038) 2024-10-11 09:08:13 +02:00
Olivier Bouillet
d86adc52f3 Chore: rework ad props (#4220)
* fix: move ad configuration in source
2024-10-10 23:53:39 +02:00
Olivier Bouillet
9a3fcda3b8 feat: add setSource API function fix ads playback (#4185)
* feat: add setSource API function fix ads playback
2024-10-10 22:59:41 +02:00
Olivier Bouillet
4c9db2845b chore: Add react compiler workaround (#4227) 2024-10-10 07:24:11 +02:00
Wojciech Ogrodowczyk
2c19a4770d fix(iOS): pause video on end reached & don't remove listeners (#4218)
This fixes an issue when seeking back to the beginning results in
no `onProgress` events being fired.
2024-10-07 14:32:35 +02:00
Seyed Mostafa Hasani
d1883a7e00 feat(android): add settings button to control video playback speed (#4211) 2024-10-05 18:34:25 +02:00
Kamil Moskała
d81e6ea31e docs: highlight maintainer services & update twg site links (#4214)
* docs: add cta block

* chore: update twg urls
2024-10-05 11:21:50 +02:00
Seyed Mostafa Hasani
74fb44ddcf docs: update the TextTrackType enum (#4216)
* chore: update the document

* chore: update the document
2024-10-05 11:20:54 +02:00
Krzysztof Moch
0820f8167f chore: release v6.6.4 2024-10-03 08:32:35 +02:00
Paul Rinaldi
40872f5ea7 fix(android) Use startForegroundService and do not delete the notification channel until onDestroy (#4105)
See https://developer.android.com/develop/background-work/services/foreground-services\#fgs-prerequisites
    See rationale here https://stackoverflow.com/questions/45525214/are-there-any-benefits-to-using-context-startforegroundserviceintent-instead-o
    Deleting the notification channel while the foreground service is still running is not permitted.
2024-10-03 08:23:17 +02:00
Seyed Mostafa Hasani
149924ffcb feat(android): add live video label configuration (#4190) 2024-10-02 23:37:18 +02:00
Krzysztof Moch
82dc4cf3a0 chore: release v6.6.3 2024-09-29 21:24:47 +02:00
Kamil Moskała
279cc0e5ed feat(android): allow to hide specific controls (#4183)
* feat(android): enable to hide specific controls

* fix: ts

* fix: lint

* docs: update `controlsStyles` docs
2024-09-29 20:51:02 +02:00
Olivier Bouillet
3ecf324bb3 fix(android): bad rotation handling (#4205) 2024-09-29 20:48:44 +02:00
Olivier Bouillet
0c6b47f42c docs: remove desugaring section as no more need on media3 1.4.1 (#4206) 2024-09-29 20:46:11 +02:00
Krzysztof Moch
b11f05f175 fix(tvos): typo (#4204)
* fix(tvos): typo

* lint
2024-09-28 16:39:09 +02:00
Krzysztof Moch
724b32b434 chore(infra): hide previous bot comments (#4191)
* chore(infra): hide prev comments from bot

* fix comment format
2024-09-28 14:52:23 +02:00
Lukas
c81eea54d8 fix(docs): invalid URLs in updating section (#4201) 2024-09-26 20:23:26 +02:00
Olivier Bouillet
ae82c83eef fix(ios): Add safety checks and remove some of the ! in types declaration (#4182) 2024-09-22 18:41:25 +02:00
Krzysztof Moch
17dc2c064f chore: release v6.6.2 2024-09-20 18:54:07 +02:00
Krzysztof Moch
0e4c95def9 feat(iOS): rewrite DRM Module (#4136)
* minimal api

* add suport for `getLicense`

* update logic for obtaining `assetId`

* add support for localSourceEncryptionKeyScheme

* fix typo

* fix pendingLicenses key bug

* lint code

* code clean

* code clean

* remove old files

* fix tvOS build

* fix errors loop

* move `localSourceEncryptionKeyScheme` into drm params

* add check for drm type

* use DebugLog

* lint

* update docs

* lint code

* fix bad rebase

* update docs

* fix crashes on simulators

* show error on simulator when using DRM

* fix typos

* code clean
2024-09-20 17:46:10 +02:00
Olivier Bouillet
c96f7d41f3 chore(sample): fix default track identification and add audio tracks selection option (#4184) 2024-09-20 16:26:20 +02:00
Olivier Bouillet
6fedca0df7 chore(sample): upgrade sample expo version (#4179) 2024-09-19 13:51:24 +02:00
Olivier Bouillet
892efdd3ab chore: release v6.6.1 2024-09-18 22:51:28 +02:00
Olivier Bouillet
7d43d5d3da fix(ios): fix side loaded text track management (#4180) 2024-09-18 22:43:25 +02:00
Krzysztof Moch
41d3da9146 chore: release v6.6.0 2024-09-18 21:17:15 +02:00
Olivier Bouillet
835186a321 fix(JS): improve loader api to allow function call instead of component (#4171) 2024-09-17 15:58:47 +02:00
Olivier Bouillet
7f6b500c82 fix(android): ensure maxbitrate & selectedVideoTrack interact correctly (#4155) 2024-09-17 15:57:26 +02:00
Seyed Mostafa Hasani
1ef2b3a977 chore(android): add null check for id of videoFormat (#4174)
* chore(android): add null check for id of videoFormat

* chore: null check videoFormat.id on onBandwidthSample

* fix: PR feedback

* fix: linter error

* chore: update trackId fallback value
2024-09-17 14:11:02 +02:00
Amin Meshk
0538b3b468 fix(expo-plugin): add check for existing service in AndroidManifest for notification controls (#4172)
* fix: add check for existing VideoPlaybackService in AndroidManifest

* Update src/expo-plugins/withNotificationControls.ts

Co-authored-by: Seyed Mostafa Hasani <seyedmostafahassani@gmail.com>

* fix: comment spacing

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
Co-authored-by: Seyed Mostafa Hasani <seyedmostafahassani@gmail.com>
2024-09-17 14:09:39 +02:00
Seyed Mostafa Hasani
e57c7bda5d feat(android): upgrade dependencies / media3 1.4.1 / androidxCore to 1.13.1 / androidxActivity 1.8.2 (#4173) 2024-09-16 13:54:51 +02:00
Olivier Bouillet
24d90e9ec8 chore(android): move contentStartTime into source prop (#4160) 2024-09-14 19:53:54 +02:00
Krzysztof Moch
b74cb59602 chore(android): add null checks (#4168) 2024-09-14 15:20:50 +02:00
Olivier Bouillet
84a27f3d9f fix: refactor side loaded text tracks management (#4158)
* fix: refactor side loaded text tracks management

More textTracks in source.
android/ios: ensure text tracks are not selected by default
android/ios make textTrack field not nullable
clean up doc
check compatibility with the old api
Add comments on deprecated JS apis
Apply API change on basic sample

* chore: fix linter

* fix(ios): fix build with caching & remove warnings
2024-09-13 10:50:33 +02:00
Olivier Bouillet
7118ba6819 chore(ios): remove some warnings (#4159) 2024-09-13 10:49:43 +02:00
Krzysztof Moch
2c1fc964bf fix(visionOS): remove unsupported apis (#4154) 2024-09-09 15:46:53 +02:00
Olivier Bouillet
b2fd8d62a1 fix(android): ensure pause is well tken in account after onEnd (#4147)
Issue linked to: https://github.com/TheWidlarzGroup/react-native-video/issues/2690
This original issue is not reproduced
2024-09-06 15:11:33 +02:00
Olivier Bouillet
809a730198 fix(ios): ensure onBandwidthUpdate is reported only when value change (#4149)
* fix(ios): ensure onBandwidthUpdate is reported only when value change

* chore: fix PodFile.lock
2024-09-06 15:11:12 +02:00
Olivier Bouillet
e18769ab3a fix(sample): remove warning on ios with NavigationBar (#4148)
* fix(sample): remove warning on ios with NavigationBar
2024-09-06 09:45:24 +02:00
Seyed Mostafa Hasani
4a2beaa147 chore(android): remove onBackPressed function in FullScreenPlayerView (#4049)
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-09-06 00:12:22 +02:00
Gunnar Carlson
bee4123402 fix(ios): losing subtitle selection on foreground (#3707) 2024-09-05 10:41:27 +02:00
Olivier Bouillet
b871d937a3 chore: release v6.5.0 2024-09-04 11:47:49 +02:00
Kamil Moskała
b66d2fe146 docs: add ios platform for onBandwidthUpdate callback (#4145) 2024-09-04 11:08:49 +02:00
whdudtod1273
22c21ad249 feat: Correct isBehindLiveWindow Error Handling (#4143)
* feat: Correct isBehindLiveWindow Error Handling

---------

Co-authored-by: young <young@afreecatv.com>
2024-09-04 09:54:08 +02:00
Olivier Bouillet
9707081ab9 Chore/rework fullscreen configuration (#4142)
* feat(android): handle navigation bar status in full-screen mode
* chore: update default value of prop
* chore(android): rework fullscreen configuration

---------

Co-authored-by: mostafahasani <seyedmostafahassani@gmail.com>
2024-09-04 09:53:30 +02:00
Olivier Bouillet
d6bae3cd07 fix(ios): fix onBandwidth update event (old ios api is deprecated and doens't work) (#4140) 2024-09-03 15:33:43 +02:00
Seyed Mostafa Hasani
c51c061f43 chore(android): clean up ReactExoplayerView class (#4141) 2024-09-03 11:16:20 +02:00
Seyed Mostafa Hasani
8b8ebe9410 fix(android): show the status bar and navigation bar after exiting full-screen mode (#4112)
* fix(android):  show the status bar and navigation bar after exiting full-screen mode
2024-09-03 08:59:24 +02:00
Olivier Bouillet
308447a5ba Fix/track selection by title (#4129)
* chore(sample): make track selection by title possible

* fix(android): fix test for track selection by title
2024-09-02 19:10:39 +02:00
Olivier Bouillet
89df9d69ff fix(ios): ensure we don't disable tracks when not necessary (causes black screen) (#4130) 2024-09-02 19:08:27 +02:00
Olivier Bouillet
fbe570d62f Fix/allow text track selection by index (#4124)
* fix(ios): ensure behavior is correct with empty text track list
* fix(ios): add index to text tracks reported
2024-09-02 17:01:39 +02:00
Olivier Bouillet
2fa6c43615 fix(android): add subtitleStyle.subtitlesFollowVideo prop to control subtitles positionning (#4133)
* fix(android): add subtitleStyle.subtitlesFollowVideo prop to control subtitles positionning
* docs: add new prop description
* docs: add supported platform for subtitleStyle
* chore: use constructor instead of parse
2024-09-02 16:13:06 +02:00
Olivier Bouillet
688d98d68f fix(tvos): fix build (and update sample) (#4134)
* fix(tvos): fix build (and update sample)
2024-09-02 15:42:51 +02:00
Olivier Bouillet
3a32d67087 fix(ios): ensure behavior is correct with empty text track list (#4123)
* fix(ios): ensure behavior is correct with empty text track list
2024-09-02 15:40:38 +02:00
Olivier Bouillet
7a2b4014f4 fix(sample): update dependencies to fix local asset playback (#4121)
* fix(sample): align dependencies and fix local asset playback
2024-09-02 15:40:10 +02:00
Olivier Bouillet
fb3c0da6af chore(sample): additionnal sample cleanup (#4122)
* chore: move MultiValueControl & toggleControl to component
* fix(sample): fix import / export to avoid circular deps
* chore(sample): fix warning
2024-08-31 18:32:32 +02:00
Błażej Lewandowski
451806c547 fix(expo-plugin): adding bg mode if none exist yet (#4126) 2024-08-31 15:10:52 +02:00
Guy Haguy
703ed43996 feat: add ads localize (#4113)
* add prop adLanguage; add docs

* add native code ios&android for adLanguage props

* add missing function to adsLoader and imafactory

* Update docs/pages/component/ads.md

Language correction

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

---------

Co-authored-by: Guy <guyha@reshet.tv>
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-08-29 12:30:05 +02:00
Wojciech Ogrodowczyk
9c38d9f4ef fix(ios): Add handler for Earpods play/pause command (#4116) 2024-08-29 12:28:27 +02:00
Olivier Bouillet
0576eacfdd fix(VisionOS): do not access to isExternalPlaybackActive on VisionOS (#4109) 2024-08-29 12:27:07 +02:00
Kamil Moskała
24c99f03b9 chore: add space to validator text (#4111) 2024-08-26 17:25:15 +02:00
Krzysztof Moch
ffa5044e23 infra: add posibility to skip issue validation 2024-08-23 10:53:51 +02:00
Krzysztof Moch
7db7024cb3 fix: set does not have find method (#4110) 2024-08-22 11:22:11 +02:00
Subin Yang
ca795f298a feat(android): Support Common Media Client Data (CMCD) (#4034)
* feat(VideoNativeComponent.ts): add support for cmcd configuration in VideoSrc type to enable cmcd feature on android
feat(video.ts): introduce CmcdMode enum and CmcdConfiguration type to define cmcd configuration options

* feat(Video.tsx): add support for CMCD configuration in Video component to handle Content Management and Delivery (CMCD) headers for Android platform.

* feat(CMCDProps.kt): add CMCDProps class to handle CMCD related properties and parsing logic for React Native module

* feat(CMCDConfig.kt): add CMCDConfig class to handle CMCD configuration for ExoPlayer with support for custom data and configuration options.

* feat(ReactExoplayerViewManager.java): add support for CMCD configuration in ReactExoplayerViewManager to enable Content Management and Control Data (CMCD) for better video playback optimization.

* feat(ReactExoplayerView.java): add support for setting CmcdConfiguration.Factory to customize CMCD configuration for media playback

* feat(Source.kt): add support for CMCD properties linked to the source to enhance functionality and data handling

* docs(props.mdx): add documentation for configuring CMCD parameters in the component, including usage examples and default values

* refactor(ReactExoplayerViewManager.java): remove unused PROP_CMCD and prevCmcdConfig variables to clean up code and improve readability

* refactor(Video.tsx): simplify cmcd configuration logic for better readability and maintainability

* docs(props.mdx): improve props documentation for clarity and consistency
feat(props.mdx): add definitions for CmcdMode enum and CmcdData type to enhance understanding of CMCD data structure and usage

* refactor(CMCDProps.kt): refactor CMCDProps class to data class for improved readability and immutability
-  update CMCDProps class to use List instead of Array for properties

* refactor(Video.tsx): refactor createCmcdHeader function to improve code readability and reduce duplication

* fix(CMCDProps.kt): remove import statement for CmcdConfiguration

* feat(ReactExoplayerView.java): add support for CMCD configuration in ReactExoplayerView component
feat(ReactExoplayerViewManager.java): remove redundant CMCD configuration logic from ReactExoplayerViewManager to simplify code and improve maintainability

* fix(Video.tsx): merge _cmcd memo into src memo for optimization
2024-08-22 10:47:51 +02:00
Kamil Moskała
65faba312d fix(android): hide surfaceView for loading time when shutter is hidden (#4060)
* fix(android): hide surfaceView for loading time when shutter is hidden

* fix: hide/show surface view without casting
2024-08-22 10:37:58 +02:00
Błażej Lewandowski
b05201a9fa fix crash on source change, if the app was put in bg beforehand (#4074) 2024-08-22 10:30:23 +02:00
Faustino Kialungila
0a1085ce03 fix(ios): build fail due to an unwrapped value (#4101)
* fix: ios build crash due to AVMediaSelectionGroup not being unwrapped

* fix: use shorthand optional binding

* fix: disable swiftlint shorthand_optional_binding for guard let

* fix(ios): use guard do catch

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>

---------

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
2024-08-21 10:26:32 +02:00
ashlyWeiting
41e2bed6b3 feat(android): support hiding Exoplayer video duration on android (#4090)
* feat: support for hiding duration on Android

* docs: add hideDuration property to control styles documentation
2024-08-21 10:05:40 +02:00
Krzysztof Moch
4611284247 infra: add issue validator and stale action (#4061)
* infra: update stale action

* infra: add issue validator

* code clean

* add missing labels

* fix reproduction check

* code clean

* add version check

* fix version check

* add missing label

* add note to version message

* code clean

* update stale message
2024-08-21 09:55:45 +02:00
Olivier Bouillet
1b691f8e81 chore: release v6.4.5 2024-08-17 15:37:45 +02:00
Seyed Mostafa Hasani
7e222e8fc4 fix(android): resolve a release issue with DefaultDashChunkSource (#4097) 2024-08-17 15:33:37 +02:00
Krzysztof Moch
736594ed23 chore: release v6.4.4 2024-08-15 16:13:00 +02:00
Seyed Mostafa Hasani
b7d1cabf72 refactor(android): migrate DefaultDashChunkSource to Kotlin (#4078)
* refactor(android): migrate DefaultDashChunkSource to Kotlin
2024-08-13 09:58:31 +02:00
Paul
c6ae17e41d fix(ios): remove resume logic in notification seek closure (#4068) 2024-08-12 13:58:40 +02:00
Seyed Mostafa Hasani
cd41a1b234 chore(doc): update document (props & method) (#4072)
* chore: update method document

* chore: update method document

* chore: update method document

* fix: PR feedback

* chore: update description for controls prop
2024-08-12 13:55:10 +02:00
Krzysztof Moch
899bb822a5 fix(android): build warnings (#4058) 2024-08-07 14:39:41 +02:00
Krzysztof Moch
6c03d0a700 infra: update feature request form (#4065) 2024-08-07 14:33:16 +02:00
Krzysztof Moch
38aa2b057a fix(ios): override source metadata with custom metadata (#4050)
* fix(ios): override source metadata with custom metadata

* lint code
2024-08-05 11:59:49 +02:00
Seyed Mostafa Hasani
af0302b1b7 fix(android): return the value as a float for the getCurrentPosition function (#4054)
* fix(android): return value for getCurrentPosition function

* fix: remove additional casting
2024-08-05 11:58:33 +02:00
Seyed Mostafa Hasani
74c6dd6279 refactor(android): migrate ReactExoplayerViewManager to Kotlin (#4011)
* Rename .java to .kt

* refactor(android): migrate ReactExoplayerViewManager to Kotlin

* fix: lint error

* refactor: setPreventsDisplaySleepDuringVideoPlayback function
2024-08-02 10:52:08 +02:00
Mickael Lecoq
22cfd6cead fix(android): viewType is ignored when its value is ViewType.TEXTURE (#4031) 2024-08-02 10:50:27 +02:00
Błażej Lewandowski
08a57a3ba3 fix(ios): metadata update race (#4033)
* fix metadata update race

* merge nowPlayingInfo instead of overriding it

* update method name

* fix code style
2024-08-02 10:49:52 +02:00
EETVApps
2348c5e42c fix(ios): updated getLicense call to work with new syntax, and fixed spelling error (#4014) (#4042)
* fix(android): updated getLicense call to work with new syntax, and fixed spelling error (#4014)

* fix(android): corrected error with my refactor (#4014)

* fix(android): Removed trailing space (#4014)

* fix(ios): Refactored following review (#4014)

* fix(ios): Lint tidy (#4014)

* fix(ios): Removed incorrect semi-colon (#4014)

* fix(ios): Fixed more lint errors (#4014)

---------

Co-authored-by: Darren Taft <darren.taft@bt.com>
2024-08-02 10:42:20 +02:00
Krzysztof Moch
5abfb0d448 chore: release v6.4.3 2024-07-24 10:12:58 +02:00
Seyed Mostafa Hasani
fb1d6bdef7 fix: index of the selected track (#4012) 2024-07-24 10:08:37 +02:00
Seyed Mostafa Hasani
6189080c9a feat(android): add error handling for Kotlin version (#4018)
* feat(android): add error handling for Kotlin version mismatch

* fix: lint error

* refactor: use variables from gradle file

* chore: downgrade required Kotlin version

* refactor: check Kotlin version

* refactor: kotlin variables in build.gradle

* refactor: kotlin variables in build.gradle

* chore(doc): update document

* chore: add dependency to build.gradle for a specific version of react-native

* fix: remove additional dependency
2024-07-24 10:07:19 +02:00
Kamil Moskała
adbd06e2df feat: add ability to define poster props as Image type and render poster as custom component (#3972) 2024-07-22 22:38:35 +02:00
Olivier Bouillet
1ee5811c8e fix(android): app crash at boot with old arch (#4022)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: ensure player doesn't start when view is unmounted

* Fix/ensure view drop stop playback startup (#3875)

* fix: ensure player doesn't start when view is unmounted

* chore: fix app boot due to missing registered events

* chore: fix linter

* chore: fix build

* fix: handle code review feedbacks

* chore: revert previous change to handle only necessary fix
2024-07-22 10:56:58 +02:00
Olivier Bouillet
ffbc977ff9 fix(sample): boot failure on emulator (#4016)
* fix: disable coreLibraryDesugaringEnabled by default
2024-07-18 22:22:58 +02:00
lovegaoshi
9f382163d8 fix(android): resize mode cover calculation (#4010) 2024-07-18 10:53:50 +02:00
Krzysztof Moch
ab7e02e453 fix(android): fix backward compatibility (#4020) 2024-07-18 10:51:57 +02:00
Seyed Mostafa Hasani
76d5e93c17 refactor(android): migrate BecomingNoisyListener interface to Kotlin (#3996)
* Rename .java to .kt

* refactor(android): migrate BecomingNoisyListener interface to Kotlin

* remove: java file (AudioBecomingNoisyReceiver.java)
2024-07-18 09:11:07 +02:00
Igor Malgrab
2f70c02cdc feat(android): set originalFitsSystemWindows on fullscreen open (#4013) 2024-07-17 12:29:38 +02:00
Kamil Moskała
7611da155f chore(sample): refactor sample code to follow rn best practices (#3990)
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-07-15 23:29:23 +02:00
Krzysztof Moch
8ef2df1bac chore: release v6.4.2 2024-07-15 12:52:15 +02:00
Krzysztof Moch
5cd5e5efe7 fix(android): resolve compatibility issue 2024-07-15 12:29:57 +02:00
Seyed Mostafa Hasani
0104fbbbf3 chore(android): migrate FullScreenPlayerView to Kotlin (#3969)
* Rename .java to .kt

* chore(android): migrate FullScreenPlayerView to Kotlin

* refactor: onBackPressed function

* refactor: fullScreen button function

* fix: lint error

* chore: revert onBackPressed function
2024-07-15 12:04:49 +02:00
Seyed Mostafa Hasani
4b8d09e61f fix(android): exit fullscreen mode after finishing video playback (#3978) 2024-07-15 12:04:19 +02:00
Seyed Mostafa Hasani
5abc223db8 refactor(android): migrate AudioOutput to Kotlin (#3993)
* Rename .java to .kt

* refactor(android): migrate AudioOutput to Kotlin

* fix: lint error

* refactor: rename of variables
2024-07-15 12:01:32 +02:00
Seyed Mostafa Hasani
df9ffde5fa refactor(android): migrate ReactExoplayerConfig to Kotlin (#3994)
* Rename .java to .kt

* refactor(android): migrate ReactExoplayerConfig to Kotlin
2024-07-15 11:58:07 +02:00
Seyed Mostafa Hasani
4e7c64e707 refactor(android): migrate ReactExoplayerLoadErrorHandlingPolicy to Kotlin (#3995)
* Rename .java to .kt

* refactor(android): migrate ReactExoplayerLoadErrorHandlingPolicy to Kotlin
2024-07-15 11:30:53 +02:00
Seyed Mostafa Hasani
bed9b8e4d9 refactor(android): migrate AudioBecomingNoisyReceiver to Kotlin (#3997)
* Rename .java to .kt

* refactor(android): migrate AudioBecomingNoisyReceiver to Kotlin

* fix: lint error

* chore: initialize appContext with context.applicationContext
2024-07-15 10:17:07 +02:00
YangJH
79c30767fc fix(ts): make multiDrm prop optional type (#3999) 2024-07-15 10:15:44 +02:00
Kamil Moskała
0a55ace0ca fix(android): handle aspect ratio for rotated videos (#4000) 2024-07-15 10:15:29 +02:00
YangJH
f82268be1b fix(android): fix onVideoLoad event field key (#4001)
* fix(android): fix onVideoLoad event naturalSize field key

* fix(android): fix onVideoLoad event currentTime field key
2024-07-15 10:14:56 +02:00
YangJH
05623c9f54 fix(android): fix android notification controller order (#4002) 2024-07-15 10:14:31 +02:00
Kamil Moskała
38bcfa2f6a chore(example): update MultiValueControl types (#4003) 2024-07-15 10:13:21 +02:00
Seyed Mostafa Hasani
39cf477ceb fix(example/basic): select resizeMode (#3989)
* fix(example/basic): select resizeMode

* chore: refactor the onResizeModeSelected function
2024-07-12 13:42:36 +02:00
Krzysztof Moch
21e78ea2c1 chore: release v6.4.1 2024-07-12 10:53:41 +02:00
YangJH
c2084c2ace refactor: internal refactor for prepare new arch (#3980)
* chore(js): fix typo

* refactor(js): refactor type code for codegen

* refactor(js): refactor Video component

- parse shutterColor value within JS
- remove internal fullscreen state

* chore(js): add deprecation warning comment

* fix(js): fix return type

* fix(js): fix import path

* refactor(android): apply changed API for new arch

* refactor(ios): apply changed API for new arch

* fix(ios): fix wrong name

* refactor: refactor VideoDecoderProperties

- rename and add wrapper

* refactor(android): Code fixes for backward compatibility with Kotlin
2024-07-12 10:27:42 +02:00
Krzysztof Moch
de8ade0620 fix: expo plugin export (#3992) 2024-07-11 22:52:10 +02:00
Krzysztof Moch
f05d190ae6 chore: release v6.4.0 2024-07-11 13:08:45 +02:00
Krzysztof Moch
40a72825e6 fix(ios): remove pip check for other platforms (#3991)
* fix(ios): remove pip check for other platforms

* Update ios/Video/RCTVideo.swift
2024-07-11 12:54:54 +02:00
Krzysztof Moch
111a5d2163 feat: add isSeeking to onPlaybackStateChanged (#3899)
* feat: add `isSeeking` to `onPlaybackStateChanged``

* refactor `onSeek` event emit logic

* fix rebase
2024-07-11 10:08:36 +02:00
Seyed Mostafa Hasani
b25e43ee79 chore(android): migrate DataSourceUtil to Kotlin (#3984)
* Rename .java to .kt

* chore(android): migrate DataSourceUtil to Kotlin

* fix: type issue in DataSourceUtil

* fix: lint error

* chore: refactor DataSourceUtil with object & @JvmStatic

* fix: lint error
2024-07-11 10:07:05 +02:00
Seyed Mostafa Hasani
452e42f1dd chore(android): migrate AspectRatioFrameLayout to Kotlin (#3985)
* Rename .java to .kt

* chore(android): migrate AspectRatioFrameLayout to Kotlin

* chore: refactor setter and getter of class

* fix: use field
2024-07-11 10:06:22 +02:00
Olivier Bouillet
66dcf32b56 refactor: move view type and drm in source (#3867)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* chore(android): refactor DRM props into a dedicated class

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java

* chore: fix linter

* fix: ensure drm prop is correctly cleaned

* feat(android): move viewType (secure texture) & drm inside the source

The origianl behavior has been kept for interoperability, but marked as deprecated in doc

* chore: fix linter

* chore(ios): move drm prop in source like on android

* chore: fix linter

* chore: clean log

* fix: allow to disable secure View

* chore: fix viewType resolution (source value was not handled)

* chore: use contentDeepEquals instead of manual checks

* chore: fix linter

* fix: ensure player doesn't start when view is unmounted

* Fix/ensure view drop stop playback startup (#3875)

* fix: ensure player doesn't start when view is unmounted

* chore: revert change

* chore: add warning in case of invalid Surface configuration

* chore: code clean

* fix: simplify surface management

* chore: restore previous code

* chore: fix typo

* chore: code cleanup

* feat(android): add multiDrm flag support

* docs: update docs

* chore: fix ios build

* chore: fix deprecated declaration

---------

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
2024-07-10 12:17:22 +02:00
Krzysztof Moch
08f6caa645 feat: add expo plugins (#3933)
* feat: add expo plugins

* add export

* fix import

* fix bugs

* build `lib` to `CommonJS`

* restore `build.gradle`

* remove plugin tmp

* add expo plugin for ios caching

* add docs for expo plugin

* fix expo plugin export

* fix docs
2024-07-10 11:49:13 +02:00
Kamil Moskała
25c74e0534 docs: update deprecated tool (#3982) 2024-07-10 10:20:54 +02:00
Seyed Mostafa Hasani
1728373d11 chore(android): migrate DefaultReactExoplayerConfig to Kotlin (#3983)
* Rename .java to .kt

* chore(android): migrate DefaultReactExoplayerConfig to Kotlin
2024-07-10 10:20:16 +02:00
Krzysztof Moch
ccffcfd709 fix(ios): don't pause playback when entering background (#3973) 2024-07-08 13:41:21 +02:00
Seyed Mostafa Hasani
a3ecc0108c chore(example/basic): refactor state variable (#3949)
* refactor: basic example from class component to functional component

* refactor: toast component path

* refactor: bufferConfig prop

* refacotr: import component path

* fix: seekbar issue on iOS

* refactor: state variable

* chore: refactor overlay component according to new state structure
2024-07-08 09:54:32 +02:00
YangJH
7562669fd6 feat(android): Bump default media3 version from v1.1.1 to v1.3.1 (#3977) ⚠️ need targetSdkVersion 34 2024-07-06 22:16:07 +02:00
yungblud
1d6fb29753 feat: modified Fabric example android build.gradle for resolving build issue (#3976) 2024-07-06 10:19:24 +02:00
Seyed Mostafa Hasani
01a00b12ae chore: upgrade react-native & expo version in the basic example app (#3964)
* chore: upgrade react-native & expo version

* chore: update Podfile.lock
2024-07-04 14:02:05 +02:00
YangJH
3c9b1b571a refactor(android): migrate VideoEventEmitter to Kotlin (#3962)
* refactor(android): migrate VideoEventEmitter to Kotlin

* feat(android): apply rewritten EventEmitter's functions

* refactor(android): remove duplicated code

* fix(android): fix lint error

* fix(android): fix event name value

* refactor(android): rename of event constants for Fabric

- https://github.com/facebook/react-native/blob/v0.74.3/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java#L136-L138
2024-07-04 14:01:28 +02:00
YangJH
7def3ac387 fix(ios): fix fullscreen view controller ANR (#3952)
- ANR occurred setFullscreen(true) when view controller presenting
2024-07-04 12:46:42 +02:00
yungblud
de6e71966a chore(example/fabric): bump up fabric example android deps (#3957)
* feat: 🔥 version bump react native for ios FabricExample

* feat:  copied newly created basic example TS files

* feat: 🔥 bump up native layers of FabricExample android app

* feat: 🔥 turn off buildDir setting in android/build.gradle for correct Fabric codegen dir location

* Revert "feat: 🔥 turn off buildDir setting in android/build.gradle for correct Fabric codegen dir location"

This reverts commit 62ec8d739b1f21906972eb935fc934e87294a8e6.
2024-07-04 12:44:43 +02:00
YangJH
76c6329110 refactor(ios): refactor NowPlayingInfoCenerManager.swift (#3968)
* refactor(ios): refactor NowPlayingInfoCenerManager

* fix(ios): fix lint error
2024-07-04 12:11:12 +02:00
Seyed Mostafa Hasani
530686ca82 fix(android): build issue on the latest react-native version (#3963) 2024-07-02 22:40:25 +02:00
yungblud
df29c2310d feat(fabric): updated ios podspec for implementing new architecture (#3961)
* chore: 🔥 updated FabricExample/Gemfile.lock (bundle install)

* feat: updated react-native-video.podspec for implementing new architecture

* feat: updated FabricExample/ios/Podfile.lock (pod install)

* chore: updated examples/basic Gemfile.lock (bundle install)

* chore:  updated examples/basic ios Podfile.lock (pod install)
2024-07-02 10:56:14 +02:00
YangJH
3f11894c2b fix(android): fix wrong module name (#3959) 2024-06-30 18:20:08 +02:00
Seyed Mostafa Hasani
702a0d9d32 refactor(android): migrate ReactVideoPackage to Kotlin (#3955) 2024-06-30 17:34:28 +02:00
YangJH
99585987ea refactor(android): migrate VideoDecoderPropertiesModule to Kotlin (#3954) 2024-06-30 17:32:30 +02:00
yungblud
e5a2ee3bd3 feat: bump up fabric example react-native iOS (#3951)
* feat: 🔥 version bump react native for ios FabricExample
* feat:  copied newly created basic example TS files
2024-06-30 17:20:28 +02:00
EETVApps
322d7e993d fix(android): added setAllowChunklessPreparation to HlsMediaSource.Factory to allow build success on projects without HLS support (#3948) (#3950)
Co-authored-by: Darren Taft <darren.taft@bt.com>
2024-06-28 11:43:55 +02:00
Seyed Mostafa Hasani
d4f1648681 refactor: basic example from class component to functional component (#3934)
* refactor: basic example from class component to functional component

* refactor: toast component path

* refactor: bufferConfig prop

* refacotr: import component path

* fix: seekbar issue on iOS
2024-06-28 11:33:10 +02:00
Olivier Bouillet
b431d09e2f chore(android): rework view type (#3940) 2024-06-27 11:58:06 +02:00
Bartosz Kaszubowski
6e1337689a chore: update homepage field in package.json (#3945) 2024-06-26 11:29:10 +02:00
Olivier Bouillet
a7d834a80a feat(android): allow building exoplayer from source (#3932)
* feat(android): allow to build from source code

* fix(android): allow to build plugin from source
2024-06-25 13:20:12 +02:00
Olivier Bouillet
91d27a6009 feat: add plugins management (#3909) 2024-06-25 08:55:32 +02:00
Olivier Bouillet
3cfb96adb9 Chore(docs): fix typo (#3938)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: ensure player doesn't start when view is unmounted

* Fix/ensure view drop stop playback startup (#3875)

* fix: ensure player doesn't start when view is unmounted

* chore(docs): fix typo
2024-06-24 10:59:33 +02:00
Olivier Bouillet
cfa5984df7 chore: release v6.3.0 2024-06-22 11:17:24 +02:00
Ash Mish
c2ce372bcf feat(android): add onControlsVisiblityChange (#3925)
* Adding onControlsVisiblityChange for Android
2024-06-22 11:15:21 +02:00
Olivier Bouillet
104ee703ba fix(android): avoid crash multiplayer with notification (#3931) 2024-06-22 10:55:40 +02:00
Paul
91751abc87 feat(ios): add live key to now playing dict to decorate when livestream playing (#3922)
Co-authored-by: Paul <paul@dnconsulting.dev>
2024-06-21 22:33:24 +02:00
Olivier Bouillet
c1c7a056f1 chore: move sample to expo (#3880)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: ensure player doesn't start when view is unmounted

* chore: move basic sample to expo

* Update examples/basic/package.json

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>

* chore: use last expo version

* chore: fix podfile

---------

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
2024-06-20 11:58:55 +02:00
Olivier Bouillet
856b1dd58b fix(android): use UI thread to pause when lost audio focus (#3916) 2024-06-20 11:50:56 +02:00
Olivier Bouillet
264b57aa2e fix(android): allow chunk less preparation (#3913) 2024-06-20 11:19:35 +02:00
Olivier Bouillet
84bb910d10 fix(JS): safety check on resolve uri (#3915) 2024-06-20 11:19:18 +02:00
Olivier Bouillet
3d6bc9409c fix(ios): crash on ads after leaving the app (#3911) 2024-06-20 11:18:27 +02:00
Justin Emery
dc2a2ab863 fix(typescript): type checks on selectedTextTrack, selectedAudioTrack, selectedVideoTrack (#3910)
* Fix type checks on selectedTextTrack and selectedAudioTrack

* Improve logging

* Rename variable for clarity

* Apply same fix to selectedVideoTrack

* Lint fixes

* Update src/Video.tsx

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

* Update src/Video.tsx

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

* Update src/Video.tsx

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

---------

Co-authored-by: Krzysztof Moch <krzysmoch.programs@gmail.com>
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-06-19 11:49:42 +02:00
Mihai
87e7f42042 chore(docs): fix sample app link (#3918) 2024-06-19 11:26:17 +02:00
Krzysztof Moch
098a754110 fix(android): show controls in notification on older androids (#3886) 2024-06-12 11:54:45 +02:00
Krzysztof Moch
2d793dbde1 fix(ios): missing notification controls when enabled from start (#3898) 2024-06-12 11:54:22 +02:00
Olivier Bouillet
cfb5b1cd31 Chore(android): refactor drm props (#3846) 2024-06-10 22:42:18 +02:00
Seyed Mostafa Hasani
3a4a13011a chore: add setFullScreen to component's ref (#3855)
* chore: add setFullScreen to component's ref and remove presentFullscreenPlayer & dismissFullscreenPlayer
2024-06-10 22:41:26 +02:00
Olivier Bouillet
016fca8a2a chore: release v6.2.0 2024-06-07 17:42:00 +02:00
Olivier Bouillet
c2cd7529fd fix(android): fix null pointer exception at playback start with item metadata (#3879)
* fix(android): fix null pointer exception at playback start with item metadata
2024-06-07 15:06:49 +02:00
Olivier Bouillet
d4a8c24f65 feat(android): allow chunckless preparation (#3882)
* feat(android): add a way to disable chuncklessPreparation on HLS
2024-06-07 14:06:46 +02:00
Dibyendu Das
ac0a9c3e3a fix(ios): Implicit use of 'self' in closure - use 'self.' to make capture semantics explicit (#3764) (#3881)
* Fix/ensure view drop stop playback startup (#3875)

* fix: ensure player doesn't start when view is unmounted

* Patch commit to fix the error:
  Implicit use of 'self' in closure; use 'self.' to make capture semantics explicit

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-06-07 14:04:00 +02:00
Krzysztof Moch
ff1e24aaad fix: ensure view drop stop playback startup (#3875) 2024-06-07 13:54:14 +02:00
Olivier Bouillet
7133c96cac fix: ensure progress is sent before onEnd callback (#3872)
* fix: add onProgress event before onEnd
2024-06-03 12:13:52 +02:00
Krzysztof Moch
adedc052f0 chore: update bug report template (#3873)
* chore: update bug report template

* update description
2024-06-03 12:07:51 +02:00
Olivier Bouillet
a604cd750a chore(sample): clean log in sample (#3868) 2024-05-31 23:32:20 +02:00
Olivier Bouillet
b698b1837b fix(android): video resolution orientation android (#3862)
* fix(android): ensure width/heigh respect rotation of the video + Add rotation info in onVideoTrack event
2024-05-31 18:53:24 +02:00
Olivier Bouillet
c2ce66ed26 fix(android): optimize lag on old android (#3860)
* fix: enable forceEnableMediaCodecAsynchronousQueueing for old android device
2024-05-31 08:58:29 +02:00
Krzysztof Moch
5c6dfb26c5 fix(ios): fix notification controls enabled by default (#3861) 2024-05-31 08:57:17 +02:00
Olivier Bouillet
8ad1047dc5 chore(docs): fix typo (#3859)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* chore: fix typo in showNotificationControls
2024-05-30 12:57:30 +02:00
Olivier Bouillet
bdf3e556d8 fix(android): refactor source, fix random DRM issue and crop start on local asset (#3835)
* fix: refactor source parameter parsing

Also fix a side issue when using a local file cropping props were not applied
Also fix random DRM issue by refactoring initializePlayerSource https://github.com/TheWidlarzGroup/react-native-video/issues/3082

* chore: restore metadata checks before appling them
2024-05-30 08:53:49 +02:00
YangJH
1b51c15348 fix(ios): fix playback status with lifecycle (#3819)
* fix(ios): fix playback status with lifecycle

* chore(ios): fix lint error

* fix(ios): check _playInBackground value within handleExternalPlaybackActiveChange
2024-05-28 21:04:55 +02:00
Olivier Bouillet
d072aeb451 Docs: fix controlsStyle ordering (#3848)
* docs: fix controlsStyle ordering
2024-05-28 11:58:50 +02:00
Seyed Mostafa Hasani
c7f4d7b83b feat: add getCurrentPosition to component's ref (#3824)
* feat: add getCurrentPosition to component's ref
---------

Co-authored-by: mostafahasani <hasanie.mostafa@alopeyk.com>
2024-05-28 11:00:38 +02:00
Olivier Bouillet
e23e02b359 chore(sample): review seeker in sample (#3787) 2024-05-28 09:33:21 +02:00
Seyed Mostafa Hasani
5059e7a7f1 feat(android): handle increment forward and rewind buttons (#3818)
* feat(android): handle increment forward and rewind buttons
* fix: function name for get seekIncrementMS
2024-05-28 09:32:30 +02:00
Krzysztof Moch
46e12e0b94 docs: update min iOS version info (#3814)
* docs: update min iOS version info

* Update docs/pages/component/props.mdx

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-05-28 09:31:00 +02:00
YangJH
c2cc917368 feat(android): Change subtitleLayout from child to sibling of layout (#3830) 2024-05-28 09:29:40 +02:00
lovegaoshi
c2a14240ad fix(android): android cache header (#3832)
* fix: android cache header
2024-05-28 09:29:21 +02:00
Paul Rinaldi
5c29b48747 fix(android): allow notification tap to foreground app (#3831) 2024-05-28 09:23:56 +02:00
Seyed Mostafa Hasani
ce9a0cdfa2 chore(android): add colors and dimens files to handle variables (#3836) 2024-05-27 19:19:34 +02:00
Krzysztof Moch
cd28d370d2 fix(ios): don't crash app if view wasn't found (#3841)
* fix(ios): don't crash app if view wasn't found

* lint code
2024-05-27 09:22:22 +02:00
Olivier Bouillet
36c23a5251 chore: release v6.1.2 2024-05-23 17:37:25 +02:00
Olivier Bouillet
69bde447b8 fix(android): revert previous fix not compatible with old java version (#3828) 2024-05-23 17:35:13 +02:00
Krzysztof Moch
563b27a166 chore: release v6.1.1 2024-05-22 23:21:56 +02:00
Krzysztof Moch
ec42e7d0c4 chore: fix package version 2024-05-22 23:06:19 +02:00
YangJH
089dc7e032 feat(android): change default user agent value (#3813)
* feat(android): change default user agent value

* docs: fix headers prop platform
2024-05-22 22:59:32 +02:00
Olivier Bouillet
dac0985430 fix(iOS): sometimes aspect ratio is invalid (#3821)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: invert aspect ratio evaluation to find video height / width
2024-05-22 22:54:00 +02:00
Olivier Bouillet
bb2404f8ba chore: release v6.1.0 2024-05-22 14:25:00 +02:00
Olivier Bouillet
d987525b6d chore(android): fix various warnings (#3810)
* chore: add missing annotation
* chore: remove unused import
* chore: add final on members
* chore: remove unused
* chore: add missing annotations
* chore: remove unused
* chore: replace switch by if
* chore: simplify lamba declaration
* chore: more beautifull declaration
* chore: rename variable considered as a typo
* chore: remove deprecated & avoid multiple calls to the same function
2024-05-22 14:02:55 +02:00
Olivier Bouillet
cad5c4624c fix(android)!: rework video tracks management (#3778)
* fix: fix crash when invalid index type is provided and minor clean up
* fix: review video track management. Fix index support and rework string vs int in tracks management
* fix: ABR track selection check
* fix: split track selector in sample and lint code
* fix: ensure we don't report null fields
* chore: improve tracks displayed
* chore: start moving to selection by index only
2024-05-22 14:01:55 +02:00
Olivier Bouillet
dbd7d7aa2c chore(example/basic): add mute state button (#3809) 2024-05-22 13:58:47 +02:00
Paul Rinaldi
94b3da3477 fix(android): implement seek backward in notification service (#3808) 2024-05-21 22:16:07 +02:00
Olivier Bouillet
c80164ccdf Fix(ts): crash seeking to null (#3802)
* fix: avoid crash when seeking to null
2024-05-21 22:15:47 +02:00
Olivier Bouillet
e16730de11 fix(android): implement live configuration management (#3792)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* feat(android): implement live buffer configuration

* chore: fix linter
2024-05-20 16:18:20 +02:00
Seyed Mostafa Hasani
8fdc52372c docs: update document link (#3796)
* fix: relative path

---------

Co-authored-by: mostafahasani <hasanie.mostafa@alopeyk.com>
2024-05-20 15:58:11 +02:00
Olivier Bouillet
d272a86f20 Fix(android): allow to select tracks with exceeded capabilities for more flexibility (#3791)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: allow to select Exceeded Constraints tracks for more playback flexibilities
2024-05-20 15:36:54 +02:00
Philip Heinser
77f7255c72 chore(ios): bump IMA SDK to 3.22.1 (#3783) 2024-05-20 15:34:55 +02:00
Olivier Bouillet
cd42dd78c9 fix(tvos): fix tvOS build and sample (#3785)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* chore: fix tvOS supported platform

* chore: fix tvOS build

* chore: update project tvos configuration
2024-05-20 13:09:58 +02:00
Seyed Mostafa Hasani
95e6140eea feat(android): add possibility to hide seekBar (#3789)
* feat: hide seekBar on Android when a video is a live broadcast

* refactor: prop name & code

* chore: update the document for a new prop (controlsStyles)

* refactor: code

* remove: additional variables

* fix: indent issues

* remove: duplicate function

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

revert change1

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

revert change2

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

chore: revert indent change

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

chore: revert indent change

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

chore: revert indent change

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

chore: revert indent change

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java

chore: revert indent change

* fix: eslint errors

* Update android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java

chore: revert indent change

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-05-20 12:45:18 +02:00
Seyed Mostafa Hasani
3cd7ab60b2 feat: add setVolume function to component's ref (#3794)
* feat: add setVolume function to component's ref

* Update methods.mdx
2024-05-20 12:21:48 +02:00
Olivier Bouillet
d4c9be2ba0 fix(android): random android crash (#3777)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix(android): fix random crash
2024-05-17 15:16:48 +02:00
Olivier Bouillet
d716e1ab2a chore(android): improve exoplayer logs (#3780)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: improve debuging display and enable it in the sample
2024-05-17 15:10:37 +02:00
Olivier Bouillet
8a57b127d0 chore(example/basic): upgrade sample react native version (#3776)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* chore: upgrade sample react native version and dependancies
2024-05-17 15:09:42 +02:00
Olivier Bouillet
7b1e1293f6 fix(android): avoid blinking on video track change (#3782)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix(android): avoid ghost resizing when video track change
2024-05-17 14:47:03 +02:00
YangJH
6455380f9e fix(android): source metadata compare function (#3775) 2024-05-17 14:44:48 +02:00
YangJH
66e0ba579b fix(android): playback doesn't work with 0 startPositionMs (#3784) 2024-05-17 14:21:02 +02:00
Olivier Bouillet
324b461527 fix(android): remove remaining ad view when zapping (#3786)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix(android): ensure adsLoader is well release if unused
2024-05-16 15:57:27 +02:00
Alireza Hadjar
8ee4bccc37 Update README.md (#3781) 2024-05-16 11:43:11 +02:00
Krzysztof Moch
464ce7c516 chore: release v6.0.0 2024-05-14 17:44:43 +02:00
Olivier Bouillet
219496ff3a fix(android): video tracks crash and clean (#3767)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup

* fix: fix crash when invalid index type is provided and minor clean up
2024-05-14 16:39:41 +02:00
Olivier Bouillet
98b4a75a90 fix(android): poster hidding (#3768)
* fix(android): fix poster management
2024-05-14 13:04:44 +02:00
Krzysztof Moch
aa854fb4ad chore: release v6.0.0-rc.2 2024-05-13 21:41:24 +02:00
Krzysztof Moch
b3f08f6c99 fix(ios): call onLoadStart earlier (#3750) 2024-05-13 21:32:51 +02:00
Olivier Bouillet
9716f4cb36 Fix(android): avoid video resizing flickering (#3751)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template

* fix(android): avoid video view flickering at playback startup
2024-05-13 19:20:36 +02:00
Olivier Bouillet
2e623ca0fb fix(avoid): avoid early return in setSrc (#3759)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* fix(android): avoid easly return in setSrc
2024-05-13 19:19:20 +02:00
Olivier Bouillet
e420418e8f feat(android): make buffering strategy dynamic (#3756)
* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size
* feat: expose bufferingStrategy to app and change default behavior
rename disableBuffering undocumented prop to bufferingStrategy and document it.
before this change, default was 'dependingOnMemory'. It can trigger some unnecessary gc() call on android.
2024-05-11 22:02:04 +02:00
Olivier Bouillet
1a48f190f0 chore(android): clean up logs (#3758)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore(android): cleanup logs TAG
2024-05-11 19:03:44 +02:00
Olivier Bouillet
2d9484499c fix(ts): add missing type (#3757)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* fix(js): add missing type for cacheSizeMB
2024-05-11 19:01:47 +02:00
Olivier Bouillet
efb338ee2a chore(android): refactor side loaded text tracks (#3754)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore(android): refactor external text tracks management.

Split parsing and tracks handling

* chore: fix linter
2024-05-11 18:57:59 +02:00
Olivier Bouillet
3f63fd076d docs: bug template (#3760)
* chore: fix bug template
2024-05-11 00:23:59 +02:00
YangJH
f87a7938c6 fix(js): fix onPlaybackStateChanged callback (#3753) 2024-05-10 20:38:34 +02:00
Zakir Bangash
d25629bb62 Revert "fix(android): video flickering add playback start (#3746)" (#3748)
This reverts commit b1cd52bc58.

s
2024-05-10 14:23:30 +02:00
Krzysztof Moch
aef7879dc1 chore: release v6.0.0-rc.1 2024-05-08 18:13:47 +02:00
Olivier Bouillet
b1cd52bc58 fix(android): video flickering add playback start (#3746)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* fix(android): avoid video flickering at playback start
2024-05-08 17:55:35 +02:00
Olivier Bouillet
8e2709ecfd Chore/improve issue template (#3747)
* perf: ensure we do not provide callback to native if no callback provided from app

* chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size

* chore: improve issue template
2024-05-08 17:22:53 +02:00
Krzysztof Moch
1d235a1fea perf(ios): add early returns (#3741)
* perf(ios): add early returns

* code review

* apply code review
2024-05-08 16:07:32 +02:00
Krzysztof Moch
627e5da4c3 chore: update readme (#3743) 2024-05-07 17:32:37 +02:00
Krzysztof Moch
cbdcbf2573 chore: update repository links (#3742) 2024-05-07 17:25:09 +02:00
Krzysztof Moch
8ad4be459b feat: add notification controls (#3723)
* feat(ios): add `showNotificationControls` prop

* feat(android): add `showNotificationControls` prop

* add docs

* feat!: add `metadata` property to srouce

This is breaking change for iOS/tvOS as we are moving some properties, but I believe that this will more readable and more user friendly

* chore(ios): remove UI blocking function

* code review changes for android

* update example

* fix readme

* fix typos

* update docs

* fix typo

* chore: improve sample metadata notification

* update codegen types

* rename properties

* update tvOS example

* reset metadata on source change

* update docs

---------

Co-authored-by: Olivier Bouillet <freeboub@gmail.com>
2024-05-07 12:30:57 +02:00
Olivier Bouillet
c59d00a0f0 perf: ensure we do not provide callback to native if no callback provided from app (#3735)
* perf: ensure we do not provide callback to native if no callback provided from app

* perf: remove onIdle Callback

* chore: code review
2024-05-07 11:06:12 +02:00
Olivier Bouillet
8eb31e82c7 chore: rework bufferConfig to make it more generic and reduce ReactExoplayerView code size (#3739) 2024-05-06 22:04:40 +02:00
Olivier Bouillet
e05da4e9fe feat(android): implement asset folder playback (#3733)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* feat(android): implement playback from asset folder

* chore(android): fix linter

* chore: move sample mp4 from package assets to exemple assets
2024-05-06 21:51:17 +02:00
Olivier Bouillet
51e22abfe3 fix(ios): fix text track selection by index (#3728)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix(ios): text tracks selection by index. regression due to codegen rework

* chore: make selection by index testable (need a small manual patch)

* chore(ios): fix linter warning
2024-05-04 15:20:59 +02:00
Olivier Bouillet
4c63988d12 docs: review and clean up (#3730)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* chore: reorder drm props

* chore: reorder events

* docs: move onAudioTracks to events

* docs: reorder and clean <PlatformsList from methods

* docs: fix props case naming

* docs: fix ordering

* docs: fix ordering & remove trackId

* chore: remove sample build from installation page

* docs: remove outdated information

* docs: remove beta information from doc landing page
2024-05-04 14:36:39 +02:00
Krzysztof Moch
edbb3b60aa chore(example/basic): update Podfile.lock (#3725) 2024-05-01 11:51:24 +02:00
Nguyễn Bảo Khương
e96c17321f fix(ios): destroy adsManager when player detach from super view (#3716) (#3722)
* fix(ios): destroy adsManager when player detach from super view (#3716)

* fix: swift format
2024-05-01 11:31:20 +02:00
lovegaoshi
ecc946d1c1 feat(android): cache (#3514)
* feat: android cache

* docs: bufferSize

* Revert "docs: bufferSize"

This reverts commit 09637b134e121b9ca3ffd78f2f5bc657319ed67a.

* fix: cacheSize name

* feat: singleton android cache

* fix: local cache resolve

* fix: lint

* docs: android cache

* chore: merge conflict

* fix: lint

* chore: useCache button

* chore: fix state in the sample

* fix: cache factory

* chore: update cacheSizeMB docs

---------

Co-authored-by: Olivier Bouillet <freeboub@gmail.com>
2024-05-01 11:20:34 +02:00
Olivier Bouillet
518a9a93e0 fix: avoid crash when setting index to 0 to tracks selection (#3721)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix: avoid crash when setting invalid selected track or index 0
2024-04-30 15:12:37 +02:00
Olivier Bouillet
1a8295c8bf fix(ios): ensure orientation is correct on iOS (#3719)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix(iOS): ensure orientation is correctly reported

* chore: fix build
2024-04-26 10:02:13 +02:00
Olivier Bouillet
a1090a1639 chore(examples/basic): add startPosition sample (#3718)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix: fix sample style & avoid changing channel onEnd when looping is enable

* chore: add startPosition in the sample

* chore: update startPosition for easier testing
2024-04-26 09:43:39 +02:00
Olivier Bouillet
2cd49b53fd chore(examples/basic): minor sample fixes (#3717)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix: fix sample style & avoid changing channel onEnd when looping is enable
2024-04-26 09:42:21 +02:00
Olivier Bouillet
ce0c113a19 chore: update bug template to get free field for version (#3714)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix(ios): ensure duration is correct when starting a live playback

* chore: move ticket version to a free text field instead of dropDown
2024-04-25 11:33:14 +02:00
Olivier Bouillet
d56b251aef fix(ios): ensure duration available when playing live (#3710)
* fix(ios): ensure duration is correct when starting a live playback
2024-04-25 11:29:00 +02:00
Pixee OSS Assistant
eec125579f chore(android): switch order of literals to prevent NullPointerException (#3711)
Co-authored-by: pixeebot[bot] <104101892+pixeebot[bot]@users.noreply.github.com>
2024-04-25 11:15:32 +02:00
Olivier Bouillet
e8ce4979b3 docs: no onSeek callback with controls on iOS (#3701)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix(android): add onSeek callback when controls are enable

* chore: remove seekTime which is useless now

* doc: indicate that onSeek is not reported when controls are enable on iOS
2024-04-23 09:01:50 +02:00
Olivier Bouillet
818f92f654 chore(example/basic): bump react-native to 0.74.0 (#3702)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* chore: update basic sample to official react native 0.74
2024-04-23 09:00:42 +02:00
YangJH
6f61d7f6e6 fix(android): prevent changing video track when video load (#3683)
- video track must change after video loaded, if change with load state, ready state can be called
2024-04-22 21:32:59 +02:00
Krzysztof Moch
709818f0ef chore: release v6.0.0-rc.0 2024-04-22 19:32:36 +02:00
Olivier Bouillet
c730306e3a fix(android): seek callback with controls (#3694)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* fix(android): add onSeek callback when controls are enable

* chore: remove seekTime which is useless now
2024-04-22 10:35:51 +02:00
Olivier Bouillet
20d398608a Doc/fix change log link (#3693)
* doc: fix changelog link in "Version 6.0.0 breaking changes" section
2024-04-21 10:58:42 +02:00
Krzysztof Moch
efa1c52491 fix(ios): fix sideloading external subtitles (#3690)
* fix(ios): fix subtitles side loading

* update example

* Update examples/basic/src/VideoPlayer.tsx

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

* Update examples/basic/src/VideoPlayer.tsx

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

---------

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
2024-04-19 22:38:52 +02:00
Krzysztof Moch
b5ccc48476 fix(ios): add workaround for TouchableWithoutFeedback (#3688) 2024-04-18 14:18:17 +02:00
邵瑾瑜
3e3532691a fix(android): catch errors in performOnPlayerView (#3685)
Co-authored-by: JinYuSha0 <a1009943858@gmail.com>
2024-04-18 10:18:14 +02:00
YangJH
042e13c1dc fix(ios): update onPlaybackStateChanged implementation (#3687) 2024-04-18 10:11:15 +02:00
YangJH
1af12f9dfb fix(android): fixed bug where video would not be visible after remount and change of drm source (#3668)
* refactor(android): remove redundant lifecycle (onDetachedFromWindow)

* fix: remove initPlayer within onAttachedToWindow and remove lifecycle
2024-04-16 14:42:34 +02:00
Olivier Bouillet
a0bcdb75d3 chore(ios): remove unnecessary import (#3682)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample

* chore: remove unnecessary import
2024-04-16 14:36:16 +02:00
Krzysztof Moch
64e3191f73 feat(android): allow to disable selected functionalities (#3681)
* feat(android): add possibility do disable some of functionalities

* create dump classes

* remove dump files when functionalities are enabled

* add docs

* enable all functionalities in example

* throw error when trying to use disabled functionality

* update docs
2024-04-16 14:23:19 +02:00
Olivier Bouillet
2285eba8f0 Feat/add rtsp support (#3677)
* feat(android) : add rtsp support
2024-04-16 10:41:39 +02:00
Krzysztof Moch
336b9f0220 fix(android): set title for external subtitles (#3676) 2024-04-14 14:57:04 +02:00
Krzysztof Moch
f815834025 docs: update readme (#3673) 2024-04-11 15:28:04 +02:00
Krzysztof Moch
60c7a5e57e docs: update readme (#3669) 2024-04-10 08:09:52 +02:00
Olivier Bouillet
e26afac403 fix(ios): workaround for rate change (#3657)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix(ios): add a workaround for smooth rate change management
2024-04-07 19:04:43 +02:00
YangJH
e82f9dc24b fix: prevents crash from occurring when using the selected video track with resolution type (#3664)
* fix: fix video resolution track native crash error

* fix: fix type error
2024-04-07 19:03:37 +02:00
Olivier Bouillet
4c7719a3f5 fix: ensure tracks are available in sample (#3660)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* fix: ensure tracks are well displayed in the sample
2024-04-07 19:02:39 +02:00
Olivier Bouillet
453907483d docs: describe project and sample build (#3656)
* fix(ts): onPlaybackRateChangeData was not correctly typed

* doc: describe project and sample build
2024-04-07 19:01:42 +02:00
Olivier Bouillet
e992243305 fix(ios): do not save pause state before seeking (#3650)
* fix(ios): do not save pause state before seeking
2024-04-05 10:37:21 +02:00
Krzysztof Moch
07f71c2fc4 fix(ios): apply PictureInPicture state on start (#3655) 2024-04-05 10:37:00 +02:00
Olivier Bouillet
d6941392e0 fix: ensure poster works as expected and add it to the sample (#3643)
* fix: ensure poster works as expected and add it to the sample
* chore: drop audioOnly property as not implemented on any platform
* fix(ios): do not save pause state before seeking
* fix(ts): onPlaybackRateChangeData was not correctly typed
2024-04-05 10:35:57 +02:00
Gaëtan Kueny
051e884c8f fix(ios): call PictureInPicture callbacks with native controls (#3603)
* fix(ios): call PictureInPictureStatusChanged callback with native controls

We add RCTPlayerObserver as playerViewController delegate to be notified with PiP lifecycle
should partially fix #3602

* fix(ios): call onRestoreUserInterfaceForPictureInPictureStop callback with native controls

should partially fix #3602
2024-04-04 15:08:48 +02:00
Olivier Bouillet
2a858df8bc fix(ts): onPlaybackRateChangeData was not correctly typed (#3651) 2024-04-04 14:45:39 +02:00
Krzysztof Moch
10b100de44 feat!(ios): remove native dependency promises (#3631) 2024-04-04 13:23:44 +02:00
Olivier Bouillet
2633f087d2 doc: fix table format (#3649) 2024-04-03 22:19:13 +02:00
Krzysztof Moch
f28bf0ae33 chore: release v6.0.0-beta.8 2024-04-03 21:48:19 +02:00
Krzysztof Moch
1e5af7b526 chore: disable codegen (#3647)
We can not use Interop Layer with codegen - to be reverted once we integrate new architecture
2024-04-03 20:51:26 +02:00
Krzysztof Moch
9b66e7fdce fix: fix codegen types (#3636)
* fix: remove string unions from component spec

* fix linter issue

* fix codegen type

* update podspec
2024-04-03 20:49:47 +02:00
Olivier Bouillet
d3cc0d0c5a chore(android): fix various warning (#3625) 2024-03-31 20:03:52 +02:00
Krzysztof Moch
e87c14a437 fix(android): update ui manager getter (#3634) 2024-03-31 19:15:14 +02:00
Olivier Bouillet
0fa0086a39 chore: release v6.0.0-beta.7 2024-03-30 12:28:47 +01:00
Olivier Bouillet
182c953597 chore(typescript): fix external loaded textTracks typing and add one in sample (#3626) 2024-03-30 12:22:37 +01:00
Olivier Bouillet
dd3a400689 Chore/fix strange management of resume (#3629)
* fix(android): rename startPlayback to resumePlayback and fix implementation

I cannot understand why this implementation has been done. I guess this is to workaround some issue...
now resume only resume playback and don't start a new playback during bufffering...

* chore: simplify duplicated code
2024-03-29 20:59:58 +01:00
Olivier Bouillet
75d370742b fix(ios): fix regression when playing source starting with ph:// (#3630) 2024-03-29 19:47:04 +01:00
Olivier Bouillet
f10511d953 fix(android): improve and backBufferDurationMs. mainly let exoplayer manage the prop (#3619)
BREAKING CHANGE: move backBufferDurationMs from root props to bufferConfig
2024-03-28 21:33:17 +01:00
YangJH
24c1aab3f5 fix: inject onGetLicense prop properly for detect user defined or not (#3608)
* fix: inject onGetLicense prop properly for detect user defined or not

* fix: fix lint error
2024-03-28 11:22:52 +01:00
Krzysztof Moch
0312afc8ea fix: remove setNativeProps usage (#3605)
* fix: remove `setNativeProps` usage

* code review
2024-03-28 11:22:04 +01:00
Olivier Bouillet
38746ff2ba doc: improve network tracing description (#3618) 2024-03-28 10:40:13 +01:00
YangJH
89ae8438fa fix: fix getLicense function's type definition (#3606)
* fix(type): fix getLicense type definition
2024-03-26 23:41:49 +01:00
Gaëtan Kueny
bb9e7eb5a5 fix(ios): fix PiP callback (#3601) 2024-03-26 14:10:31 +01:00
YangJH
c9a75f3cde fix(iOS): fix iOS DRM header parser (#3609) 2024-03-26 13:56:22 +01:00
YangJH
098358076d fix(iOS): throw when content id defined with empty string (#3612) 2024-03-26 13:55:11 +01:00
1026 changed files with 53633 additions and 124061 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2

View File

@@ -1,2 +0,0 @@
examples/
lib/

View File

@@ -1,13 +0,0 @@
{
"plugins": ["@typescript-eslint"],
"extends": [
"@react-native",
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"requireConfigFile": false
}
}

View File

@@ -1,18 +0,0 @@
### Current behavior
Describe what happens when you encounter this issue.
### Reproduction steps
A 1, 2, 3, etc. list of what's needed to see the issue happen.
### Expected behavior
Describe what you wanted to happen
### Platform
Which player are you experiencing the problem on:
* iOS
* Android
* Windows UWP
* Windows WPF
### Video sample
If possible, include a link to the video that has the problem that can be streamed or downloaded from.

View File

@@ -1,74 +0,0 @@
name: Bug report
description: Create a report to help us improve
title: "[BUG]: "
labels: ["bug"]
assignees: []
body:
- type: markdown
attributes:
value: Thanks for taking the time to fill out this bug report!
- type: dropdown
id: version
attributes:
label: Version
description: What version are you using?
options:
- v6 (Beta)
- v5 (Stable)
validations:
required: true
- type: dropdown
id: platforms
attributes:
label: What platforms are you having the problem on?
multiple: true
options:
- iOS
- Android
- Windows
- visionOS
- Android TV
- Apple tvOS
- type: dropdown
id: architecture
attributes:
label: Architecture
description: What architecture are you using?
options:
- Old architecture
- New architecture with interop layer
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen?
placeholder: Tell us what you see!
value: "A bug happened!"
validations:
required: true
- type: input
id: reproduction-repo
attributes:
label: Reproduction
description: Provide a link to a repository with a reproduction of the bug, this is optional but it will make us to fix the bug faster
placeholder: Reproduction Repository
value: "repository link"
validations:
required: false
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: Tell us how can we reproduce this bug
placeholder: Reproduction
value: "Step to reproduce this bug are: "
validations:
required: true

View File

@@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: GitHub Discussions
url: https://github.com/react-native-video/react-native-video/discussions
about: Please ask and answer questions here.
- name: Slack Channel
url: https://join.slack.com/t/video-dev/shared_invite/zt-24kgmctuv-2z0O9J_v6q_rg~x1RujdOA
about: You can find us on the VideoDev Slack in the react-native-video channel.
- name: TheWidlarzGroup Discord
url: https://discord.gg/7Y6eE62hXM
about: Feel free to join our Discord server and ask questions there.

View File

@@ -1,52 +0,0 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature]: "
labels: ["feature"]
assignees: []
body:
- type: markdown
attributes:
value: Thanks for taking the time to fill out this feature report!
- type: textarea
id: description
attributes:
label: Description
description: Tell us your idea and why will concern if we implement it. You can also create a PR 😄
placeholder: Tell us your idea!
value: "Very cool idea!"
validations:
required: true
- type: textarea
id: why-it-is-needed
attributes:
label: Why it is needed ?
description: Tell us your why it is needed!
placeholder: Why it is needed ?
value: "Because it is cool!"
validations:
required: true
- type: textarea
id: possible-implementation
attributes:
label: Possible implementation
description: |
Tell us your possible implementation! It really helps if you could describe from a technical POV how this new feature would work, which code it rely on, etc
placeholder: How to implement ?
value: "Technical POV how to do it"
validations:
required: false
- type: textarea
id: code-sample
attributes:
label: Code sample
description: Please show how the new code could work, if doable
placeholder: Code sample
value: "Code sample"
validations:
required: false

View File

@@ -1,25 +0,0 @@
<!--
Thanks for opening a PR!
Since this is a volunteer project and is very active, anything you can do to reduce the amount of time needed to review and merge your PR is appreciated.
The following steps will help get your PR merged quickly:
- Update the documentation
If you've added new functionality, update the README.md with an entry for your prop or event.
The entry should be inserted in alphabetical order.
- Provide an example of how to test the change
If the PR requires special testing setup provide all the relevant instructions and files. This may include a sample video file or URL, configuration, or setup steps.
- Focus the PR on only one area
If you're touching multiple different areas that aren't related, break the changes up into multiple PRs.
- Describe the changes
Add a note describing what your PR does. If there is a change to the behavior of the code, explain why it needs to be updated.
-->
## Summary
### Motivation
### Changes
## Test plan

View File

@@ -11,24 +11,23 @@ runs:
using: composite
steps:
- name: Setup Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.0.4
bun-version: 1.2.19
- name: Cache dependencies
id: bun-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/package.json') }}
key: ${{ runner.os }}-v7-bun-${{ hashFiles('**/bun.lock') }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
${{ runner.os }}-bun-
${{ runner.os }}-v7-bun-${{ hashFiles('**/bun.lock') }}
${{ runner.os }}-v7-bun-
- name: Install dependencies
working-directory: ${{ inputs.working-directory }}
if: steps.bun-cache.outputs.cache-hit != 'true'
run: bun install
shell: bash

View File

@@ -1,34 +0,0 @@
name: Setup node_modules
description: Setup Node.js and install dependencies
inputs:
working-directory:
description: 'working directory for yarn install'
default: ./
required: false
runs:
using: composite
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Cache dependencies
id: yarn-cache
uses: actions/cache@v3
with:
path: |
${{ inputs.working-directory }}/node_modules
.yarn/install-state.gz
key: ${{ runner.os }}-yarn-${{ inputs.working-directory }}-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }}
restore-keys: |
${{ runner.os }}-yarn-${{ inputs.working-directory }}-${{ hashFiles('yarn.lock') }}-
${{ runner.os }}-yarn-${{ inputs.working-directory }}
- name: Install dependencies
working-directory: ${{ inputs.working-directory }}
if: steps.yarn-cache.outputs.cache-hit != 'true'
run: yarn install --immutable --ignore-scripts
shell: bash

60
.github/stale.yml vendored
View File

@@ -1,60 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 3
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- pinned
- security
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions. If you are having a similar problem, please open a
new issue and reference this one instead of commenting on a stale or closed
issue.
# Comment to post when removing the stale label.
unmarkComment: false
# Comment to post when closing a stale Issue or Pull Request.
closeComment: false
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 50
# Limit to only `issues` or `pulls`
only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed

View File

@@ -1,80 +0,0 @@
name: Build Android
on:
push:
branches:
- master
paths:
- '.github/workflows/build-android.yml'
- 'android/**'
- 'examples/basic/android/**'
- 'yarn.lock'
- 'examples/basic/yarn.lock'
pull_request:
paths:
- '.github/workflows/build-android.yml'
- 'android/**'
- 'examples/basic/android/**'
- 'yarn.lock'
- 'examples/basic/yarn.lock'
jobs:
build:
name: Build Android Example App
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup JDK 17
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: 17
java-package: jdk
- name: Install node_modules
uses: ./.github/actions/setup-node
with:
working-directory: examples/basic
- name: Restore Gradle cache
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Run Gradle Build for basic example
run: cd examples/basic/android && ./gradlew assembleDebug --build-cache && cd ../../..
build-without-ads:
name: Build Android Example App Without Ads
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup JDK 17
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: 17
java-package: jdk
- name: Install node_modules
uses: ./.github/actions/setup-node
with:
working-directory: examples/basic
- name: Restore Gradle cache
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Run Gradle Build for basic example
run: cd examples/basic/android && export RNV_SAMPLE_ENABLE_ADS=false && ./gradlew assembleDebug --build-cache && cd ../../..

View File

@@ -1,166 +0,0 @@
name: Build iOS
on:
workflow_dispatch:
push:
branches:
- master
paths:
- '.github/workflows/build-ios.yml'
- 'ios/**'
- '*.podspec'
- 'examples/basic/ios/**'
pull_request:
paths:
- '.github/workflows/build-ios.yml'
- 'ios/**'
- '*.podspec'
- 'examples/basic/ios/**'
jobs:
build:
name: Build iOS Example App
runs-on: macos-14 # This allow us to use Xcode 15.0.1 which is a lot faster - TODO change to "macos-latest" once it's out of beta
defaults:
run:
working-directory: examples/basic/ios
steps:
- uses: actions/checkout@v4
- name: Install node_modules
uses: ./.github/actions/setup-node
with:
working-directory: examples/basic
- name: Restore buildcache
uses: mikehardy/buildcache-action@v2
continue-on-error: true
- name: Setup Ruby (bundle)
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6.10
bundler-cache: true
- name: Restore Pods cache
uses: actions/cache@v3
with:
path: |
examples/basic/ios/Pods
~/Library/Caches/CocoaPods
~/.cocoapods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install Pods
run: pod install
- name: Install xcpretty
run: gem install xcpretty
- name: Build App
run: "set -o pipefail && xcodebuild \
-derivedDataPath build -UseModernBuildSystem=YES \
-workspace videoplayer.xcworkspace \
-scheme videoplayer \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 14' \
build \
CODE_SIGNING_ALLOWED=NO | xcpretty"
build-with-ads:
name: Build iOS Example App With Ads
runs-on: macos-14 # This allow us to use Xcode 15.0.1 which is a lot faster - TODO change to "macos-latest" once it's out of beta
defaults:
run:
working-directory: examples/basic/ios
steps:
- uses: actions/checkout@v4
- name: Install node_modules
uses: ./.github/actions/setup-node
with:
working-directory: examples/basic
- name: Restore buildcache
uses: mikehardy/buildcache-action@v2
continue-on-error: true
- name: Setup Ruby (bundle)
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6.10
bundler-cache: true
- name: Restore Pods cache
uses: actions/cache@v3
with:
path: |
examples/basic/ios/Pods
~/Library/Caches/CocoaPods
~/.cocoapods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install Pods
run: export RNV_SAMPLE_ENABLE_ADS=true && pod install
- name: Install xcpretty
run: gem install xcpretty
- name: Build App
run: "set -o pipefail && export RNV_SAMPLE_ENABLE_ADS=true && xcodebuild \
-derivedDataPath build -UseModernBuildSystem=YES \
-workspace videoplayer.xcworkspace \
-scheme videoplayer \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 14' \
build \
CODE_SIGNING_ALLOWED=NO | xcpretty"
build-with-caching:
name: Build iOS Example App With Caching
runs-on: macos-14 # This allow us to use Xcode 15.0.1 which is a lot faster - TODO change to "macos-latest" once it's out of beta
defaults:
run:
working-directory: examples/basic/ios
steps:
- uses: actions/checkout@v4
- name: Install node_modules
uses: ./.github/actions/setup-node
with:
working-directory: examples/basic
- name: Restore buildcache
uses: mikehardy/buildcache-action@v2
continue-on-error: true
- name: Setup Ruby (bundle)
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6.10
bundler-cache: true
- name: Restore Pods cache
uses: actions/cache@v3
with:
path: |
examples/basic/ios/Pods
~/Library/Caches/CocoaPods
~/.cocoapods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
- name: Install Pods
run: export RNV_SAMPLE_VIDEO_CACHING=true && pod install
- name: Install xcpretty
run: gem install xcpretty
- name: Build App
run: "set -o pipefail && export RNV_SAMPLE_VIDEO_CACHING=true && xcodebuild \
-derivedDataPath build -UseModernBuildSystem=YES \
-workspace videoplayer.xcworkspace \
-scheme videoplayer \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 14' \
build \
CODE_SIGNING_ALLOWED=NO | xcpretty"

View File

@@ -1,34 +0,0 @@
name: Check Android
on:
push:
branches:
- master
paths:
- '.github/workflows/check-android.yml'
- 'android/**'
pull_request:
paths:
- '.github/workflows/check-android.yml'
- 'android/**'
jobs:
Kotlin-Lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.0.1/ktlint && chmod a+x ktlint && sudo mv ktlint /usr/local/bin/
- name: run ktlint
working-directory: ./android/
run: |
ktlint --reporter=checkstyle,output=build/ktlint-report.xml --relative --editorconfig=./.editorconfig
continue-on-error: true
- uses: yutailang0119/action-ktlint@v3
with:
report-path: ./android/build/*.xml
continue-on-error: false
- uses: actions/upload-artifact@v3
with:
name: ktlint-report
path: ./android/build/*.xml

View File

@@ -1,31 +0,0 @@
name: Check CLang
on:
push:
branches:
- master
paths:
- '.github/workflows/check-clang.yml'
- 'ios/**'
pull_request:
branches:
- master
paths:
- '.github/workflows/check-clang.yml'
- 'ios/**'
jobs:
CLang-Format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang-format
run: sudo apt-get install clang-format
- name: Check ios clang formatting
run: |
find ios -type f \( -name "*.h" -o -name "*.cpp" -o -name "*.m" -o -name "*.mm" \) -print0 | while read -d $'\0' file; do
clang-format -style=file:./ios/.clang-format -i "$file"
done
shell: bash
- name: Check for changes
run: git diff --exit-code HEAD

View File

@@ -1,41 +0,0 @@
name: Check iOS
on:
push:
branches:
- master
paths:
- '.github/workflows/check-ios.yml'
- 'ios/**'
pull_request:
paths:
- '.github/workflows/check-ios.yml'
- 'ios/**'
jobs:
Swift-Lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint with SwiftLint
uses: norio-nomura/action-swiftlint@master
with:
args: --strict
env:
WORKING_DIRECTORY: ios
Swift-Format:
runs-on: macos-14 # This allow us to use Xcode 15.0.1 which is a lot faster - TODO change to "macos-latest" once it's out of beta
defaults:
run:
working-directory: ./ios
steps:
- uses: actions/checkout@v4
- name: Install SwiftFormat
run: brew install swiftformat
- name: Format Swift code
run: swiftformat --verbose .
- name: Verify formatted code is unchanged
run: git diff --exit-code HEAD

View File

@@ -1,62 +0,0 @@
name: Check JS
on:
push:
branches:
- master
paths:
- '.github/workflows/check-js.yml'
- 'src/**'
- '*.json'
- '*.js'
- '*.jsx'
- '*.ts'
- '*.tsx'
- '*.lock'
pull_request:
paths:
- '.github/workflows/check-js.yml'
- 'src/**'
- '*.json'
- '*.js'
- '*.jsx'
- '*.ts'
- '*.tsx'
- '*.lock'
jobs:
TypeScript-Check:
name: Check TS (tsc)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install node_modules
uses: ./.github/actions/setup-node
- name: Install reviewdog
uses: reviewdog/action-setup@v1
- name: Check TypeScript
run: |
yarn tsc | reviewdog -name="tsc" -efm="%f(%l,%c): error TS%n: %m" -reporter="github-pr-review" -filter-mode="nofilter" -fail-on-error -tee
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JS-Lint:
name: Lint JS (eslint, prettier)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install node_modules
uses: ./.github/actions/setup-node
- name: Run ESLint
run: yarn lint -f @jamesacarr/github-actions
- name: Run ESLint with auto-fix
run: yarn lint --fix
- name: Verify no files have changed after auto-fix
run: git diff --exit-code HEAD

View File

@@ -1,46 +1,50 @@
name: Deploy Documentation
name: deploy docs
on:
workflow_dispatch:
push:
branches:
- master
- v7
paths:
# Update on workflow change
- '.github/workflows/deploy-docs.yml'
# Update on docs change
- 'docs/**'
# Update on code change (Api Reference)
- 'packages/react-native-video/src/**'
jobs:
deploy-docs:
build:
name: Build Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-bun
- uses: actions/checkout@v4
with:
working-directory: ./docs
fetch-depth: 0
- name: Cache build
uses: actions/cache@v3
- uses: ./.github/actions/setup-bun
- name: Build Documentation
run: bun run --cwd docs build
- name: Upload Build Artifact
uses: actions/upload-pages-artifact@v3
with:
path: |
docs/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}
${{ runner.os }}-nextjs-
path: docs/build
- name: Build docs
run: |
bun --cwd docs build
touch docs/out/.nojekyll
- name: Deploy docs to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: docs/out
deploy:
name: Deploy Documentation to GitHub Pages
needs: build
permissions:
contents: write
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -1,35 +0,0 @@
name: Test Docs build
on:
workflow_dispatch:
pull_request:
paths:
- '.github/workflows/test-build-docs.yml'
- 'docs/**'
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-bun
with:
working-directory: ./docs
- name: Cache build
uses: actions/cache@v3
with:
path: |
docs/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}
${{ runner.os }}-nextjs-
- name: Build docs
run: |
bun --cwd docs build
touch docs/out/.nojekyll

27
.github/workflows/test-docs-build.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Test Documentation Build
on:
pull_request:
branches:
- v7
paths:
# Update on workflow change
- '.github/workflows/test-docs-build.yml'
# Update on docs change
- 'docs/**'
# Update on code change (Api Reference)
- 'packages/react-native-video/src/**'
jobs:
test-docs-deploy:
name: Test Documentation Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bun
- name: Test build website
run: bun run --cwd docs build

63
.gitignore vendored
View File

@@ -2,6 +2,15 @@
#
.DS_Store
**/.xcode.env.local
# XDE
.expo/
# VSCode
.vscode/
jsconfig.json
# Xcode
#
build/
@@ -21,29 +30,35 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
Pods
# Android/IJ
#
*.iml
.idea
.classpath
.cxx
.gradle
local.properties
*.hprof
.idea
.project
.settings
.classpath
local.properties
android.iml
# Cocoapods
#
example/ios/Pods
# Ruby
example/vendor/
# node.js
#
node_modules/
*.log
npm-debug.log
yarn-debug.log
yarn-error.log
# yarn
yarn.lock
# editor workspace settings
.vscode
# Bun
package-lock.json
**/*.bun
# BUCK
buck-out/
@@ -51,12 +66,22 @@ buck-out/
android/app/libs
android/keystores/debug.keystore
# windows
Deploy.binlog
msbuild.binlog
android/buildOutput_*
# Yarn
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# lib build
# Expo
.expo/
# Turborepo
.turbo/
# generated by bob
lib/
!src/lib
*.tsbuildinfo
# TypeScript
tsconfig.tsbuildinfo

1
.nvmrc
View File

@@ -1 +0,0 @@
v18

View File

@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxBracketSameLine": true
}

View File

@@ -1,27 +0,0 @@
{
"git": {
"commitMessage": "chore: release v${version}",
"requireCleanWorkingDir": true,
"tagAnnotation": "Release v${version}",
"tagName": "v${version}"
},
"plugins": {
"@release-it/conventional-changelog": {
"preset": "angular",
"infile": "CHANGELOG.md"
}
},
"hooks": {
"before:init": [
"rm -Rf lib tsconfig.tsbuildinfo",
"yarn install --frozen-lockfile --non-interactive --production=false",
"yarn run lint",
"yarn run build"
],
"after:release": "echo Successfully released ${name} v${version} from repository ${repo.repository}."
},
"npm": {
"skipChecks": false
}
}

View File

@@ -1,502 +0,0 @@
# [6.0.0-beta.6](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.5...v6.0.0-beta.6) (2024-03-18)
### Bug Fixes
* add missing node_modules paths to metro.config.js of basic example app ([#3555](https://github.com/react-native-video/react-native-video/issues/3555)) ([d505de5](https://github.com/react-native-video/react-native-video/commit/d505de5910a22ab9a0d7429e6b88a81cd2594b9c))
* add missing shutterColor type ([#3561](https://github.com/react-native-video/react-native-video/issues/3561)) ([ba00881](https://github.com/react-native-video/react-native-video/commit/ba00881ddcd53c2f5a4e1fc6e30cb5eb7ef674a3))
* **android:** check disableFocus when state is ready ([#3494](https://github.com/react-native-video/react-native-video/issues/3494)) ([366c841](https://github.com/react-native-video/react-native-video/commit/366c841c0b960fd461ae7dcfdcb76a928fadf2b8))
* **android:** enableDecoderFallback to decrease DECODER_ERROR issue ([#3416](https://github.com/react-native-video/react-native-video/issues/3416)) ([eaa72c6](https://github.com/react-native-video/react-native-video/commit/eaa72c66659b9e2a22af9ff9d43013521f6a66e3))
* **android:** onSeek called instantly ([#3530](https://github.com/react-native-video/react-native-video/issues/3530)) ([af6aea8](https://github.com/react-native-video/react-native-video/commit/af6aea8934e19467e1ed8e21808b2dbddb6f6356))
* **android:** suppress lint `PrivateResource` ([#3531](https://github.com/react-native-video/react-native-video/issues/3531)) ([38e3625](https://github.com/react-native-video/react-native-video/commit/38e3625541753340e912e474b753e0f4fac4e9c1))
* **docs/ci:** add typescript ([#3572](https://github.com/react-native-video/react-native-video/issues/3572)) ([0f31271](https://github.com/react-native-video/react-native-video/commit/0f31271dcf2bfe2f4429e22040660025be8a6a3c))
* **docs:** fix build ([#3571](https://github.com/react-native-video/react-native-video/issues/3571)) ([4fc7d27](https://github.com/react-native-video/react-native-video/commit/4fc7d2788b4d01c581a31cc3ac733c3948b65a3a))
* **ios:** add text tracks only if we successfully insertTimeRage ([#3557](https://github.com/react-native-video/react-native-video/issues/3557)) ([b73baad](https://github.com/react-native-video/react-native-video/commit/b73baad2c2c0c6ea701d865eee32d4e94ae58178))
* **ios:** apply `cropStart` when in repeat mode ([#3525](https://github.com/react-native-video/react-native-video/issues/3525)) ([2c0e009](https://github.com/react-native-video/react-native-video/commit/2c0e00987685875f9603ae2084ae23b3c1aebce7))
* **ios:** current release volume change observer ([#3565](https://github.com/react-native-video/react-native-video/issues/3565)) ([16f3cdb](https://github.com/react-native-video/react-native-video/commit/16f3cdbd9a7864206feaeef29344c09792d66d56))
* **ios:** Do not crash when accessLog return nil ([#3549](https://github.com/react-native-video/react-native-video/issues/3549)) ([4d4b56c](https://github.com/react-native-video/react-native-video/commit/4d4b56c05dd3c09fce5ddc38f56b0391c357ac85))
* **ios:** don't crop video when in repeat mode ([#3575](https://github.com/react-native-video/react-native-video/issues/3575)) ([90b31af](https://github.com/react-native-video/react-native-video/commit/90b31af2c969b6d6d57877c71ef3a4830a76aedc))
* **ios:** ensure playback stopped in background ([#3587](https://github.com/react-native-video/react-native-video/issues/3587)) ([41c6785](https://github.com/react-native-video/react-native-video/commit/41c6785ee8c667ebe9c6c464223f6485473d94f8))
* **ios:** fix missing bridge in bridgeless mode ([#3570](https://github.com/react-native-video/react-native-video/issues/3570)) ([46c8c49](https://github.com/react-native-video/react-native-video/commit/46c8c498c474600a0b35ebaf744306aefa42905f))
* **ios:** fix tvOS build ([#3524](https://github.com/react-native-video/react-native-video/issues/3524)) ([9306d9a](https://github.com/react-native-video/react-native-video/commit/9306d9a15d281a60492f6d4166598a389a56f652))
* **ios:** split licenseUrl and loadedLicenseUrl ([#3578](https://github.com/react-native-video/react-native-video/issues/3578)) ([7c4d19f](https://github.com/react-native-video/react-native-video/commit/7c4d19fa72a35449dd11ec59278b2ea11ec629fc))
### Features
* **android:** add subtitle event ([#3566](https://github.com/react-native-video/react-native-video/issues/3566)) ([6184c10](https://github.com/react-native-video/react-native-video/commit/6184c10acc90defd63cd55af51458864dfe112d5))
* implement opacity to control visibility of subtitles ([#3583](https://github.com/react-native-video/react-native-video/issues/3583)) ([f4cce2e](https://github.com/react-native-video/react-native-video/commit/f4cce2ecdba0668c3ecf74d2fd7956df4dd8489d))
* **ios:** Add ios support for accessing WebVTT Subtitle Content ([#3541](https://github.com/react-native-video/react-native-video/issues/3541)) ([253ffb5](https://github.com/react-native-video/react-native-video/commit/253ffb595633a4b18221339278f73c8416225f56))
* move require (local files) to `source.uri` ([#3535](https://github.com/react-native-video/react-native-video/issues/3535)) ([41ac781](https://github.com/react-native-video/react-native-video/commit/41ac7814121fc70a123fa4585dc9b1bd96e9629f))
# [6.0.0-beta.5](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.4...v6.0.0-beta.5) (2024-02-02)
### Bug Fixes
* **android:** fix crash with interop layer ([#3509](https://github.com/react-native-video/react-native-video/issues/3509)) ([41e9bcb](https://github.com/react-native-video/react-native-video/commit/41e9bcb1ef28c1532863186c83423814fcaf2372))
* **android:** re-layout controls after fullscreen dismiss ([#3490](https://github.com/react-native-video/react-native-video/issues/3490)) ([135d97c](https://github.com/react-native-video/react-native-video/commit/135d97ce506bf1a0226042e0f29f4de5bcc10972))
* fix typo ([#3497](https://github.com/react-native-video/react-native-video/issues/3497)) ([336eb44](https://github.com/react-native-video/react-native-video/commit/336eb44dc6061dad9cdc3382eb05d0a0effbef64))
* **ios:** fix pip memory leak ([#3506](https://github.com/react-native-video/react-native-video/issues/3506)) ([53068dd](https://github.com/react-native-video/react-native-video/commit/53068ddd41218bb615cd129eba2c36d6347ccf25))
* remove lifecycle listener after component unmount ([#3489](https://github.com/react-native-video/react-native-video/issues/3489)) ([3858a15](https://github.com/react-native-video/react-native-video/commit/3858a15b4268ae54d5b97c036d86b05aaf31bcf9)), closes [#3488](https://github.com/react-native-video/react-native-video/issues/3488)
* remove pausePlayback when audio focus loss event ([#3496](https://github.com/react-native-video/react-native-video/issues/3496)) ([b1ab0f2](https://github.com/react-native-video/react-native-video/commit/b1ab0f24a3efbcc3be49005060f50b34a117664e))
### Features
* implement onAudioTracks and onTextTracks on ios ([#3503](https://github.com/react-native-video/react-native-video/issues/3503)) ([6a49cba](https://github.com/react-native-video/react-native-video/commit/6a49cba273fa0a47e106f4abb8caeb4ab6dbe4c8))
### Reverts
* Revert "fix: remove pausePlayback when audio focus loss event (#3496)" (#3504) ([aec7db6](https://github.com/react-native-video/react-native-video/commit/aec7db63901c42dd7a591b030bfc69daa8860341)), closes [#3496](https://github.com/react-native-video/react-native-video/issues/3496) [#3504](https://github.com/react-native-video/react-native-video/issues/3504)
# [6.0.0-beta.4](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.3...v6.0.0-beta.4) (2024-01-15)
### Bug Fixes
* add missing audioOutput prop (#3450) (f20d68b)
* **android**: support opacity properly (#3464) (11e5b75)
* **ios**: currentPlaybackTime in ms and not seconds (#3472) (3f63c16)
* **ios**: remove extra dismissFullscreenPlayer declaration (#3474) (045f5fa)
### Features
* add visionOS support (#3425) (cf3ebb7)
* **ios**: migrate from deprecated methods (#3444) (5aaa53d)
* **ios**: update the way to get keyWindow (#3448) (f35727f)
* **ios**: update timed metadata handler (#3449) (481cc71)
# [6.0.0-beta.3](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.2...v6.0.0-beta.3) (2023-12-24)
### Bug Fixes
* **android:** default UA ([#3429](https://github.com/react-native-video/react-native-video/issues/3429)) ([dd7bb54](https://github.com/react-native-video/react-native-video/commit/dd7bb54720c06eca045d72e7557d6f472a793b6f))
* ensure save doesn't crash on android ([#3415](https://github.com/react-native-video/react-native-video/issues/3415)) ([22a2655](https://github.com/react-native-video/react-native-video/commit/22a2655dca4bb53074ce5a74cfeb7f9bb26b13a3))
* **ios:** revert ios url encoding as this breaks encoded urls ([#3440](https://github.com/react-native-video/react-native-video/issues/3440)) ([0723481](https://github.com/react-native-video/react-native-video/commit/0723481fee75890bc2fff967e3b5bc8946e481a3))
* **ReactVideoProps:** add accessibility & testID in typing ([#3434](https://github.com/react-native-video/react-native-video/issues/3434)) ([d986b7b](https://github.com/react-native-video/react-native-video/commit/d986b7bf57f8fe49cbf5f507efde4aeb28ee34f8))
# [6.0.0-beta.2](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2023-12-08)
### Bug Fixes
* add allowsExternalPlayback missing on ReactVideoProps ([#3398](https://github.com/react-native-video/react-native-video/issues/3398)) ([72679a7](https://github.com/react-native-video/react-native-video/commit/72679a7d639b9c000e060af0dbab7c862c180b00))
* **android:** add explicitly dependancy to androidx.activity ([#3410](https://github.com/react-native-video/react-native-video/issues/3410)) ([908e30f](https://github.com/react-native-video/react-native-video/commit/908e30f9b8d950fa1423a10d4b08135b6cc4d43a))
* **android:** ensure adTagUrl can be reset ([#3408](https://github.com/react-native-video/react-native-video/issues/3408)) ([f9bcaac](https://github.com/react-native-video/react-native-video/commit/f9bcaac5158ea2d835dd3177b62ad0446eb30d67))
* revert drm type definition change ([#3409](https://github.com/react-native-video/react-native-video/issues/3409)) ([fbb5654](https://github.com/react-native-video/react-native-video/commit/fbb5654a8e075a2b33ae17bd322bb79b1f459d53))
# [6.0.0-beta.1](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.0...v6.0.0-beta.1) (2023-12-02)
### Bug Fixes
* **android:** ads build and enable ads in android sample ([#3376](https://github.com/react-native-video/react-native-video/issues/3376)) ([fe89122](https://github.com/react-native-video/react-native-video/commit/fe89122524826093689118a4515802d83ca88679))
* **android:** fix leak caused by removing lifecycle listener too early ([#3380](https://github.com/react-native-video/react-native-video/issues/3380)) ([0c0f317](https://github.com/react-native-video/react-native-video/commit/0c0f3174cb37d3c664a345ea00fcbaafffcd4b10))
* **android:** revert media3 update, back to 1.1.1 ([#3369](https://github.com/react-native-video/react-native-video/issues/3369)) ([5beef38](https://github.com/react-native-video/react-native-video/commit/5beef383cba13d3ac471bfde27e4acfaa19adfec))
* **ios:** check for ios url query encoding ([#3384](https://github.com/react-native-video/react-native-video/issues/3384)) ([de4159f](https://github.com/react-native-video/react-native-video/commit/de4159f0c2825a58d88f3882215da4bf51fdbeb2))
* **ios:** fix pip(when player doesn't fill screen) ([#3363](https://github.com/react-native-video/react-native-video/issues/3363)) ([11f6201](https://github.com/react-native-video/react-native-video/commit/11f62013e33939ce3f78ec7cf40e4da464afa824))
### Features
* **ad:** add data to onReceiveAdEvent ([#3378](https://github.com/react-native-video/react-native-video/issues/3378)) ([d05231d](https://github.com/react-native-video/react-native-video/commit/d05231d76b87e2f65bc7648bfb81d01e4054b2de))
* add AdEvent enum to have an exhaustive list of all possible AdEvent values ([#3374](https://github.com/react-native-video/react-native-video/issues/3374)) ([b3744f9](https://github.com/react-native-video/react-native-video/commit/b3744f9b9f25b469fb8b0828e3762842bd5026de))
* add onAdError event listener ([#3381](https://github.com/react-native-video/react-native-video/issues/3381)) ([596c02d](https://github.com/react-native-video/react-native-video/commit/596c02d2b3b5175e1653844c39a47ecfd5e23163))
* **android:** bump media3 version from v1.1.1 to v1.2.0 ([#3362](https://github.com/react-native-video/react-native-video/issues/3362)) ([17dbf6e](https://github.com/react-native-video/react-native-video/commit/17dbf6e8264c5c6bed10ff23d96c2b7296a49651))
* implement startPosition ([#3355](https://github.com/react-native-video/react-native-video/issues/3355)) ([2648502](https://github.com/react-native-video/react-native-video/commit/2648502b364c2802f5a2a7302c31200905c0a807))
# [6.0.0-beta.1](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.0...v6.0.0-beta.1) (WIP)
* **android:** fix leak caused by removing lifecycle listener too early ([#3380](https://github.com/react-native-video/react-native-video/pull/3380))
# [6.0.0-beta.0](https://github.com/react-native-video/react-native-video/compare/v6.0.0-alpha.11...v6.0.0-beta.0) (2023-11-18)
### Bug Fixes
* **example:** remove dependency loop ([#3353](https://github.com/react-native-video/react-native-video/issues/3353)) ([211c3c7](https://github.com/react-native-video/react-native-video/commit/211c3c7d08c8438bfca3350f0070cfec0ae5bc56))
* **ios:** change isPlaybackLikelyToKeepUp check ([#3357](https://github.com/react-native-video/react-native-video/issues/3357)) ([1ba93f9](https://github.com/react-native-video/react-native-video/commit/1ba93f9e9d33f653f0e01214f220e1e5eda819f5))
* **ios:** fix cache playerItemPrepareText type ([#3358](https://github.com/react-native-video/react-native-video/issues/3358)) ([0e23952](https://github.com/react-native-video/react-native-video/commit/0e23952cea5c71324a2f5eea0383c4db9e02504b))
* **ios:** fix external text tracks crashes with m3u8 files ([#3330](https://github.com/react-native-video/react-native-video/issues/3330)) ([782e7e0](https://github.com/react-native-video/react-native-video/commit/782e7e0df1386ef0aad3f00d73171d04d6cf725d))
* update onError definition to match implementation ([#3349](https://github.com/react-native-video/react-native-video/issues/3349)) ([fdbd6a6](https://github.com/react-native-video/react-native-video/commit/fdbd6a6ba8aef2da854ff7b0fbf25085ce6983e3))
### Features
* **android:** replace deprecated ExoPlayer2 with AndroidX media3 ([#3337](https://github.com/react-native-video/react-native-video/issues/3337)) ([f2e80e9](https://github.com/react-native-video/react-native-video/commit/f2e80e9f2d1acc97080d48913802639dd2f38346))
# [6.0.0-alpha.11](https://github.com/react-native-video/react-native-video/compare/v6.0.0-alpha.10...v6.0.0-alpha.11) (2023-11-15)
### Bug Fixes
* fix bad package release process ([#3347](https://github.com/react-native-video/react-native-video/issues/3347)) ([f961f95](https://github.com/react-native-video/react-native-video/commit/f961f952a483192ee3de1f7bae59419ec6ddc5b7))
# [6.0.0-alpha.10](https://github.com/react-native-video/react-native-video/compare/v6.0.0-alpha.9...v6.0.0-alpha.10) (2023-11-13)
### Bug Fixes
* fixes where Android's muted prop behavior differs from iOS ([#3339](https://github.com/react-native-video/react-native-video/issues/3339)) ([8fbdc28](https://github.com/react-native-video/react-native-video/commit/8fbdc28a73a0b3ffd3691ef0c8cf523c760ae288))
* **ios:** fix wrong fullscreen method definition ([#3338](https://github.com/react-native-video/react-native-video/issues/3338)) ([7f49b56](https://github.com/react-native-video/react-native-video/commit/7f49b560278262fb4276f931404c70672a6445c8))
* **ios:** player is frozen after re-focusing on the app ([#3326](https://github.com/react-native-video/react-native-video/issues/3326)) ([722ae34](https://github.com/react-native-video/react-native-video/commit/722ae3477a68aecb812b26d71ea22a17dda71f50))
### Features
* add `onVolumeChange` event ([#3322](https://github.com/react-native-video/react-native-video/issues/3322)) ([cdbc856](https://github.com/react-native-video/react-native-video/commit/cdbc85638789da0002cdadb13190963d4c1332c2))
* add release-it ([#3342](https://github.com/react-native-video/react-native-video/issues/3342)) ([da27089](https://github.com/react-native-video/react-native-video/commit/da270891fbce485bb132825a336638f2af98408d))
* **ios:** add onBandwidthUpdate event ([#3331](https://github.com/react-native-video/react-native-video/issues/3331)) ([9054db3](https://github.com/react-native-video/react-native-video/commit/9054db35d7d5e4e6d54739fc9349576c03522d7c))
## Changelog
## Next
- Android, iOS: add onVolumeChange event #3322
- iOS: Externally loaded text tracks not loading properly [#3461](https://github.com/react-native-video/react-native-video/pull/3461)
### Version 6.0.0-alpha.9
- All: add built-in typescript support [#3266](https://github.com/react-native-video/react-native-video/pull/3266)
- All: update documentation generation [#3296](https://github.com/react-native-video/react-native-video/pull/3296)
- **BREAKING CHANGE**❗Android: update isCodecSupported to return enum [#3254](https://github.com/react-native-video/react-native-video/pull/3254)
- Android: use explicit not-exported flag for AudioBecomingNoisyReceiver [#3327](https://github.com/react-native-video/react-native-video/pull/3327)
- Android: remove kotlin-android-extensions [#3299](https://github.com/react-native-video/react-native-video/pull/3299)
- Android: ensure audio volume is changed in UI thread [3292](https://github.com/react-native-video/react-native-video/pull/3292)
- Android: multiple internal refactor and switch to kotlin
- Android: refactor log management and add an option to increase log verbosity [#3277](https://github.com/react-native-video/react-native-video/pull/3277)
- iOS: Fix audio session category when not using the audioOutput prop
- iOS: implement onPlaybackStateChanged callback [#3307](https://github.com/react-native-video/react-native-video/pull/3307)
- iOS: remove false calls at onPlaybackRateChange [#3306](https://github.com/react-native-video/react-native-video/pull/3306)
- iOS: audio does not work with headphones [#3284](https://github.com/react-native-video/react-native-video/pull/3284)
- iOS: Resuming video ad after closing the in-app browser on iOS [#3275](https://github.com/react-native-video/react-native-video/pull/3275)
- iOS, Android: expose playback functions to ref [#3245](https://github.com/react-native-video/react-native-video/pull/3245)
- tvOS: fix build: [#3276](https://github.com/react-native-video/react-native-video/pull/3276)
- Windows: fix build error from over-specified SDK version [#3246](https://github.com/react-native-video/react-native-video/pull/3246)
- Windows: fix `onError` not being raised [#3247](https://github.com/react-native-video/react-native-video/pull/3247)
### Version 6.0.0-alpha.8
- All: Playing audio over earpiece [#2887](https://github.com/react-native-video/react-native-video/issues/2887)
- All: Prepare for fabric [#3175](https://github.com/react-native-video/react-native-video/pull/3175) [#]()
- iOS: Fix Pip [#3221](https://github.com/react-native-video/react-native-video/pull/3221)
- iOS: Fix regression in presentFullscreenPlayer & dismissFullscreenPlayer [#3230](https://github.com/react-native-video/react-native-video/pull/3230)
- tvOS: Fix build [#3207](https://github.com/react-native-video/react-native-video/pull/3207)
- tvOS: Add sample [#3208](https://github.com/react-native-video/react-native-video/pull/3208)
- tvOS: Allow chapter customization [#3216](https://github.com/react-native-video/react-native-video/pull/3216)
- doc: Fix internal links [#3229](https://github.com/react-native-video/react-native-video/pull/3229)
### Version 6.0.0-alpha.7
- All: clean JS warnings (https://github.com/react-native-video/react-native-video/pull/3183)
- Android: Add shutterView color configurtion (https://github.com/react-native-video/react-native-video/pull/3179)
- Android: React native 0.73 support (https://github.com/react-native-video/react-native-video/pull/3163)
- Android: Fix memory leaks from AudioManager [#3123](https://github.com/react-native-video/react-native-video/pull/3123)
- Android: Fixed syntax error [#3182](https://github.com/react-native-video/react-native-video/issues/3182)
- iOS: Fix freeze at playback startup (https://github.com/react-native-video/react-native-video/pull/3173)
- iOS: Various safety checks (https://github.com/react-native-video/react-native-video/pull/3168)
### Version 6.0.0-alpha.6
- Feature: Video range support [#3030](https://github.com/react-native-video/react-native-video/pull/3030)
- iOS: remove undocumented `currentTime` property [#3064](https://github.com/react-native-video/react-native-video/pull/3064)
- iOS: make sure that the audio in ads is muted when the player is muted. [#3068](https://github.com/react-native-video/react-native-video/pull/3077)
- iOS: make IMA build optionnal
### Version 6.0.0-alpha.5
- iOS: ensure controls are not displayed when disabled by user [#3017](https://github.com/react-native-video/react-native-video/pull/3017)
- iOS: app crashes on call to presentFullScreenPlayer [#2808](https://github.com/react-native-video/react-native-video/pull/2971)
- Android: Fix publicated progress handler causing duplicated progress event [#2972](https://github.com/react-native-video/react-native-video/pull/2972)
- Android: Fix audio/Subtitle tracks selection [#2979](https://github.com/react-native-video/react-native-video/pull/2979)
- Android: add new events on tracks changed to be notified of audio/text/video Tracks update during playback [2806](https://github.com/react-native-video/react-native-video/pull/2806)
- Feature: Add VAST support for AVOD [#2923](https://github.com/react-native-video/react-native-video/pull/2923)
- Sample: Upgrade react-native version of basic sample [#2960](https://github.com/react-native-video/react-native-video/pull/2960)
### Version 6.0.0-alpha.4
- ensure src is always provided to native player even if it is invalid [#2857](https://github.com/react-native-video/react-native-video/pull/2857)
- Sample: Add react-native-video controls support [#2852](https://github.com/react-native-video/react-native-video/pull/2852)
- Android: Switch Google's maven repository to default `google()` [#2860](https://github.com/react-native-video/react-native-video/pull/2860)
- Android: Implement focusable prop so the video view can toggle whether it is focusable for non-touch devices [#2819](https://github.com/react-native-video/react-native-video/issues/2819)
- Android: fix linter warning [#2891] (https://github.com/react-native-video/react-native-video/pull/2891)
- Fix iOS RCTSwiftLog naming collision [#2868](https://github.com/react-native-video/react-native-video/issues/2868)
- Added "homepage" to package.json [#2882](https://github.com/react-native-video/react-native-video/pull/2882)
- Fix regression when fullscreen prop is used combined with controls [#2911](https://github.com/react-native-video/react-native-video/pull/2911)
- Fix: memory leak issue on iOS [#2907](https://github.com/react-native-video/react-native-video/pull/2907)
- Fix setting text tracks before player is initialized on iOS [#2935](https://github.com/react-native-video/react-native-video/pull/2935)
### Version 6.0.0-alpha.3
- Fix ios build [#2854](https://github.com/react-native-video/react-native-video/pull/2854)
### Version 6.0.0-alpha.2
- Upgrade ExoPlayer to 2.18.1 [#2846](https://github.com/react-native-video/react-native-video/pull/2846)
- Feature add new APIs to query supported features of device decoder (widevine level & codec capabilities) on android [#2740](https://github.com/react-native-video/react-native-video/pull/2740)
- Feature add support of subtitle styling on android [#2759](https://github.com/react-native-video/react-native-video/pull/2759)
- Fix Android #2690 ensure onEnd is not sent twice [#2690](https://github.com/react-native-video/react-native-video/issues/2690)
- Fix Exoplayer progress not reported when paused [#2664](https://github.com/react-native-video/react-native-video/pull/2664)
- Call playbackRateChange onPlay and onPause [#1493](https://github.com/react-native-video/react-native-video/pull/1493)
- Fix being unable to disable sideloaded texttracks in the AVPlayer [#2679](https://github.com/react-native-video/react-native-video/pull/2679)
- Fixed crash when iOS seek method called reject on the promise [#2743](https://github.com/react-native-video/react-native-video/pull/2743)
- Fix maxBitRate property being ignored on Android [#2670](https://github.com/react-native-video/react-native-video/pull/2670)
- Fix crash when the source is a cameraroll [#2639] (https://github.com/react-native-video/react-native-video/pull/2639)
- Fix IOS UI frame drop on loading video [#2848] (https://github.com/react-native-video/react-native-video/pull/2848)
### Version 6.0.0-alpha.1
- Remove Android MediaPlayer support [#2724](https://github.com/react-native-video/react-native-video/pull/2724)
**WARNING**: when switching from older version to V6, you need to remove all refrerences of android-exoplayer. This android-exoplayer folder has been renamed to android. Exoplayer is now the only player implementation supported.
- Replace Image.propTypes with ImagePropTypes. [#2718](https://github.com/react-native-video/react-native-video/pull/2718)
- Fix iOS build caused by type mismatch [#2720](https://github.com/react-native-video/react-native-video/pull/2720)
- ERROR TypeError: undefined is not an object (evaluating '_reactNative.Image.propTypes.resizeMode') [#2714](https://github.com/react-native-video/react-native-video/pull/2714)
- Fix video endless loop when repeat set to false or not specified. [#2329](https://github.com/react-native-video/react-native-video/pull/2329)
### Version 6.0.0-alpha.0
- Support disabling buffering [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Fix AudioFocus bug that could cause the player to stop responding to play/pause in some instances. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Fix player crashing when it is being cleared. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Add support for customising back buffer duration and handle network errors gracefully to prevent releasing the player when network is lost. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Allow player to be init before source is provided, and later update once a source is provided. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Adds handling for providing a empty source in order to stop playback and clear out any existing content [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Add support for detecting if format is supported and exclude unsupported resolutions from auto quality selection and video track info in RN. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Improve error handling [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Add support for L1 to L3 Widevine fallback if playback fails initially. [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Reduce buffer size based on available heap [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Force garbage collection when there is no available memory [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Improve memory usage [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Support disabling screen recording [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Improved error capturing [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Fix DRM init crashes [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Improve progress reporting [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Fix progress loss when network connection is regained [#2689](https://github.com/react-native-video/react-native-video/pull/2689)
- Add Google's maven repository to avoid build error [#2552](https://github.com/react-native-video/react-native-video/pull/2552)
- Fix iOS 15.4 HLS playback race condition [#2633](https://github.com/react-native-video/react-native-video/pull/2633)
- Fix app crash from NPE in Exoplayer error handler [#2575](https://github.com/react-native-video/react-native-video/pull/2575)
- Fix default closed captioning behavior for Android ExoPlayer [#2181](https://github.com/react-native-video/react-native-video/pull/2181)
- Disable pipController init if pictureInPicture is false [#2645](https://github.com/react-native-video/react-native-video/pull/2645)
- Make sure modifiers are applied before playing [#2395](https://github.com/react-native-video/react-native-video/pull/2395)
- Better support newer versions of RNW (64 and newer) [#2535](https://github.com/react-native-video/react-native-video/pull/2535)
- Fix nil string uri parameter error [#695](https://github.com/react-native-video/react-native-video/pull/695)
- (Breaking) Bump shaka-player to 3.3.2 [#2587](https://github.com/react-native-video/react-native-video/pull/2587)
- Improve basic player example on android [#2662](https://github.com/react-native-video/react-native-video/pull/2662)
- Ensure we always use `hideShutterView` before showing the `shutterView` on Android [#2609](https://github.com/react-native-video/react-native-video/pull/2609)
- Convert iOS implementation to Swift [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Add iOS support for decoding offline sources [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Upgrade ExoPlayer to 2.17.1 [#2498](https://github.com/react-native-video/react-native-video/pull/2498)
- Fix volume reset issue in exoPlayer [#2371](https://github.com/react-native-video/react-native-video/pull/2371)
- Change WindowsTargetPlatformVersion to 10.0 [#2706](https://github.com/react-native-video/react-native-video/pull/2706)
- Fixed Android seeking bug [#2712](https://github.com/react-native-video/react-native-video/pull/2712)
- Fixed `onReadyForDisplay` not being called [#2721](https://github.com/react-native-video/react-native-video/pull/2721)
- Fix type of `_eventDispatcher` on iOS target to match `bridge.eventDispatcher()` [#2720](https://github.com/react-native-video/react-native-video/pull/2720)
### Version 5.2.0
- Fix for tvOS native audio menu language selector
- Update ExoPlayer to allow pre-init and content clear [#2412] (https://github.com/react-native-video/react-native-video/pull/2412)
- iOS rate is reset to 1.0 after play/pause [#2167] (https://github.com/react-native-video/react-native-video/pull/2167)
- Upgrade ExoPlayer to 2.13.2 [#2317] (https://github.com/react-native-video/react-native-video/pull/2317)
- Fix AudioFocus pausing video when attempting to play [#2311] (https://github.com/react-native-video/react-native-video/pull/2311)
### Version 5.1.0-alpha9
- Add ARM64 support for windows [#2137](https://github.com/react-native-community/react-native-video/pull/2137)
- Fix deprecated API bug for windows [#2119](https://github.com/react-native-video/react-native-video/pull/2119)
- Added `rate` property and autolinking support for windows [#2206](https://github.com/react-native-video/react-native-video/pull/2206)
### Version 5.1.0-alpha8
- Fixing ID3 Frame Error When Receiving EventMessage in TimedMetadata [#2116](https://github.com/react-native-community/react-native-video/pull/2116)
### Version 5.1.0-alpha7
- Basic support for DRM on iOS and Android [#1445](https://github.com/react-native-community/react-native-video/pull/1445)
### Version 5.1.0-alpha6
- Fix iOS bug which would break size of views when video is displayed with controls on a non full-screen React view. [#1931](https://github.com/react-native-community/react-native-video/pull/1931)
- Fix video dimensions being undefined when playing HLS in ios. [#1992](https://github.com/react-native-community/react-native-video/pull/1992)
- Add support for audio mix with other apps for iOS. [#1978](https://github.com/react-native-community/react-native-video/pull/1978)
- Properly implement pending seek for iOS. [#1994](https://github.com/react-native-community/react-native-video/pull/1994)
- Added `preferredForwardBufferDuration` (iOS) - the duration the player should buffer media from the network ahead of the playhead to guard against playback disruption. (#1944)
- Added `currentPlaybackTime` (Android ExoPlayer, iOS) - when playing an HLS live stream with a `EXT-X-PROGRAM-DATE-TIME` tag configured, then this property will contain the epoch value in msec. (#1944)
- Added `trackId` (Android ExoPlayer) - Configure an identifier for the video stream to link the playback context to the events emitted. (#1944)
- Added preventsDisplaySleepDuringVideoPlayback (#2019)
- Reverted the JS fullscreening for Android. [#2013](https://github.com/react-native-community/react-native-video/pull/2013)
- Set iOS request headers without needing to edit RCTVideo.m. [#2014](https://github.com/react-native-community/react-native-video/pull/2014)
- Fix exoplayer aspect ratio update on source changes [#2053](https://github.com/react-native-community/react-native-video/pull/2053)
### Version 5.1.0-alpha5
- Add support for react-native Windows Cpp/WinRT [#1893]((https://github.com/react-native-community/react-native-video/pull/1893))
### Version 5.1.0-alpha4
- Fix android play/pause bug related to full-screen mode [#1916](https://github.com/react-native-community/react-native-video/pull/1916)
### Version 5.1.0-alpha3
- Improve Android Audio Focus [#1897](https://github.com/react-native-community/react-native-video/pull/1897)
### Version 5.1.0-alpha2
- Added support for full-screen functionality in Android Exoplayer [#1730](https://github.com/react-native-community/react-native-video/pull/1730)
### Version 5.1.0-alpha1
- Fixed Exoplayer doesn't work with mute=true (Android). [#1696](https://github.com/react-native-community/react-native-video/pull/1696)
- Added support for automaticallyWaitsToMinimizeStalling property (iOS) [#1723](https://github.com/react-native-community/react-native-video/pull/1723)
- Bump Exoplayer to 2.10.4, remove deprecated usages of Exoplayer methods (Android). [#1753](https://github.com/react-native-community/react-native-video/pull/1753)
- Preserve Exoplayer BandwidthMeter instance across video plays, this should noticeably improve streaming bandwidth detection (Android).
### Version 5.0.2
- Fix crash when RCTVideo's superclass doesn't observe the keyPath 'frame' (iOS) [#1720](https://github.com/react-native-community/react-native-video/pull/1720)
### Version 5.0.1
- Fix AndroidX Support bad merge
### Version 5.0.0 [Deprecated]
- AndroidX Support
### Version 4.4.4
- Handle racing conditions when props are settled on Exoplayer
### Version 4.4.3
- Fix mute/unmute when controls are present (iOS) [#1654](https://github.com/react-native-community/react-native-video/pull/1654)
- Fix Android videos being able to play with background music/audio from other apps.
- Fixed memory leak on iOS when using `controls` [#1647](https://github.com/react-native-community/react-native-video/pull/1647)
- (Android) Update gradle and target SDK [#1629](https://github.com/react-native-community/react-native-video/pull/1629)
- Fix iOS stressed mount/unmount crash [#1646](https://github.com/react-native-community/react-native-video/pull/1646)
### Version 4.4.2
- Change compileOnly to implementation on gradle (for newer gradle versions and react-native 0.59 support) [#1592](https://github.com/react-native-community/react-native-video/pull/1592)
- Replaced RCTBubblingEventBlock events by RCTDirectEventBlock to avoid event name collisions [#1625](https://github.com/react-native-community/react-native-video/pull/1625)
- Added `onPlaybackRateChange` to README [#1578](https://github.com/react-native-community/react-native-video/pull/1578)
- Added `onReadyForDisplay` to README [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
- Improved handling of poster image. Fixes bug with displaying video and poster simultaneously. [#1627](https://github.com/react-native-community/react-native-video/pull/1627)
- Fix background audio stopping on iOS when using `controls` [#1614](https://github.com/react-native-community/react-native-video/pull/1614)
### Version 4.4.1
- Fix tvOS picture-in-picture compilation regression [#1518](https://github.com/react-native-community/react-native-video/pull/1518)
- fullscreen rotation issues with iOS built-in controls [#1441](https://github.com/react-native-community/react-native-video/pull/1441)
- Fix player freeze when playing audio files on ExoPlayer [#1529](https://github.com/react-native-community/react-native-video/pull/1529)
### Version 4.4.0
- Fix runtime warning by replacing `UIManager.RCTVideo` with `UIManager.getViewManagerConfig('RCTVideo')` (and ensuring backwards compat) [#1487](https://github.com/react-native-community/react-native-video/pull/1487)
- Fix loading package resolved videos when using video-caching [#1438](https://github.com/react-native-community/react-native-video/pull/1438)
- Fix "message sent to deallocated instance" crash on ios [#1482](https://github.com/react-native-community/react-native-video/pull/1482)
- Display a warning when source is empty [#1478](https://github.com/react-native-community/react-native-video/pull/1478)
- Don't crash on iOS for an empty source [#1246](https://github.com/react-native-community/react-native-video/pull/1246)
- Recover from from transient internet failures when loading on ExoPlayer [#1448](https://github.com/react-native-community/react-native-video/pull/1448)
- Add controls support for ExoPlayer [#1414](https://github.com/react-native-community/react-native-video/pull/1414)
- Fix check for text tracks when iOS caching enabled [#1387](https://github.com/react-native-community/react-native-video/pull/1387)
- Add support for Picture in Picture on iOS [#1325](https://github.com/react-native-community/react-native-video/pull/1325)
- Fix UIManager undefined variable [#1488](https://github.com/react-native-community/react-native-video/pull/1488)
### Version 4.3.0
- Fix iOS video not displaying after switching source [#1395](https://github.com/react-native-community/react-native-video/pull/1395)
- Add the filterEnabled flag, fixes iOS video start time regression [#1384](https://github.com/react-native-community/react-native-video/pull/1384)
- Fix text not appearing in release builds of Android apps [#1373](https://github.com/react-native-community/react-native-video/pull/1373)
- Update to ExoPlayer 2.9.3 [#1406](https://github.com/react-native-community/react-native-video/pull/1406)
- Add video track selection & onBandwidthUpdate [#1199](https://github.com/react-native-community/react-native-video/pull/1199)
- Recovery from transient internet failures and props to configure the custom retry count [#1448](https://github.com/react-native-community/react-native-video/pull/1448)
### Version 4.2.0
- Don't initialize filters on iOS unless a filter is set. This was causing a startup performance regression [#1360](https://github.com/react-native-community/react-native-video/pull/1360)
- Support setting the maxBitRate [#1310](https://github.com/react-native-community/react-native-video/pull/1310)
- Fix useTextureView not defaulting to true [#1383](https://github.com/react-native-community/react-native-video/pull/1383)
- Fix crash on MediaPlayer w/ Android 4.4 & avoid memory leak [#1328](https://github.com/react-native-community/react-native-video/pull/1328)
### Version 4.1.0
- Generate onSeek on Android ExoPlayer & MediaPlayer after seek completes [#1351](https://github.com/react-native-community/react-native-video/pull/1351)
- Remove unneeded onVideoSaved event [#1350](https://github.com/react-native-community/react-native-video/pull/1350)
- Disable AirPlay if sidecar text tracks are enabled [#1304](https://github.com/react-native-community/react-native-video/pull/1304)
- Add possibility to remove black screen while video is loading in Exoplayer [#1355](https://github.com/react-native-community/react-native-video/pull/1355)
### Version 4.0.1
- Add missing files to package.json [#1342](https://github.com/react-native-community/react-native-video/pull/1342)
### Version 4.0.0
- Partial support for timed metadata on Android MediaPlayer [#707](https://github.com/react-native-community/react-native-video/pull/707)
- Support video caching for iOS [#955](https://github.com/react-native-community/react-native-video/pull/955)
- Video caching cleanups [#1172](https://github.com/react-native-community/react-native-video/pull/1172)
- Add ipod-library support [#926](https://github.com/react-native-community/react-native-video/pull/926/files)
- Fix crash on ExoPlayer when there are no audio tracks [#1233](https://github.com/react-native-community/react-native-video/pull/1233)
- Reduce package size [#1231](https://github.com/react-native-community/react-native-video/pull/1231)
- Remove unnecessary import in TextTrackType [#1229](https://github.com/react-native-community/react-native-video/pull/1229)
- Prevent flash between poster and video [#1167](https://github.com/react-native-community/react-native-video/pull/1167)
- Support react-native-dom [#1253](https://github.com/react-native-community/react-native-video/pull/1253)
- Update to ExoPlayer 2.8.2. Android SDK 26 now required [#1170](https://github.com/react-native-community/react-native-video/pull/1170)
- Update to ExoPlayer 2.8.4 [#1266](https://github.com/react-native-community/react-native-video/pull/1266)
- Add fullscreenOrientation option for iOS [#1215](https://github.com/react-native-community/react-native-video/pull/1215)
- Update to ExoPlayer 2.9.0 [#1285](https://github.com/react-native-community/react-native-video/pull/1285)
- Switch useTextureView to default to `true` [#1286](https://github.com/react-native-community/react-native-video/pull/1286)
- Re-add fullscreenAutorotate prop [#1303](https://github.com/react-native-community/react-native-video/pull/1303)
- Make seek throw a useful error for NaN values [#1283](https://github.com/react-native-community/react-native-video/pull/1283)
- Video Filters and Save Video [#1306](https://github.com/react-native-community/react-native-video/pull/1306)
- Fix: volume should not change on onAudioFocusChange event [#1327](https://github.com/react-native-community/react-native-video/pull/1327)
- Update ExoPlayer to 2.9.1 and OkHTTP to 3.12.0 [#1338](https://github.com/react-native-community/react-native-video/pull/1338)
### Version 3.2.0
- Basic fullscreen support for Android MediaPlayer [#1138](https://github.com/react-native-community/react-native-video/pull/1138)
- Simplify default Android SDK code [#1145](https://github.com/react-native-community/react-native-video/pull/1145) [#1146](https://github.com/react-native-community/react-native-video/pull/1146)
- Various iOS sideloaded text track fixes [#1157](https://github.com/react-native-community/react-native-video/pull/1157)
- Fix #1150 where assets with bundled assets don't work on iOS in release mode [#1162](https://github.com/react-native-community/react-native-video/pull/1162)
- Support configuring the buffer on Android ExoPlayer [#1160](https://github.com/react-native-community/react-native-video/pull/1160)
- Prevent sleep from sleeping while videos are playing on Android MediaPlayer [#1117](https://github.com/react-native-community/react-native-video/pull/1117)
- Update NewtonSoft JSON to match react-native-windows version [#1169](https://github.com/react-native-community/react-native-video/pull/1169)
### Version 3.1.0
- Support sidecar text tracks on iOS [#1109](https://github.com/react-native-community/react-native-video/pull/1109)
- Support onAudioBecomingNoisy on iOS [#1131](https://github.com/react-native-community/react-native-video/pull/1131)
### Version 3.0
- Inherit Android buildtools and SDK version from the root project [#1081](https://github.com/react-native-community/react-native-video/pull/1081)
- Automatically play on ExoPlayer when the paused prop is not set [#1083](https://github.com/react-native-community/react-native-video/pull/1083)
- Preserve Android MediaPlayer paused prop when backgrounding [#1082](https://github.com/react-native-community/react-native-video/pull/1082)
- Support specifying headers on ExoPlayer as part of the source [#805](https://github.com/react-native-community/react-native-video/pull/805)
- Prevent iOS onLoad event during seeking [#1088](https://github.com/react-native-community/react-native-video/pull/1088)
- ExoPlayer playableDuration incorrect [#1089](https://github.com/react-native-community/react-native-video/pull/1089)
### Version 2.3.1
- Revert PR to inherit Android SDK versions from root project. Re-add in 3.0 [#1080](https://github.com/react-native-community/react-native-video/pull/1080)
### Version 2.3.0
- Support allowsExternalPlayback on iOS [#1057](https://github.com/react-native-community/react-native-video/pull/1057)
- Inherit Android buildtools and SDK version from the root project [#999](https://github.com/react-native-community/react-native-video/pull/999)
- Fix bug that caused ExoPlayer to start paused if playInBackground was set [#833](https://github.com/react-native-community/react-native-video/pull/833)
- Fix crash if clearing an observer on iOS that was already cleared [#1075](https://github.com/react-native-community/react-native-video/pull/1075)
- Add audioOnly prop for music files [#1039](https://github.com/react-native-community/react-native-video/pull/1039)
- Support seeking with more exact tolerance on iOS [#1076](https://github.com/react-native-community/react-native-video/pull/1076)
### Version 2.2.0
- Text track selection support for iOS & ExoPlayer [#1049](https://github.com/react-native-community/react-native-video/pull/1049)
- Support outputting to a TextureView on Android ExoPlayer [#1058](https://github.com/react-native-community/react-native-video/pull/1058)
- Support changing the left/right balance on Android MediaPlayer [#1051](https://github.com/react-native-community/react-native-video/pull/1051)
- Prevent multiple onEnd notifications on iOS [#832](https://github.com/react-native-community/react-native-video/pull/832)
- Fix doing a partial swipe on iOS causing a black screen [#1048](https://github.com/react-native-community/react-native-video/pull/1048)
- Fix crash when switching to a new source on iOS [#974](https://github.com/react-native-community/react-native-video/pull/974)
- Add cookie support for ExoPlayer [#922](https://github.com/react-native-community/react-native-video/pull/922)
- Remove ExoPlayer onMetadata that wasn't being used [#1040](https://github.com/react-native-community/react-native-video/pull/1040)
- Fix bug where setting the progress interval on iOS didn't work [#800](https://github.com/react-native-community/react-native-video/pull/800)
- Support setting the poster resize mode [#595](https://github.com/react-native-community/react-native-video/pull/595)

133
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,133 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@@ -1,31 +1,119 @@
## Issues
# Contributing
* New issues are reviewed and if they require additional work will be marked with the [`triage needed`](https://github.com/react-native-video/react-native-video/labels/triage%20needed) label. This is an open call for help from the community to verify the issue and help categorize it. If an issue stays in this state for a long time, it will be closed unresolved.
* Once an issue has been reviewed it will be labeled with [`help wanted`](https://github.com/react-native-video/react-native-video/labels/help%20wanted) to indicate it is ready to be worked on. Please wait for this label before submitting a PR to avoid spending time on something that is likely to be rejected.
Contributions are always welcome, no matter how large or small!
## Cleanup
We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md).
* Given the history of this project, we are going to be more aggressive than usual in keeping things clean. We are working with limited resources and do not want to return to the 1000+ open issues state. This is not meant to be disrespectful or hostile. It is just a way to keep the limited resources we have focused. If your issue was closed prematurely, just chime in and engage!
* Issues and pull requests that become stale (60 days of inactivity) will be closed unless assigned and show progress.
* If the issue creator fails to provide additional information within a week when asked, we may close the issue to keep things tidy (but you can always comment back and we can reopen).
## Development workflow
## Pull Requests
This project is a monorepo managed using [Bun workspaces](https://bun.sh/docs/install/workspaces). It contains the following packages:
* Please open an issue before opening a PR to make sure the proposed change is wanted and is likely to be merged. We don't want you to waste your time!
* Pull requests require 1-3 approved reviews to be merged.
* The number of reviews depends on the complexity by adding up (max of 3):
* `1` reviewer for each PR
* `1` if more than 3 files and/or 30 lines of code changed
* `1` for each native platform code changes involved
- An Library package in the `packages/react-native-video` directory.
- An example app in the `example/` directory.
For example, a single file JS code change requires 1 review while a 3 files iOS code change requires 3 reviews. As soon as the reviews show up as approved without any requested changes, the PR will be merged into the next milestone.
To get started with the project, run `bun install` in the root directory to install the required dependencies for each package:
* Reviewers will be asked to assign a risk level when they are done from 1 (super safe) to 5 (super risky). A release with any risk level 4 or 5 will be published as a major version, otherwise as a patch or minor based on the changes. Prepare for some large version increments while we get more comfortable... (but remember versions are free).
```sh
bun install
```
* If you have time to help out, look for the [`review requested`](https://github.com/react-native-video/react-native-video/labels/review%20requested) label. It will have another numeric label with it (`1`, `2`, or `3` indicating how many more reviews are needed to merge).
> Since the project relies on Bun workspaces, you cannot use [`npm`](https://github.com/npm/cli) or [`yarn`](https://yarnpkg.com/) for development.
## Releases
The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make.
* Aim for a bi-weekly (every other week) release to flush out whatever was approved and merge. Most people use this with a lock file (and if you don't you are doing it wrong) and should not have any issues with new bugs showing up. This is already a high risk dependency which must be tested well before going into production. Let's take advantage of that and move faster.
It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app.
Please do not harass people to review your pull request! You can tag those you feel have relevant experience but please don't abuse this as people will unfollow or mute the project if they are called too many times!
If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/VideoExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-video`.
To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-video` under `Android`.
You can use various commands from the root directory to work with the project.
To start the packager:
```sh
bun example start
```
To run the example app on Android:
```sh
bun example android
```
To run the example app on iOS:
```sh
bun example ios
```
Make sure your code passes TypeScript and ESLint. Run the following to verify:
```sh
bun typecheck
bun lint
```
To fix formatting errors, run the following:
```sh
bun lint --fix
```
### Commit message convention
We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
- `fix`: bug fixes, e.g. fix crash due to deprecated method.
- `feat`: new features, e.g. add new method to the module.
- `refactor`: code refactor, e.g. migrate from class components to hooks.
- `docs`: changes into documentation, e.g. add usage example for the module..
- `test`: adding or updating tests, e.g. add integration tests using detox.
- `chore`: tooling changes, e.g. change CI config.
Our pre-commit hooks verify that your commit message matches this format when committing.
### Linting and tests
[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)
We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.
Our pre-commit hooks verify that the linter and tests pass when committing.
### Publishing to npm
We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc.
To publish new versions, run the following:
```sh
bun release
```
### Scripts
The `package.json` file contains various scripts for common tasks:
- `bun install`: setup project by installing dependencies.
- `bun typecheck`: type-check files with TypeScript.
- `bun lint`: lint files with ESLint.
- `bun example start`: start the Metro server for the example app.
- `bun example android`: run the example app on Android.
- `bun example ios`: run the example app on iOS.
### Sending a pull request
> **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).
When you're sending a pull request:
- Prefer small pull requests focused on one change.
- Verify that linters and tests are passing.
- Review the documentation to make sure it looks good.
- Follow the pull request template when opening a pull request.
- For pull requests that change the API or implementation, discuss with maintainers first by opening an issue.
### License
By contributing to this project, you agree that your contributions will be licensed under the [MIT License](LICENSE).

View File

@@ -1,8 +1,6 @@
MIT License
Copyright (c) 2016-2022 Project contributors
Copyright (c) 2016 Brent Vatne, Baris Sencan
Copyright (c) 2024-2025 TheWidlarzGroup
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights

200
README.md
View File

@@ -1,63 +1,163 @@
# react-native-video
🎬 `<Video>` component for React Native
[![React Native Video Component](./docs/static/baners/rnv-banner.png)](https://thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_id=banner)
> **Note:** version 5.2.1 won't have any updates. We are currently working on making a 6.0.0 fully stable
The most battle-tested open-source video player component for React Native with support for DRM, offline playback, HLS/DASH streaming, and more.
## Documentation
documentation is available at [react-native-video.github.io/react-native-video](https://react-native-video.github.io/react-native-video/)
> [!IMPORTANT]
> This is a new version (v7) of `react-native-video` that is currently in active development.
> You can expect breaking changes and missing features.
>
> If you have any questions, please contact us at [hi@thewidlarzgroup.com](mailto:hi@thewidlarzgroup.com).
> if you find some issue with new version, don't hesitate to open a ticket! Also Old version can be found [here](https://github.com/react-native-video/react-native-video/tree/v6.0.0-alpha.8)
## 🔍 Features
## Usage
| Feature | Status |
|---------|--------|
| 📱 Plays all video formats natively supported by iOS/Android | ✅ Available & Production Ready by August |
| ▶️ Local and remote playback | ✅ Available & Production Ready by August |
| 🔁 Streaming: HLS • DASH • SmoothStreaming | ✅ Available & Production Ready by August |
| 🧩 Expo plugin support | ✅ Available & Production Ready by August |
| 📴 Offline playback, video download, support for side-tracks and side-captions (via [optional SDK](https://docs.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=readme&utm_id=features-text)) | ✅ Available & Production Ready by August |
| 📱 Picture in Picture | ✅ Available & Production Ready by August |
| 🎚️ Fine-grained control over tracks, buffering & events | 🏗️ In Development |
| 🧠 Advanced control over playback and buffering | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4604) |
| 🔐 DRM: Widevine & FairPlay ([See free DRM stream example](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm)) | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4606) |
| 🌐 Basic Web Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4605) |
| 📺 TV Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4607) |
| 🥽 VisionOS Support | 📝 [TODO](https://github.com/TheWidlarzGroup/react-native-video/issues/4608) |
```javascript
// Load the module
import Video, {VideoRef} from 'react-native-video';
// Within your render function, assuming you have a file called
// "background.mp4" in your project. You can include multiple videos
// on a single screen if you like.
## ✨ Project Status
const VideoPlayer = () => {
const videoRef = useRef<VideoRef>(null);
const background = require('./background.mp4');
| Version | State | Architecture |
|---------|-------|--------------|
| **v5 and lower** | ❌ End-of-life [Commercial Support Available](https://www.thewidlarzgroup.com/blog/react-native-video-upgrade-challenges-custom-maintenance-support#how-we-can-help?utm_source=rnv&utm_medium=readme&utm_id=upgradev5) | Old Architecture |
| **v6** | 🛠 Maintained (community + TWG) | Old + New (Interop Layer) |
| **v7** | 🚀 Active Development | Old + New (Full Support) |
return (
<Video
// Can be a URL or a local file.
source={background}
// Store reference
ref={videoRef}
// Callback when remote video is buffering
onBuffer={onBuffer}
// Callback when video cannot be loaded
onError={onError}
style={styles.backgroundVideo}
/>
)
}
`react-native-video` v7 introduces full support for the new React Native architecture, unlocking better performance, improved consistency, and modern native modules.
// Later on in your styles..
var styles = StyleSheet.create({
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
---
## 📚 Documentation & Examples
- 📖 [Documentation](https://docs.thewidlarzgroup.com/react-native-video)
- 📦 [Example: Basic Usage](https://github.com/TheWidlarzGroup/react-native-video/tree/v7/example)
- 📦 [Example: Free DRM Stream](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm)
- 📦 Example: Offline SDK integration - In Progress 🏗️, will be available soon
## 🚀 Quick Start
### Requirements
- React Native 0.75 or higher
- `react-native-nitro-modules` (>=0.27.2) - Please see [nitro requirements](https://nitro.margelo.com/docs/minimum-requirements)
### Install
`react-native-video` requires `react-native-nitro-modules` (>=0.27.2) in your project.
```bash
npm install react-native-nitro-modules
```
## Supported by
<p>
📱 TWG provides both free and commercial support for this project. Feel free to contact us 🤝 to build something awesome together! 🚀
</p>
<a href="https://thewidlarzgroup.com/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./docs/assets/baners/twg-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="./docs/assets/baners/twg-light.png" />
<img alt="TheWidlarzGroup" src="./docs/assets/baners/twg-light-1.png" />
</picture>
Then install `react-native-video`
```bash
# Install the alpha version of react-native-video v7
npm install react-native-video@next
# Install pods
cd ios && pod install
```
<details>
<summary>For react-native < 0.80</summary>
`react-native` < 0.80 have bug that prevents to properly handle errors by nitro modules on Android.
We highly recommend to apply bellow patch for `react-native-nitro-modules` to fix this issue.
You can apply it using `patch-package`.
Without this patch you won't be able "recognize" errors, all will be thrown as unknown errors.
see [installation guide](https://docs.thewidlarzgroup.com/react-native-video/docs/v7/installation#patch-for-react-native--080)
</details>
### Usage
```tsx
import { useVideoPlayer, VideoView } from 'react-native-video';
export default () => (
const player = useVideoPlayer(
'https://www.w3schools.com/html/mov_bbb.mp4',
(_player) => {
_player.play();
}
);
<VideoView
player={player}
style={{ width: '100%', aspectRatio: 16 / 9 }}
controls
/>
);
```
---
## 🧩 Plugins
<a href="https://www.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=readme&utm_id=banner">
<img src="./docs/static/baners/sdk-banner.png" alt="Offline SDK Preview" width="40%" align="right" />
</a>
### 1 · 📥 Offline SDK
Enable offline streaming with full control over downloads, license lifecycle, secure storage, and media access.
- Track selection (bitrate, audio, subtitles)
- Pause / resume & background queueing
- Expiration & auto-cleanup
- Built for Android & iOS
- → [Read the SDK Docs](https://docs.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=readme&utm_id=modules-sdk-text)
### 2 · 🧪 Architecture
Write your own plugins to extend library logic, attach analytics or add custom workflows - **without forking** the core SDK.
→ [Plugin documentation](https://docs.thewidlarzgroup.com/react-native-video/other/plugin)
---
## 💼 TWG Services & Products
| Offering | Description |
|----------|-------------|
| [**Professional Support Packages**](https://www.thewidlarzgroup.com/issue-boost?utm_source=rnv&utm_medium=readme&utm_campaign=professional-support-packages#Contact) | Priority bug-fixes, guaranteed SLAs, [roadmap influence](https://github.com/orgs/TheWidlarzGroup/projects/6) |
| [**Issue Booster**](https://www.thewidlarzgroup.com/issue-boost?utm_source=rnv&utm_medium=readme) | Fast-track urgent fixes with a payperissue model |
| [**Offline Video SDK**](https://www.thewidlarzgroup.com/offline-video-sdk/?utm_source=rnv&utm_medium=readme&utm_campaign=downloading&utm_id=offline-video-sdk-link) | Plugandplay secure download solution for iOS & Android |
| [**Integration Support**](https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_campaign=integration-support#Contact) | Handson help integrating video, DRM & offline into your app |
| [**Free DRM Token Generator**](https://www.thewidlarzgroup.com/services/free-drm-token-generator-for-video?utm_source=rnv&utm_medium=readme&utm_id=free-drm) | Generate Widevine / FairPlay tokens for testing |
| [**Ready Boilerplates**](https://www.thewidlarzgroup.com/showcases?utm_source=rnv&utm_medium=readme) | Ready-to-use apps with offline HLS/DASH DRM, video frame scrubbing, TikTok-style video feed, background uploads, Skia-based frame processor (R&D phase), and more |
| [**React Native Video Upgrade Guide**](https://www.thewidlarzgroup.com/blog/react-native-video-upgrade-challenges-custom-maintenance-support?utm_source=rnv&utm_medium=readme&utm_id=upgrade-blog&utm_campaign=v7) | Common upgrade pitfalls & how to solve them |
*See how [TWG](https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=readme&utm_id=services-text) helped **Learnn** ship a worldclass player in record time - [case study](https://gitnation.com/contents/a-4-year-retrospective-lessons-learned-from-building-a-video-player-from-scratch-with-react-native).*
Contact us at [hi@thewidlarzgroup.com](mailto:hi@thewidlarzgroup.com)
## 🌍 Social
- 🐦 **X / Twitter** - [follow product & release updates](https://x.com/TheWidlarzGroup)
- 💬 **Discord** - [talk to the community and us](https://discord.gg/9WPq6Yx)
- 💼 **LinkedIn** - [see TWG flexing](https://linkedin.com/company/the-widlarz-group)
## 📰 Community & Media
- 🗽 **React Summit US** How TWG helped Learnn boost video performance on React Native.
[Watch the talk »](https://gitnation.com/contents/a-4-year-retrospective-lessons-learned-from-building-a-video-player-from-scratch-with-react-native)
- 🧨 **v7 deep dive** Why were building v7 with Nitro Modules
[Watch on X »](https://x.com/krzysztof_moch/status/1854162551946478051)
- 🛠️ **Well-maintained open-source library** - What does it truly mean? - Bart's talk for React Native Warsaw
[Watch here »](https://www.youtube.com/watch?v=RAQQwGCQNqY)
- 📺 **“Over the Top” Panel** - Building Streaming Apps for Mobile, Web, and Smart TVs - Bart giving his insights on the industry
[Watch here »](https://youtu.be/j2b_bG-32JI)

View File

@@ -1,14 +0,0 @@
[*.{kt,kts}]
indent_style=space
indent_size=4
continuation_indent_size=4
insert_final_newline=true
max_line_length=160
ktlint_code_style=android_studio
ktlint_standard=enabled
ktlint_experimental=enabled
ktlint_standard_filename=disabled # dont require PascalCase filenames
ktlint_standard_no-wildcard-imports=disabled # allow .* imports
ktlint_function_signature_body_expression_wrapping=multiline
ij_kotlin_allow_trailing_comma_on_call_site=false
ij_kotlin_allow_trailing_comma=false

View File

@@ -1,46 +0,0 @@
## react-native-video - ExoPlayer
This is an Android React Native video component based on ExoPlayer v2.
> ExoPlayer is an application level media player for Android. It provides an alternative to Androids MediaPlayer API for playing audio and video both locally and over the Internet. ExoPlayer supports features not currently supported by Androids MediaPlayer API, including DASH and SmoothStreaming adaptive playbacks. Unlike the MediaPlayer API, ExoPlayer is easy to customize and extend, and can be updated through Play Store application updates.
https://github.com/google/ExoPlayer
## Benefits over `react-native-video@0.9.0`:
- Android Video library built by Google, with a lot of support
- Supports DASH, HLS, & SmoothStreaming adaptive streams
- Supports formats such as MP4, M4A, FMP4, WebM, MKV, MP3, Ogg, WAV, MPEG-TS, MPEG-PS, FLV and ADTS (AAC).
- Fewer device specific issues
- Highly customisable
## ExoPlayer only props
```javascript
render() {
return (
<Video
...
disableFocus={true} // disables audio focus and wake lock (default false)
onAudioBecomingNoisy={this.onAudioBecomingNoisy} // Callback when audio is becoming noisy - should pause video
onAudioFocusChanged={this.onAudioFocusChanged} // Callback when audio focus has been lost - pause if focus has been lost
/>
)
}
onAudioBecomingNoisy = () => {
this.setState({ pause: true })
}
onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
if (!this.state.paused && !event.hasAudioFocus) {
this.setState({ paused: true })
}
}
```
## Unimplemented props
- Expansion file - `source={{ mainVer: 1, patchVer: 0 }}`

View File

@@ -1,187 +0,0 @@
import com.android.Version
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
buildscript {
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['RNVideo_kotlinVersion']
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
def safeExtGet(prop) {
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : project.properties["RNVideo_" + prop]
}
def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}
def supportsNamespace() {
def parsed = Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
def major = parsed[0].toInteger()
def minor = parsed[1].toInteger()
// Namespace support was added in 7.3.0
if (major == 7 && minor >= 3) {
return true
}
return major >= 8
}
def useExoplayerIMA = safeExtGet("RNVUseExoplayerIMA")?.toBoolean() ?: false
println "useExoplayerIMA:" + useExoplayerIMA
// This string is used to define build path.
// As react native build output directory is react-native path of the module.
// We need to force a new path on each configuration change.
// If you add a new build parameter, please add the new value in this string
def configStringPath = (
'useExoplayerIMA' + useExoplayerIMA \
).md5()
if (isNewArchitectureEnabled()) {
apply plugin: "com.facebook.react"
}
android {
if (supportsNamespace()) {
namespace 'com.brentvatne.react'
sourceSets {
main {
manifest.srcFile "src/main/AndroidManifestNew.xml"
}
}
}
compileSdkVersion safeExtGet('compileSdkVersion')
buildToolsVersion safeExtGet('buildToolsVersion')
def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.majorVersion
}
}
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion')
targetSdkVersion safeExtGet('targetSdkVersion')
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
ndk {
abiFilters(*reactNativeArchitectures())
}
}
buildFeatures {
buildConfig true
}
packagingOptions {
exclude "**/libreact_render*.so"
}
buildDir 'buildOutput_' + configStringPath
sourceSets {
main {
java {
if (useExoplayerIMA) {
exclude 'com/google/ads/interactivemedia/v3/api'
exclude 'androidx/media3/exoplayer/ima'
}
}
}
}
sourceSets.main {
java {
if (isNewArchitectureEnabled()) {
srcDirs += [
"src/fabric/java",
"${project.buildDir}/generated/source/codegen/java"
]
} else {
srcDirs += [
"src/oldarch/java"
]
}
}
}
}
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
repositories {
google()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
mavenCentral()
}
def media3_version = safeExtGet('media3Version')
def kotlin_version = safeExtGet('kotlinVersion')
def androidxCore_version = safeExtGet('androidxCoreVersion')
def androidxActivity_version = safeExtGet('androidxActivityVersion')
dependencies {
// For < 0.71, this will be from the local maven repo
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation "androidx.core:core:$androidxCore_version"
implementation "androidx.activity:activity-ktx:$androidxActivity_version"
// For media playback using ExoPlayer
implementation "androidx.media3:media3-exoplayer:$media3_version"
// For Smooth Streaming playback support with ExoPlayer
implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version"
// For DASH playback support with ExoPlayer
implementation "androidx.media3:media3-exoplayer-dash:$media3_version"
// For HLS playback support with ExoPlayer
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
// For ad insertion using the Interactive Media Ads SDK with ExoPlayer
if (useExoplayerIMA) {
implementation "androidx.media3:media3-exoplayer-ima:$media3_version"
}
// For loading data using the OkHttp network stack
implementation "androidx.media3:media3-datasource-okhttp:$media3_version"
// For building media playback UIs
implementation "androidx.media3:media3-ui:$media3_version"
// For exposing and controlling media sessions
implementation "androidx.media3:media3-session:$media3_version"
// Common functionality for loading data
implementation "androidx.media3:media3-datasource:$media3_version"
// Common functionality used across multiple media libraries
implementation "androidx.media3:media3-common:$media3_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

View File

@@ -1,10 +0,0 @@
RNVideo_kotlinVersion=1.7.0
RNVideo_minSdkVersion=21
RNVideo_targetSdkVersion=31
RNVideo_compileSdkVersion=31
RNVideo_ndkversion=21.4.7075529
RNVideo_buildToolsVersion=30.0.2
RNVideo_media3Version=1.1.1
RNVideo_RNVUseExoplayerIMA=false
RNVideo_androidxCoreVersion=1.9.0
RNVideo_androidxActivityVersion=1.7.0

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<issue id="UnsafeOptInUsageError">
<ignore regexp='\(markerClass = androidx\.media3\.common\.util\.UnstableApi\.class\)' />
</issue>
</lint>

View File

@@ -1,63 +0,0 @@
package androidx.media3.exoplayer.ima;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.media3.common.AdViewProvider;
import androidx.media3.common.Player;
import androidx.media3.datasource.DataSpec;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.ads.AdsLoader;
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
import java.io.IOException;
public class ImaAdsLoader implements AdsLoader {
public void setPlayer(ExoPlayer ignoredPlayer) {
}
@Override
public void setPlayer(@Nullable Player player) {
}
public void release() {
}
@Override
public void setSupportedContentTypes(@NonNull int... ints) {
}
@Override
public void start(@NonNull AdsMediaSource adsMediaSource, @NonNull DataSpec dataSpec, @NonNull Object adsId, @NonNull AdViewProvider adViewProvider, @NonNull EventListener eventListener) {
}
@Override
public void stop(@NonNull AdsMediaSource adsMediaSource, @NonNull EventListener eventListener) {
}
@Override
public void handlePrepareComplete(@NonNull AdsMediaSource adsMediaSource, int i, int i1) {
}
@Override
public void handlePrepareError(@NonNull AdsMediaSource adsMediaSource, int i, int i1, @NonNull IOException e) {
}
public static class Builder {
public Builder(Context ignoredThemedReactContext) {
}
public Builder setAdEventListener(Object ignoredReactExoplayerView) {
return this;
}
public Builder setAdErrorListener(Object ignoredReactExoplayerView) {
return this;
}
public ImaAdsLoader build() {
return null;
}
}
}

View File

@@ -1,54 +0,0 @@
package com.brentvatne.common.api
import androidx.annotation.IntDef
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
internal object ResizeMode {
/**
* Either the width or height is decreased to obtain the desired aspect ratio.
*/
const val RESIZE_MODE_FIT = 0
/**
* The width is fixed and the height is increased or decreased to obtain the desired aspect ratio.
*/
const val RESIZE_MODE_FIXED_WIDTH = 1
/**
* The height is fixed and the width is increased or decreased to obtain the desired aspect ratio.
*/
const val RESIZE_MODE_FIXED_HEIGHT = 2
/**
* The height and the width is increased or decreased to fit the size of the view.
*/
const val RESIZE_MODE_FILL = 3
/**
* Keeps the aspect ratio but takes up the view's size.
*/
const val RESIZE_MODE_CENTER_CROP = 4
@JvmStatic
@Mode
fun toResizeMode(ordinal: Int): Int =
when (ordinal) {
RESIZE_MODE_FIXED_WIDTH -> RESIZE_MODE_FIXED_WIDTH
RESIZE_MODE_FIXED_HEIGHT -> RESIZE_MODE_FIXED_HEIGHT
RESIZE_MODE_FILL -> RESIZE_MODE_FILL
RESIZE_MODE_CENTER_CROP -> RESIZE_MODE_CENTER_CROP
RESIZE_MODE_FIT -> RESIZE_MODE_FIT
else -> RESIZE_MODE_FIT
}
@Retention(RetentionPolicy.SOURCE)
@IntDef(
RESIZE_MODE_FIT,
RESIZE_MODE_FIXED_WIDTH,
RESIZE_MODE_FIXED_HEIGHT,
RESIZE_MODE_FILL,
RESIZE_MODE_CENTER_CROP
)
annotation class Mode
}

View File

@@ -1,43 +0,0 @@
package com.brentvatne.common.api
import com.brentvatne.common.toolbox.ReactBridgeUtils
import com.facebook.react.bridge.ReadableMap
/**
* Helper file to parse SubtitleStyle prop and build a dedicated class
*/
class SubtitleStyle private constructor() {
var fontSize = -1
private set
var paddingLeft = 0
private set
var paddingRight = 0
private set
var paddingTop = 0
private set
var paddingBottom = 0
private set
var opacity = 1f
private set
companion object {
private const val PROP_FONT_SIZE_TRACK = "fontSize"
private const val PROP_PADDING_BOTTOM = "paddingBottom"
private const val PROP_PADDING_TOP = "paddingTop"
private const val PROP_PADDING_LEFT = "paddingLeft"
private const val PROP_PADDING_RIGHT = "paddingRight"
private const val PROP_OPACITY = "opacity"
@JvmStatic
fun parse(src: ReadableMap?): SubtitleStyle {
val subtitleStyle = SubtitleStyle()
subtitleStyle.fontSize = ReactBridgeUtils.safeGetInt(src, PROP_FONT_SIZE_TRACK, -1)
subtitleStyle.paddingBottom = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_BOTTOM, 0)
subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0)
subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0)
subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0)
subtitleStyle.opacity = ReactBridgeUtils.safeGetFloat(src, PROP_OPACITY, 1f)
return subtitleStyle
}
}
}

View File

@@ -1,10 +0,0 @@
package com.brentvatne.common.api
/*
* class to handle timedEvent retrieved from the stream
*/
class TimedMetadata(_identifier: String? = null, _value: String? = null) {
var identifier: String? = _identifier
var value: String? = _value
}

View File

@@ -1,15 +0,0 @@
package com.brentvatne.common.api
/*
* internal representation of audio & text tracks
*/
class Track {
var title: String? = null
var mimeType: String? = null
var language: String? = null
var isSelected = false
// in bps available only on audio tracks
var bitrate = 0
var index = 0
}

View File

@@ -1,15 +0,0 @@
package com.brentvatne.common.api
/*
* internal representation of audio & text tracks
*/
class VideoTrack {
var width = 0
var height = 0
var bitrate = 0
var codecs = ""
var id = -1
var trackId = ""
var isSelected = false
}

View File

@@ -1,470 +0,0 @@
package com.brentvatne.common.react;
import androidx.annotation.StringDef;
import android.view.View;
import com.brentvatne.common.api.TimedMetadata;
import com.brentvatne.common.api.Track;
import com.brentvatne.common.api.VideoTrack;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.common.ViewUtil;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Map;
public class VideoEventEmitter {
private final ReactContext mReactContext;
private int viewId = View.NO_ID;
public VideoEventEmitter(ReactContext reactContext) {
this.mReactContext = reactContext;
}
private static final String EVENT_LOAD_START = "onVideoLoadStart";
private static final String EVENT_LOAD = "onVideoLoad";
private static final String EVENT_ERROR = "onVideoError";
private static final String EVENT_PROGRESS = "onVideoProgress";
private static final String EVENT_BANDWIDTH = "onVideoBandwidthUpdate";
private static final String EVENT_SEEK = "onVideoSeek";
private static final String EVENT_END = "onVideoEnd";
private static final String EVENT_FULLSCREEN_WILL_PRESENT = "onVideoFullscreenPlayerWillPresent";
private static final String EVENT_FULLSCREEN_DID_PRESENT = "onVideoFullscreenPlayerDidPresent";
private static final String EVENT_FULLSCREEN_WILL_DISMISS = "onVideoFullscreenPlayerWillDismiss";
private static final String EVENT_FULLSCREEN_DID_DISMISS = "onVideoFullscreenPlayerDidDismiss";
private static final String EVENT_STALLED = "onPlaybackStalled";
private static final String EVENT_RESUME = "onPlaybackResume";
private static final String EVENT_READY = "onReadyForDisplay";
private static final String EVENT_BUFFER = "onVideoBuffer";
private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged";
private static final String EVENT_IDLE = "onVideoIdle";
private static final String EVENT_TIMED_METADATA = "onTimedMetadata";
private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy";
private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged";
private static final String EVENT_PLAYBACK_RATE_CHANGE = "onPlaybackRateChange";
private static final String EVENT_VOLUME_CHANGE = "onVolumeChange";
private static final String EVENT_AUDIO_TRACKS = "onAudioTracks";
private static final String EVENT_TEXT_TRACKS = "onTextTracks";
private static final String EVENT_TEXT_TRACK_DATA_CHANGED = "onTextTrackDataChanged";
private static final String EVENT_VIDEO_TRACKS = "onVideoTracks";
private static final String EVENT_ON_RECEIVE_AD_EVENT = "onReceiveAdEvent";
static public final String[] Events = {
EVENT_LOAD_START,
EVENT_LOAD,
EVENT_ERROR,
EVENT_PROGRESS,
EVENT_SEEK,
EVENT_END,
EVENT_FULLSCREEN_WILL_PRESENT,
EVENT_FULLSCREEN_DID_PRESENT,
EVENT_FULLSCREEN_WILL_DISMISS,
EVENT_FULLSCREEN_DID_DISMISS,
EVENT_STALLED,
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
EVENT_AUDIO_FOCUS_CHANGE,
EVENT_PLAYBACK_RATE_CHANGE,
EVENT_VOLUME_CHANGE,
EVENT_AUDIO_TRACKS,
EVENT_TEXT_TRACKS,
EVENT_TEXT_TRACK_DATA_CHANGED,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
EVENT_ON_RECEIVE_AD_EVENT
};
@Retention(RetentionPolicy.SOURCE)
@StringDef({
EVENT_LOAD_START,
EVENT_LOAD,
EVENT_ERROR,
EVENT_PROGRESS,
EVENT_SEEK,
EVENT_END,
EVENT_FULLSCREEN_WILL_PRESENT,
EVENT_FULLSCREEN_DID_PRESENT,
EVENT_FULLSCREEN_WILL_DISMISS,
EVENT_FULLSCREEN_DID_DISMISS,
EVENT_STALLED,
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
EVENT_AUDIO_FOCUS_CHANGE,
EVENT_PLAYBACK_RATE_CHANGE,
EVENT_VOLUME_CHANGE,
EVENT_AUDIO_TRACKS,
EVENT_TEXT_TRACKS,
EVENT_TEXT_TRACK_DATA_CHANGED,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
EVENT_ON_RECEIVE_AD_EVENT
})
@interface VideoEvents {
}
private static final String EVENT_PROP_FAST_FORWARD = "canPlayFastForward";
private static final String EVENT_PROP_SLOW_FORWARD = "canPlaySlowForward";
private static final String EVENT_PROP_SLOW_REVERSE = "canPlaySlowReverse";
private static final String EVENT_PROP_REVERSE = "canPlayReverse";
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
private static final String EVENT_PROP_DURATION = "duration";
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
private static final String EVENT_PROP_CURRENT_TIME = "currentTime";
private static final String EVENT_PROP_CURRENT_PLAYBACK_TIME = "currentPlaybackTime";
private static final String EVENT_PROP_SEEK_TIME = "seekTime";
private static final String EVENT_PROP_NATURAL_SIZE = "naturalSize";
private static final String EVENT_PROP_TRACK_ID = "trackId";
private static final String EVENT_PROP_WIDTH = "width";
private static final String EVENT_PROP_HEIGHT = "height";
private static final String EVENT_PROP_ORIENTATION = "orientation";
private static final String EVENT_PROP_VIDEO_TRACKS = "videoTracks";
private static final String EVENT_PROP_AUDIO_TRACKS = "audioTracks";
private static final String EVENT_PROP_TEXT_TRACKS = "textTracks";
private static final String EVENT_PROP_TEXT_TRACK_DATA = "subtitleTracks";
private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus";
private static final String EVENT_PROP_IS_BUFFERING = "isBuffering";
private static final String EVENT_PROP_PLAYBACK_RATE = "playbackRate";
private static final String EVENT_PROP_VOLUME = "volume";
private static final String EVENT_PROP_ERROR = "error";
private static final String EVENT_PROP_ERROR_STRING = "errorString";
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace";
private static final String EVENT_PROP_ERROR_CODE = "errorCode";
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
private static final String EVENT_PROP_BITRATE = "bitrate";
private static final String EVENT_PROP_IS_PLAYING = "isPlaying";
public void setViewId(int viewId) {
this.viewId = viewId;
}
public void loadStart() {
receiveEvent(EVENT_LOAD_START, null);
}
WritableMap aspectRatioToNaturalSize(int videoWidth, int videoHeight) {
WritableMap naturalSize = Arguments.createMap();
naturalSize.putInt(EVENT_PROP_WIDTH, videoWidth);
naturalSize.putInt(EVENT_PROP_HEIGHT, videoHeight);
if (videoWidth > videoHeight) {
naturalSize.putString(EVENT_PROP_ORIENTATION, "landscape");
} else if (videoWidth < videoHeight) {
naturalSize.putString(EVENT_PROP_ORIENTATION, "portrait");
} else {
naturalSize.putString(EVENT_PROP_ORIENTATION, "square");
}
return naturalSize;
}
WritableArray audioTracksToArray(ArrayList<Track> audioTracks) {
WritableArray waAudioTracks = Arguments.createArray();
if( audioTracks != null ){
for (int i = 0; i < audioTracks.size(); ++i) {
Track format = audioTracks.get(i);
WritableMap audioTrack = Arguments.createMap();
audioTrack.putInt("index", i);
audioTrack.putString("title", format.getTitle());
audioTrack.putString("type", format.getMimeType());
audioTrack.putString("language", format.getLanguage());
audioTrack.putInt("bitrate", format.getBitrate());
audioTrack.putBoolean("selected", format.isSelected());
waAudioTracks.pushMap(audioTrack);
}
}
return waAudioTracks;
}
WritableArray videoTracksToArray(ArrayList<VideoTrack> videoTracks) {
WritableArray waVideoTracks = Arguments.createArray();
if( videoTracks != null ){
for (int i = 0; i < videoTracks.size(); ++i) {
VideoTrack vTrack = videoTracks.get(i);
WritableMap videoTrack = Arguments.createMap();
videoTrack.putInt("width", vTrack.getWidth());
videoTrack.putInt("height",vTrack.getHeight());
videoTrack.putInt("bitrate", vTrack.getBitrate());
videoTrack.putString("codecs", vTrack.getCodecs());
videoTrack.putInt("trackId",vTrack.getId());
videoTrack.putBoolean("selected", vTrack.isSelected());
waVideoTracks.pushMap(videoTrack);
}
}
return waVideoTracks;
}
WritableArray textTracksToArray(ArrayList<Track> textTracks) {
WritableArray waTextTracks = Arguments.createArray();
if (textTracks != null) {
for (int i = 0; i < textTracks.size(); ++i) {
Track format = textTracks.get(i);
WritableMap textTrack = Arguments.createMap();
textTrack.putInt("index", i);
textTrack.putString("title", format.getTitle());
textTrack.putString("type", format.getMimeType());
textTrack.putString("language", format.getLanguage());
textTrack.putBoolean("selected", format.isSelected());
waTextTracks.pushMap(textTrack);
}
}
return waTextTracks;
}
public void load(double duration, double currentPosition, int videoWidth, int videoHeight,
ArrayList<Track> audioTracks, ArrayList<Track> textTracks, ArrayList<VideoTrack> videoTracks, String trackId){
WritableArray waAudioTracks = audioTracksToArray(audioTracks);
WritableArray waVideoTracks = videoTracksToArray(videoTracks);
WritableArray waTextTracks = textTracksToArray(textTracks);
load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId);
}
void load(double duration, double currentPosition, int videoWidth, int videoHeight,
WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
WritableMap naturalSize = aspectRatioToNaturalSize(videoWidth, videoHeight);
event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize);
event.putString(EVENT_PROP_TRACK_ID, trackId);
event.putArray(EVENT_PROP_VIDEO_TRACKS, videoTracks);
event.putArray(EVENT_PROP_AUDIO_TRACKS, audioTracks);
event.putArray(EVENT_PROP_TEXT_TRACKS, textTracks);
// TODO: Actually check if you can.
event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
event.putBoolean(EVENT_PROP_SLOW_FORWARD, true);
event.putBoolean(EVENT_PROP_SLOW_REVERSE, true);
event.putBoolean(EVENT_PROP_REVERSE, true);
event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
event.putBoolean(EVENT_PROP_STEP_BACKWARD, true);
event.putBoolean(EVENT_PROP_STEP_FORWARD, true);
receiveEvent(EVENT_LOAD, event);
}
WritableMap arrayToObject(String field, WritableArray array) {
WritableMap event = Arguments.createMap();
event.putArray(field, array);
return event;
}
public void audioTracks(ArrayList<Track> audioTracks){
receiveEvent(EVENT_AUDIO_TRACKS, arrayToObject(EVENT_PROP_AUDIO_TRACKS, audioTracksToArray(audioTracks)));
}
public void textTracks(ArrayList<Track> textTracks){
receiveEvent(EVENT_TEXT_TRACKS, arrayToObject(EVENT_PROP_TEXT_TRACKS, textTracksToArray(textTracks)));
}
public void textTrackDataChanged(String textTrackData){
WritableMap event = Arguments.createMap();
event.putString(EVENT_PROP_TEXT_TRACK_DATA, textTrackData);
receiveEvent(EVENT_TEXT_TRACK_DATA_CHANGED, event);
}
public void videoTracks(ArrayList<VideoTrack> videoTracks){
receiveEvent(EVENT_VIDEO_TRACKS, arrayToObject(EVENT_PROP_VIDEO_TRACKS, videoTracksToArray(videoTracks)));
}
public void progressChanged(double currentPosition, double bufferedDuration, double seekableDuration, double currentPlaybackTime) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
event.putDouble(EVENT_PROP_PLAYABLE_DURATION, bufferedDuration / 1000D);
event.putDouble(EVENT_PROP_SEEKABLE_DURATION, seekableDuration / 1000D);
event.putDouble(EVENT_PROP_CURRENT_PLAYBACK_TIME, currentPlaybackTime);
receiveEvent(EVENT_PROGRESS, event);
}
public void bandwidthReport(double bitRateEstimate, int height, int width, String id) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_BITRATE, bitRateEstimate);
event.putInt(EVENT_PROP_WIDTH, width);
event.putInt(EVENT_PROP_HEIGHT, height);
event.putString(EVENT_PROP_TRACK_ID, id);
receiveEvent(EVENT_BANDWIDTH, event);
}
public void seek(long currentPosition, long seekTime) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
event.putDouble(EVENT_PROP_SEEK_TIME, seekTime / 1000D);
receiveEvent(EVENT_SEEK, event);
}
public void ready() {
receiveEvent(EVENT_READY, null);
}
public void buffering(boolean isBuffering) {
WritableMap map = Arguments.createMap();
map.putBoolean(EVENT_PROP_IS_BUFFERING, isBuffering);
receiveEvent(EVENT_BUFFER, map);
}
public void playbackStateChanged(boolean isPlaying) {
WritableMap map = Arguments.createMap();
map.putBoolean(EVENT_PROP_IS_PLAYING, isPlaying);
receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map);
}
public void idle() {
receiveEvent(EVENT_IDLE, null);
}
public void end() {
receiveEvent(EVENT_END, null);
}
public void fullscreenWillPresent() {
receiveEvent(EVENT_FULLSCREEN_WILL_PRESENT, null);
}
public void fullscreenDidPresent() {
receiveEvent(EVENT_FULLSCREEN_DID_PRESENT, null);
}
public void fullscreenWillDismiss() {
receiveEvent(EVENT_FULLSCREEN_WILL_DISMISS, null);
}
public void fullscreenDidDismiss() {
receiveEvent(EVENT_FULLSCREEN_DID_DISMISS, null);
}
public void error(String errorString, Exception exception) {
_error(errorString, exception, "0001");
}
public void error(String errorString, Exception exception, String errorCode) {
_error(errorString, exception, errorCode);
}
void _error(String errorString, Exception exception, String errorCode) {
// Prepare stack trace
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
String stackTrace = sw.toString();
WritableMap error = Arguments.createMap();
error.putString(EVENT_PROP_ERROR_STRING, errorString);
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString());
error.putString(EVENT_PROP_ERROR_CODE, errorCode);
error.putString(EVENT_PROP_ERROR_TRACE, stackTrace);
WritableMap event = Arguments.createMap();
event.putMap(EVENT_PROP_ERROR, error);
receiveEvent(EVENT_ERROR, event);
}
public void playbackRateChange(float rate) {
WritableMap map = Arguments.createMap();
map.putDouble(EVENT_PROP_PLAYBACK_RATE, (double)rate);
receiveEvent(EVENT_PLAYBACK_RATE_CHANGE, map);
}
public void volumeChange(float volume) {
WritableMap map = Arguments.createMap();
map.putDouble(EVENT_PROP_VOLUME, volume);
receiveEvent(EVENT_VOLUME_CHANGE, map);
}
public void timedMetadata(ArrayList<TimedMetadata> _metadataArrayList) {
if (_metadataArrayList.size() == 0) {
return;
}
WritableArray metadataArray = Arguments.createArray();
for (int i = 0; i < _metadataArrayList.size(); i++) {
WritableMap map = Arguments.createMap();
map.putString("identifier", _metadataArrayList.get(i).getIdentifier());
map.putString("value", _metadataArrayList.get(i).getValue());
metadataArray.pushMap(map);
}
WritableMap event = Arguments.createMap();
event.putArray(EVENT_PROP_TIMED_METADATA, metadataArray);
receiveEvent(EVENT_TIMED_METADATA, event);
}
public void audioFocusChanged(boolean hasFocus) {
WritableMap map = Arguments.createMap();
map.putBoolean(EVENT_PROP_HAS_AUDIO_FOCUS, hasFocus);
receiveEvent(EVENT_AUDIO_FOCUS_CHANGE, map);
}
public void audioBecomingNoisy() {
receiveEvent(EVENT_AUDIO_BECOMING_NOISY, null);
}
public void receiveAdEvent(String event, Map<String, String> data) {
WritableMap map = Arguments.createMap();
map.putString("event", event);
WritableMap dataMap = Arguments.createMap();
for (Map.Entry<String, String> entry : data.entrySet()) {
dataMap.putString(entry.getKey(), entry.getValue());
}
map.putMap("data", dataMap);
receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map);
}
public void receiveAdEvent(String event) {
WritableMap map = Arguments.createMap();
map.putString("event", event);
receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map);
}
public void receiveAdErrorEvent(String message, String code, String type) {
WritableMap map = Arguments.createMap();
map.putString("event", "ERROR");
WritableMap dataMap = Arguments.createMap();
dataMap.putString("message", message);
dataMap.putString("code", code);
dataMap.putString("type", type);
map.putMap("data", dataMap);
receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map);
}
private void receiveEvent(@VideoEvents String type, WritableMap event) {
UIManager uiManager = UIManagerHelper.getUIManager(mReactContext, ViewUtil.getUIManagerType(viewId));
if(uiManager != null) {
uiManager.receiveEvent(UIManagerHelper.getSurfaceId(mReactContext), viewId, type, event);
}
}
}

View File

@@ -1,96 +0,0 @@
package com.brentvatne.common.toolbox
import android.os.Build
import android.util.Log
import java.lang.Exception
/* log utils
* This class allow defining a log level for the package
* This is useful for debugging real time issue or tricky use cases
*/
object DebugLog {
// log level to display
private var level = Log.WARN
// enable thread display in logs
private var displayThread = true
// add a common prefix for easy filtering
private const val TAG_PREFIX = "RNV"
@JvmStatic
fun setConfig(_level: Int, _displayThread: Boolean) {
level = _level
displayThread = _displayThread
}
@JvmStatic
private fun getTag(tag: String): String = TAG_PREFIX + tag
@JvmStatic
private fun getMsg(msg: String): String =
if (displayThread) {
"[" + Thread.currentThread().name + "] " + msg
} else {
msg
}
@JvmStatic
fun v(tag: String, msg: String) {
if (level <= Log.VERBOSE) Log.v(getTag(tag), getMsg(msg))
}
@JvmStatic
fun d(tag: String, msg: String) {
if (level <= Log.DEBUG) Log.d(getTag(tag), getMsg(msg))
}
@JvmStatic
fun i(tag: String, msg: String) {
if (level <= Log.INFO) Log.i(getTag(tag), getMsg(msg))
}
@JvmStatic
fun w(tag: String, msg: String) {
if (level <= Log.WARN) Log.w(getTag(tag), getMsg(msg))
}
@JvmStatic
fun e(tag: String, msg: String) {
if (level <= Log.ERROR) Log.e(getTag(tag), getMsg(msg))
}
@JvmStatic
fun wtf(tag: String, msg: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
Log.wtf(getTag(tag), "--------------->" + getMsg(msg))
} else {
Log.e(getTag(tag), "--------------->" + getMsg(msg))
}
printCallStack()
}
@JvmStatic
fun printCallStack() {
if (level <= Log.VERBOSE) {
val e = Exception()
e.printStackTrace()
}
}
// Additionnal thread safety checkers
@JvmStatic
fun checkUIThread(tag: String, msg: String) {
if (Thread.currentThread().name != "main") {
wtf(tag, "------------------------>" + getMsg(msg))
}
}
@JvmStatic
fun checkNotUIThread(tag: String, msg: String) {
if (Thread.currentThread().name == "main") {
wtf(tag, "------------------------>" + getMsg(msg))
}
}
}

View File

@@ -1,131 +0,0 @@
package com.brentvatne.common.toolbox
import com.facebook.react.bridge.Dynamic
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import java.util.HashMap
/*
* Toolbox to safe parsing of <Video props
* These are just safe accessors to ReadableMap
*/
object ReactBridgeUtils {
@JvmStatic
fun safeGetString(map: ReadableMap?, key: String?, fallback: String?): String? =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getString(key) else fallback
@JvmStatic
fun safeGetString(map: ReadableMap?, key: String?): String? = safeGetString(map, key, null)
@JvmStatic
fun safeGetDynamic(map: ReadableMap?, key: String?, fallback: Dynamic?): Dynamic? =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getDynamic(key) else fallback
@JvmStatic
fun safeGetDynamic(map: ReadableMap?, key: String?): Dynamic? = safeGetDynamic(map, key, null)
@JvmStatic
fun safeGetBool(map: ReadableMap?, key: String?, fallback: Boolean): Boolean =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getBoolean(key) else fallback
@JvmStatic
fun safeGetMap(map: ReadableMap?, key: String?): ReadableMap? = if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getMap(key) else null
@JvmStatic
fun safeGetArray(map: ReadableMap?, key: String?): ReadableArray? = if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getArray(key) else null
@JvmStatic
fun safeGetInt(map: ReadableMap?, key: String?, fallback: Int): Int =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getInt(key) else fallback
@JvmStatic
fun safeGetInt(map: ReadableMap?, key: String?): Int = safeGetInt(map, key, 0)
@JvmStatic
fun safeGetDouble(map: ReadableMap?, key: String?, fallback: Double): Double =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getDouble(key) else fallback
@JvmStatic
fun safeGetDouble(map: ReadableMap?, key: String?): Double = safeGetDouble(map, key, 0.0)
@JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?, fallback: Float): Float =
if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getDouble(key).toFloat() else fallback
@JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?): Float = safeGetFloat(map, key, 0.0f)
/**
* toStringMap converts a [ReadableMap] into a HashMap.
*
* @param readableMap The ReadableMap to be conveted.
* @return A HashMap containing the data that was in the ReadableMap.
* @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
*/
@JvmStatic
fun toStringMap(readableMap: ReadableMap?): Map<String, String?>? {
if (readableMap == null) return null
val iterator = readableMap.keySetIterator()
if (!iterator.hasNextKey()) return null
val result: MutableMap<String, String?> = HashMap()
while (iterator.hasNextKey()) {
val key = iterator.nextKey()
result[key] = readableMap.getString(key)
}
return result
}
/**
* toIntMap converts a [ReadableMap] into a HashMap.
*
* @param readableMap The ReadableMap to be conveted.
* @return A HashMap containing the data that was in the ReadableMap.
* @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
*/
@JvmStatic
fun toIntMap(readableMap: ReadableMap?): Map<String, Int>? {
if (readableMap == null) return null
val iterator = readableMap.keySetIterator()
if (!iterator.hasNextKey()) return null
val result: MutableMap<String, Int> = HashMap()
while (iterator.hasNextKey()) {
val key = iterator.nextKey()
result[key] = readableMap.getInt(key)
}
return result
}
@JvmStatic
fun safeStringEquals(str1: String?, str2: String?): Boolean {
if (str1 == null && str2 == null) return true // both are null
return if (str1 == null || str2 == null) false else str1 == str2 // only 1 is null
}
@JvmStatic
fun safeStringArrayEquals(str1: Array<String>?, str2: Array<String>?): Boolean {
if (str1 == null && str2 == null) return true // both are null
if (str1 == null || str2 == null) return false // only 1 is null
if (str1.size != str2.size) return false // only 1 is null
for (i in str1.indices) {
if (str1[i] == str2[i]) {
// standard check
return false
}
}
return true
}
@JvmStatic
fun safeStringMapEquals(first: Map<String?, String?>?, second: Map<String?, String?>?): Boolean {
if (first == null && second == null) return true // both are null
if (first == null || second == null) return false // only 1 is null
if (first.size != second.size) {
return false
}
for (key in first.keys) {
if (!safeStringEquals(first[key], second[key])) {
return false
}
}
return true
}
}

View File

@@ -1,149 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.brentvatne.exoplayer;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import com.brentvatne.common.api.ResizeMode;
/**
* A {@link FrameLayout} that resizes itself to match a specified aspect ratio.
*/
public final class AspectRatioFrameLayout extends FrameLayout {
/**
* The {@link FrameLayout} will not resize itself if the fractional difference between its natural
* aspect ratio and the requested aspect ratio falls below this threshold.
* <p>
* This tolerance allows the view to occupy the whole of the screen when the requested aspect
* ratio is very close, but not exactly equal to, the aspect ratio of the screen. This may reduce
* the number of view layers that need to be composited by the underlying system, which can help
* to reduce power consumption.
*/
private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f;
private float videoAspectRatio;
private @ResizeMode.Mode int resizeMode = ResizeMode.RESIZE_MODE_FIT;
public AspectRatioFrameLayout(Context context) {
this(context, null);
}
public AspectRatioFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Set the aspect ratio that this view should satisfy.
*
* @param widthHeightRatio The width to height ratio.
*/
public void setAspectRatio(float widthHeightRatio) {
if (this.videoAspectRatio != widthHeightRatio) {
this.videoAspectRatio = widthHeightRatio;
requestLayout();
}
}
/**
* Get the aspect ratio that this view should satisfy.
*
* @return widthHeightRatio The width to height ratio.
*/
public float getAspectRatio() {
return videoAspectRatio;
}
public void invalidateAspectRatio() {
videoAspectRatio = 0;
}
/**
* Sets the resize mode which can be of value {@link ResizeMode.Mode}
*
* @param resizeMode The resize mode.
*/
public void setResizeMode(@ResizeMode.Mode int resizeMode) {
if (this.resizeMode != resizeMode) {
this.resizeMode = resizeMode;
requestLayout();
}
}
/**
* Gets the resize mode which can be of value {@link ResizeMode.Mode}
*
* @return resizeMode The resize mode.
*/
public @ResizeMode.Mode int getResizeMode() {
return resizeMode;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (videoAspectRatio == 0) {
// Aspect ratio not set.
return;
}
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
int width = measuredWidth;
int height = measuredHeight;
float viewAspectRatio = (float) measuredWidth / measuredHeight;
float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
// We're within the allowed tolerance.
return;
}
switch (resizeMode) {
case ResizeMode.RESIZE_MODE_FIXED_WIDTH:
height = (int) (measuredWidth / videoAspectRatio);
break;
case ResizeMode.RESIZE_MODE_FIXED_HEIGHT:
width = (int) (measuredHeight * videoAspectRatio);
break;
case ResizeMode.RESIZE_MODE_FILL:
// Do nothing width and height is the same as the view
break;
case ResizeMode.RESIZE_MODE_CENTER_CROP:
width = (int) (measuredHeight * videoAspectRatio);
// Scale video if it doesn't fill the measuredWidth
if (width < measuredWidth) {
float scaleFactor = (float) measuredWidth / width;
width = (int) (width * scaleFactor);
height = (int) (measuredHeight * scaleFactor);
}
break;
default:
if (aspectDeformation > 0) {
height = (int) (measuredWidth / videoAspectRatio);
} else {
width = (int) (measuredHeight * videoAspectRatio);
}
break;
}
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
}

View File

@@ -1,36 +0,0 @@
package com.brentvatne.exoplayer;
import android.annotation.SuppressLint;
import androidx.media3.common.C;
@SuppressLint("InlinedApi")
public enum AudioOutput {
SPEAKER("speaker", C.STREAM_TYPE_MUSIC),
EARPIECE("earpiece", C.STREAM_TYPE_VOICE_CALL);
private final @C.StreamType int streamType;
private final String mName;
AudioOutput(final String name, @C.StreamType int stream) {
mName = name;
streamType = stream;
}
public static AudioOutput get(String name) {
for (AudioOutput d : values()) {
if (d.mName.equalsIgnoreCase(name))
return d;
}
return SPEAKER;
}
public int getStreamType() {
return streamType;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + this.mName + ", " + streamType + ")";
}
}

View File

@@ -1,103 +0,0 @@
package com.brentvatne.exoplayer;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.datasource.okhttp.OkHttpDataSource;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.OkHttpClientProvider;
import java.util.Map;
import okhttp3.Call;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
public class DataSourceUtil {
private DataSourceUtil() {
}
private static DataSource.Factory rawDataSourceFactory = null;
private static DataSource.Factory defaultDataSourceFactory = null;
private static HttpDataSource.Factory defaultHttpDataSourceFactory = null;
private static String userAgent = null;
public static void setUserAgent(String userAgent) {
DataSourceUtil.userAgent = userAgent;
}
public static String getUserAgent(ReactContext context) {
if (userAgent == null) {
userAgent = Util.getUserAgent(context, "ReactNativeVideo");
}
return userAgent;
}
public static DataSource.Factory getRawDataSourceFactory(ReactContext context) {
if (rawDataSourceFactory == null) {
rawDataSourceFactory = buildRawDataSourceFactory(context);
}
return rawDataSourceFactory;
}
public static void setRawDataSourceFactory(DataSource.Factory factory) {
DataSourceUtil.rawDataSourceFactory = factory;
}
public static DataSource.Factory getDefaultDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
if (defaultDataSourceFactory == null || (requestHeaders != null && !requestHeaders.isEmpty())) {
defaultDataSourceFactory = buildDataSourceFactory(context, bandwidthMeter, requestHeaders);
}
return defaultDataSourceFactory;
}
public static void setDefaultDataSourceFactory(DataSource.Factory factory) {
DataSourceUtil.defaultDataSourceFactory = factory;
}
public static HttpDataSource.Factory getDefaultHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
if (defaultHttpDataSourceFactory == null || (requestHeaders != null && !requestHeaders.isEmpty())) {
defaultHttpDataSourceFactory = buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders);
}
return defaultHttpDataSourceFactory;
}
public static void setDefaultHttpDataSourceFactory(HttpDataSource.Factory factory) {
DataSourceUtil.defaultHttpDataSourceFactory = factory;
}
private static DataSource.Factory buildRawDataSourceFactory(ReactContext context) {
return new RawResourceDataSourceFactory(context.getApplicationContext());
}
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
return new DefaultDataSource.Factory(context, buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
}
private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
OkHttpClient client = OkHttpClientProvider.getOkHttpClient();
CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
container.setCookieJar(new JavaNetCookieJar(handler));
OkHttpDataSource.Factory okHttpDataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) client)
.setTransferListener(bandwidthMeter);
if (requestHeaders != null) {
okHttpDataSourceFactory.setDefaultRequestProperties(requestHeaders);
if (!requestHeaders.containsKey("User-Agent")) {
okHttpDataSourceFactory.setUserAgent(getUserAgent(context));
}
} else {
okHttpDataSourceFactory.setUserAgent(getUserAgent(context));
}
return okHttpDataSourceFactory;
}
}

View File

@@ -1,38 +0,0 @@
package com.brentvatne.exoplayer;
import android.content.Context;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
private final DefaultBandwidthMeter bandwidthMeter;
private boolean disableDisconnectError = false;
public DefaultReactExoplayerConfig(Context context) {
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
}
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
if (this.disableDisconnectError) {
// Use custom error handling policy to prevent throwing an error when losing network connection
return new ReactExoplayerLoadErrorHandlingPolicy(minLoadRetryCount);
}
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
}
public void setDisableDisconnectError(boolean disableDisconnectError) {
this.disableDisconnectError = disableDisconnectError;
}
public boolean getDisableDisconnectError() {
return this.disableDisconnectError;
}
@Override
public DefaultBandwidthMeter getBandwidthMeter() {
return bandwidthMeter;
}
}

View File

@@ -1,300 +0,0 @@
package com.brentvatne.exoplayer;
import android.content.Context;
import androidx.core.content.ContextCompat;
import androidx.media3.common.AdViewProvider;
import androidx.media3.common.C;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player;
import androidx.media3.common.Timeline;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.Cue;
import androidx.media3.common.util.Assertions;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
import androidx.media3.ui.SubtitleView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle;
import java.util.List;
public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private View surfaceView;
private final View shutterView;
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
private ExoPlayer player;
private Context context;
private ViewGroup.LayoutParams layoutParams;
private final FrameLayout adOverlayFrameLayout;
private boolean useTextureView = true;
private boolean useSecureView = false;
private boolean hideShutterView = false;
public ExoPlayerView(Context context) {
this(context, null);
}
public ExoPlayerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
componentListener = new ComponentListener();
FrameLayout.LayoutParams aspectRatioParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
aspectRatioParams.gravity = Gravity.CENTER;
layout = new AspectRatioFrameLayout(context);
layout.setLayoutParams(aspectRatioParams);
shutterView = new View(getContext());
shutterView.setLayoutParams(layoutParams);
shutterView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black));
subtitleLayout = new SubtitleView(context);
subtitleLayout.setLayoutParams(layoutParams);
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();
updateSurfaceView();
adOverlayFrameLayout = new FrameLayout(context);
layout.addView(shutterView, 1, layoutParams);
layout.addView(subtitleLayout, 2, layoutParams);
layout.addView(adOverlayFrameLayout, 3, layoutParams);
addViewInLayout(layout, 0, aspectRatioParams);
}
private void clearVideoView() {
if (surfaceView instanceof TextureView) {
player.clearVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SurfaceView) {
player.clearVideoSurfaceView((SurfaceView) surfaceView);
}
}
private void setVideoView() {
if (surfaceView instanceof TextureView) {
player.setVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SurfaceView) {
player.setVideoSurfaceView((SurfaceView) surfaceView);
}
}
public boolean isPlaying() {
return player != null && player.isPlaying();
}
public void setSubtitleStyle(SubtitleStyle style) {
// ensure we reset subtile style before reapplying it
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();
if (style.getFontSize() > 0) {
subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize());
}
subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom());
if (style.getOpacity() != 0) {
subtitleLayout.setAlpha(style.getOpacity());
subtitleLayout.setVisibility(View.VISIBLE);
} else {
subtitleLayout.setVisibility(View.GONE);
}
}
public void setShutterColor(Integer color) {
shutterView.setBackgroundColor(color);
}
private void updateSurfaceView() {
View view;
if (!useTextureView || useSecureView) {
view = new SurfaceView(context);
if (useSecureView) {
((SurfaceView)view).setSecure(true);
}
} else {
view = new TextureView(context);
// Support opacity properly:
((TextureView) view).setOpaque(false);
}
view.setLayoutParams(layoutParams);
surfaceView = view;
if (layout.getChildAt(0) != null) {
layout.removeViewAt(0);
}
layout.addView(surfaceView, 0, layoutParams);
if (this.player != null) {
setVideoView();
}
}
private void updateShutterViewVisibility() {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
@Override
public void requestLayout() {
super.requestLayout();
post(measureAndLayout);
}
// AdsLoader.AdViewProvider implementation.
@Override
public ViewGroup getAdViewGroup() {
return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
}
/**
* Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
* player will be called and previous
* assignments are overridden.
*
* @param player The {@link ExoPlayer} to use.
*/
public void setPlayer(ExoPlayer player) {
if (this.player == player) {
return;
}
if (this.player != null) {
this.player.removeListener(componentListener);
clearVideoView();
}
this.player = player;
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
if (player != null) {
setVideoView();
player.addListener(componentListener);
}
}
/**
* Sets the resize mode which can be of value {@link ResizeMode.Mode}
*
* @param resizeMode The resize mode.
*/
public void setResizeMode(@ResizeMode.Mode int resizeMode) {
if (layout.getResizeMode() != resizeMode) {
layout.setResizeMode(resizeMode);
post(measureAndLayout);
}
}
/**
* Get the view onto which video is rendered. This is either a {@link SurfaceView} (default)
* or a {@link TextureView} if the {@code use_texture_view} view attribute has been set to true.
*
* @return either a {@link SurfaceView} or a {@link TextureView}.
*/
public View getVideoSurfaceView() {
return surfaceView;
}
public void setUseTextureView(boolean useTextureView) {
if (useTextureView != this.useTextureView) {
this.useTextureView = useTextureView;
updateSurfaceView();
}
}
public void useSecureView(boolean useSecureView) {
if (useSecureView != this.useSecureView) {
this.useSecureView = useSecureView;
updateSurfaceView();
}
}
public void setHideShutterView(boolean hideShutterView) {
this.hideShutterView = hideShutterView;
updateShutterViewVisibility();
}
private final Runnable measureAndLayout = () -> {
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
};
private void updateForCurrentTrackSelections() {
if (player == null) {
return;
}
TrackSelectionArray selections = player.getCurrentTrackSelections();
for (int i = 0; i < selections.length; i++) {
if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) {
// Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in
// onRenderedFirstFrame().
return;
}
}
// Video disabled so the shutter must be closed.
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
public void invalidateAspectRatio() {
// Resetting aspect ratio will force layout refresh on next video size changed
layout.invalidateAspectRatio();
}
private final class ComponentListener implements Player.Listener {
@Override
public void onCues(List<Cue> cues) {
subtitleLayout.setCues(cues);
}
@Override
public void onVideoSizeChanged(VideoSize videoSize) {
boolean isInitialRatio = layout.getAspectRatio() == 0;
layout.setAspectRatio(videoSize.height == 0 ? 1 : (videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height);
// React native workaround for measuring and layout on initial load.
if (isInitialRatio) {
post(measureAndLayout);
}
}
@Override
public void onRenderedFirstFrame() {
shutterView.setVisibility(INVISIBLE);
}
@Override
public void onTracksChanged(Tracks tracks) {
updateForCurrentTrackSelections();
}
}
}

View File

@@ -1,136 +0,0 @@
package com.brentvatne.exoplayer;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Handler;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import androidx.activity.OnBackPressedCallback;
import androidx.media3.ui.LegacyPlayerControlView;
import com.brentvatne.common.toolbox.DebugLog;
import java.lang.ref.WeakReference;
@SuppressLint("PrivateResource")
public class FullScreenPlayerView extends Dialog {
private final LegacyPlayerControlView playerControlView;
private final ExoPlayerView exoPlayerView;
private final ReactExoplayerView reactExoplayerView;
private ViewGroup parent;
private final FrameLayout containerView;
private final OnBackPressedCallback onBackPressedCallback;
private final Handler mKeepScreenOnHandler;
private final Runnable mKeepScreenOnUpdater;
private static class KeepScreenOnUpdater implements Runnable {
private final static long UPDATE_KEEP_SCREEN_ON_FLAG_MS = 200;
private final WeakReference<FullScreenPlayerView> mFullscreenPlayer;
KeepScreenOnUpdater(FullScreenPlayerView player) {
mFullscreenPlayer = new WeakReference<>(player);
}
@Override
public void run() {
try {
FullScreenPlayerView fullscreenVideoPlayer = mFullscreenPlayer.get();
if (fullscreenVideoPlayer != null) {
final Window window = fullscreenVideoPlayer.getWindow();
if (window != null) {
boolean isPlaying = fullscreenVideoPlayer.exoPlayerView.isPlaying();
if (isPlaying) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
fullscreenVideoPlayer.mKeepScreenOnHandler.postDelayed(this, UPDATE_KEEP_SCREEN_ON_FLAG_MS);
}
} catch (Exception ex) {
DebugLog.e("ExoPlayer Exception", "Failed to flag FLAG_KEEP_SCREEN_ON on fullscreeen.");
DebugLog.e("ExoPlayer Exception", ex.toString());
}
}
}
public FullScreenPlayerView(Context context, ExoPlayerView exoPlayerView, ReactExoplayerView reactExoplayerView, LegacyPlayerControlView playerControlView, OnBackPressedCallback onBackPressedCallback) {
super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
this.playerControlView = playerControlView;
this.exoPlayerView = exoPlayerView;
this.reactExoplayerView = reactExoplayerView;
this.onBackPressedCallback = onBackPressedCallback;
containerView = new FrameLayout(context);
setContentView(containerView, generateDefaultLayoutParams());
mKeepScreenOnUpdater = new KeepScreenOnUpdater(this);
mKeepScreenOnHandler = new Handler();
}
@Override
public void onBackPressed() {
super.onBackPressed();
onBackPressedCallback.handleOnBackPressed();
}
@Override
protected void onStart() {
parent = (FrameLayout)(exoPlayerView.getParent());
parent.removeView(exoPlayerView);
containerView.addView(exoPlayerView, generateDefaultLayoutParams());
if (playerControlView != null) {
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
imageButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_exit);
imageButton.setContentDescription(getContext().getString(androidx.media3.ui.R.string.exo_controls_fullscreen_exit_description));
parent.removeView(playerControlView);
containerView.addView(playerControlView, generateDefaultLayoutParams());
}
super.onStart();
}
@Override
protected void onStop() {
mKeepScreenOnHandler.removeCallbacks(mKeepScreenOnUpdater);
containerView.removeView(exoPlayerView);
parent.addView(exoPlayerView, generateDefaultLayoutParams());
if (playerControlView != null) {
ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
imageButton.setImageResource(androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter);
imageButton.setContentDescription(getContext().getString(androidx.media3.ui.R.string.exo_controls_fullscreen_enter_description));
containerView.removeView(playerControlView);
parent.addView(playerControlView, generateDefaultLayoutParams());
}
parent.requestLayout();
parent = null;
super.onStop();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (reactExoplayerView.getPreventsDisplaySleepDuringVideoPlayback()) {
mKeepScreenOnHandler.post(mKeepScreenOnUpdater);
}
}
private FrameLayout.LayoutParams generateDefaultLayoutParams() {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
);
layoutParams.setMargins(0, 0, 0, 0);
return layoutParams;
}
}

View File

@@ -1,20 +0,0 @@
package com.brentvatne.exoplayer;
import android.content.Context;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.RawResourceDataSource;
class RawResourceDataSourceFactory implements DataSource.Factory {
private final Context context;
RawResourceDataSourceFactory(Context context) {
this.context = context;
}
@Override
public DataSource createDataSource() {
return new RawResourceDataSource(context);
}
}

View File

@@ -1,16 +0,0 @@
package com.brentvatne.exoplayer;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
/**
* Extension points to configure the Exoplayer instance
*/
public interface ReactExoplayerConfig {
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
void setDisableDisconnectError(boolean disableDisconnectError);
boolean getDisableDisconnectError();
DefaultBandwidthMeter getBandwidthMeter();
}

View File

@@ -1,36 +0,0 @@
package com.brentvatne.exoplayer;
import androidx.media3.common.C;
import androidx.media3.datasource.HttpDataSource.HttpDataSourceException;
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
private final int minLoadRetryCount;
public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
super(minLoadRetryCount);
this.minLoadRetryCount = minLoadRetryCount;
}
@Override
public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
String errorMessage = loadErrorInfo.exception.getMessage();
if (
loadErrorInfo.exception instanceof HttpDataSourceException &&
errorMessage != null && (errorMessage.equals("Unable to connect") || errorMessage.equals("Software caused connection abort"))
) {
// Capture the error we get when there is no network connectivity and keep retrying it
return 1000; // Retry every second
} else if(loadErrorInfo.errorCount < this.minLoadRetryCount) {
return Math.min((loadErrorInfo.errorCount - 1) * 1000, 5000); // Default timeout handling
} else {
return C.TIME_UNSET; // Done retrying and will return the error immediately
}
}
@Override
public int getMinimumLoadableRetryCount(int dataType) {
return Integer.MAX_VALUE;
}
}

View File

@@ -1,456 +0,0 @@
package com.brentvatne.exoplayer;
import android.content.Context;
import android.graphics.Color;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.RawResourceDataSource;
import androidx.media3.exoplayer.DefaultLoadControl;
import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.react.VideoEventEmitter;
import com.brentvatne.common.toolbox.DebugLog;
import com.brentvatne.common.toolbox.ReactBridgeUtils;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerView> {
private static final String REACT_CLASS = "RCTVideo";
private static final String PROP_SRC = "src";
private static final String PROP_SRC_URI = "uri";
private static final String PROP_SRC_START_POSITION = "startPosition";
private static final String PROP_SRC_CROP_START = "cropStart";
private static final String PROP_SRC_CROP_END = "cropEnd";
private static final String PROP_AD_TAG_URL = "adTagUrl";
private static final String PROP_SRC_TYPE = "type";
private static final String PROP_DRM = "drm";
private static final String PROP_DRM_TYPE = "type";
private static final String PROP_DRM_LICENSESERVER = "licenseServer";
private static final String PROP_DRM_HEADERS = "headers";
private static final String PROP_SRC_HEADERS = "requestHeaders";
private static final String PROP_RESIZE_MODE = "resizeMode";
private static final String PROP_REPEAT = "repeat";
private static final String PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack";
private static final String PROP_SELECTED_AUDIO_TRACK_TYPE = "type";
private static final String PROP_SELECTED_AUDIO_TRACK_VALUE = "value";
private static final String PROP_SELECTED_TEXT_TRACK = "selectedTextTrack";
private static final String PROP_SELECTED_TEXT_TRACK_TYPE = "type";
private static final String PROP_SELECTED_TEXT_TRACK_VALUE = "value";
private static final String PROP_TEXT_TRACKS = "textTracks";
private static final String PROP_PAUSED = "paused";
private static final String PROP_MUTED = "muted";
private static final String PROP_AUDIO_OUTPUT = "audioOutput";
private static final String PROP_VOLUME = "volume";
private static final String PROP_BACK_BUFFER_DURATION_MS = "backBufferDurationMs";
private static final String PROP_BUFFER_CONFIG = "bufferConfig";
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs";
private static final String PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs";
private static final String PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent";
private static final String PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent";
private static final String PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent";
private static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback";
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
private static final String PROP_REPORT_BANDWIDTH = "reportBandwidth";
private static final String PROP_SEEK = "seek";
private static final String PROP_RATE = "rate";
private static final String PROP_MIN_LOAD_RETRY_COUNT = "minLoadRetryCount";
private static final String PROP_MAXIMUM_BIT_RATE = "maxBitRate";
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
private static final String PROP_CONTENT_START_TIME = "contentStartTime";
private static final String PROP_DISABLE_FOCUS = "disableFocus";
private static final String PROP_DISABLE_BUFFERING = "disableBuffering";
private static final String PROP_DISABLE_DISCONNECT_ERROR = "disableDisconnectError";
private static final String PROP_FOCUSABLE = "focusable";
private static final String PROP_FULLSCREEN = "fullscreen";
private static final String PROP_USE_TEXTURE_VIEW = "useTextureView";
private static final String PROP_SECURE_VIEW = "useSecureView";
private static final String PROP_SELECTED_VIDEO_TRACK = "selectedVideoTrack";
private static final String PROP_SELECTED_VIDEO_TRACK_TYPE = "type";
private static final String PROP_SELECTED_VIDEO_TRACK_VALUE = "value";
private static final String PROP_HIDE_SHUTTER_VIEW = "hideShutterView";
private static final String PROP_CONTROLS = "controls";
private static final String PROP_SUBTITLE_STYLE = "subtitleStyle";
private static final String PROP_SHUTTER_COLOR = "shutterColor";
private static final String PROP_DEBUG = "debug";
private ReactExoplayerConfig config;
public ReactExoplayerViewManager(ReactExoplayerConfig config) {
this.config = config;
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected ReactExoplayerView createViewInstance(ThemedReactContext themedReactContext) {
return new ReactExoplayerView(themedReactContext, config);
}
@Override
public void onDropViewInstance(ReactExoplayerView view) {
view.cleanUpResources();
}
@Override
public @Nullable Map<String, Object> getExportedCustomDirectEventTypeConstants() {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
for (String event : VideoEventEmitter.Events) {
builder.put(event, MapBuilder.of("registrationName", event));
}
return builder.build();
}
@ReactProp(name = PROP_DRM)
public void setDRM(final ReactExoplayerView videoView, @Nullable ReadableMap drm) {
if (drm != null && drm.hasKey(PROP_DRM_TYPE)) {
String drmType = ReactBridgeUtils.safeGetString(drm, PROP_DRM_TYPE);
String drmLicenseServer = ReactBridgeUtils.safeGetString(drm, PROP_DRM_LICENSESERVER);
ReadableArray drmHeadersArray = ReactBridgeUtils.safeGetArray(drm, PROP_DRM_HEADERS);
if (drmType != null && drmLicenseServer != null && Util.getDrmUuid(drmType) != null) {
UUID drmUUID = Util.getDrmUuid(drmType);
videoView.setDrmType(drmUUID);
videoView.setDrmLicenseUrl(drmLicenseServer);
if (drmHeadersArray != null) {
ArrayList<String> drmKeyRequestPropertiesList = new ArrayList<>();
for (int i = 0; i < drmHeadersArray.size(); i++) {
ReadableMap current = drmHeadersArray.getMap(i);
String key = current.hasKey("key") ? current.getString("key") : null;
String value = current.hasKey("value") ? current.getString("value") : null;
drmKeyRequestPropertiesList.add(key);
drmKeyRequestPropertiesList.add(value);
}
videoView.setDrmLicenseHeader(drmKeyRequestPropertiesList.toArray(new String[0]));
}
videoView.setUseTextureView(false);
}
}
}
@ReactProp(name = PROP_SRC)
public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src) {
Context context = videoView.getContext().getApplicationContext();
String uriString = ReactBridgeUtils.safeGetString(src, PROP_SRC_URI, null);
int startPositionMs = ReactBridgeUtils.safeGetInt(src, PROP_SRC_START_POSITION, -1);
int cropStartMs = ReactBridgeUtils.safeGetInt(src, PROP_SRC_CROP_START, -1);
int cropEndMs = ReactBridgeUtils.safeGetInt(src, PROP_SRC_CROP_END, -1);
String extension = ReactBridgeUtils.safeGetString(src, PROP_SRC_TYPE, null);
Map<String, String> headers = new HashMap<>();
ReadableArray propSrcHeadersArray = ReactBridgeUtils.safeGetArray(src, PROP_SRC_HEADERS);
if (propSrcHeadersArray != null) {
if (propSrcHeadersArray.size() > 0) {
for (int i = 0; i < propSrcHeadersArray.size(); i++) {
ReadableMap current = propSrcHeadersArray.getMap(i);
String key = current.hasKey("key") ? current.getString("key") : null;
String value = current.hasKey("value") ? current.getString("value") : null;
if (key != null && value != null) {
headers.put(key, value);
}
}
}
}
if (TextUtils.isEmpty(uriString)) {
videoView.clearSrc();
return;
}
if (startsWithValidScheme(uriString)) {
Uri srcUri = Uri.parse(uriString);
if (srcUri != null) {
videoView.setSrc(srcUri, startPositionMs, cropStartMs, cropEndMs, extension, headers);
}
} else {
int identifier = context.getResources().getIdentifier(
uriString,
"drawable",
context.getPackageName()
);
if (identifier == 0) {
identifier = context.getResources().getIdentifier(
uriString,
"raw",
context.getPackageName()
);
}
if (identifier > 0) {
Uri srcUri = RawResourceDataSource.buildRawResourceUri(identifier);
if (srcUri != null) {
videoView.setRawSrc(srcUri, extension);
}
} else {
videoView.clearSrc();
}
}
}
@ReactProp(name = PROP_AD_TAG_URL)
public void setAdTagUrl(final ReactExoplayerView videoView, final String uriString) {
if (TextUtils.isEmpty(uriString)) {
videoView.setAdTagUrl(null);
return;
}
Uri adTagUrl = Uri.parse(uriString);
videoView.setAdTagUrl(adTagUrl);
}
@ReactProp(name = PROP_RESIZE_MODE)
public void setResizeMode(final ReactExoplayerView videoView, final String resizeMode) {
switch (resizeMode) {
case "none":
case "contain":
videoView.setResizeModeModifier(ResizeMode.RESIZE_MODE_FIT);
break;
case "cover":
videoView.setResizeModeModifier(ResizeMode.RESIZE_MODE_CENTER_CROP);
break;
case "stretch":
videoView.setResizeModeModifier(ResizeMode.RESIZE_MODE_FILL);
break;
default:
DebugLog.w("ExoPlayer Warning", "Unsupported resize mode: " + resizeMode + " - falling back to fit");
videoView.setResizeModeModifier(ResizeMode.RESIZE_MODE_FIT);
break;
}
}
@ReactProp(name = PROP_REPEAT, defaultBoolean = false)
public void setRepeat(final ReactExoplayerView videoView, final boolean repeat) {
videoView.setRepeatModifier(repeat);
}
@ReactProp(name = PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK, defaultBoolean = false)
public void setPreventsDisplaySleepDuringVideoPlayback(final ReactExoplayerView videoView, final boolean preventsSleep) {
videoView.setPreventsDisplaySleepDuringVideoPlayback(preventsSleep);
}
@ReactProp(name = PROP_SELECTED_VIDEO_TRACK)
public void setSelectedVideoTrack(final ReactExoplayerView videoView,
@Nullable ReadableMap selectedVideoTrack) {
String typeString = null;
String value = null;
if (selectedVideoTrack != null) {
typeString = ReactBridgeUtils.safeGetString(selectedVideoTrack, PROP_SELECTED_VIDEO_TRACK_TYPE);
value = ReactBridgeUtils.safeGetString(selectedVideoTrack, PROP_SELECTED_VIDEO_TRACK_VALUE);
}
videoView.setSelectedVideoTrack(typeString, value);
}
@ReactProp(name = PROP_SELECTED_AUDIO_TRACK)
public void setSelectedAudioTrack(final ReactExoplayerView videoView,
@Nullable ReadableMap selectedAudioTrack) {
String typeString = null;
String value = null;
if (selectedAudioTrack != null) {
typeString = ReactBridgeUtils.safeGetString(selectedAudioTrack, PROP_SELECTED_AUDIO_TRACK_TYPE);
value = ReactBridgeUtils.safeGetString(selectedAudioTrack, PROP_SELECTED_AUDIO_TRACK_VALUE);
}
videoView.setSelectedAudioTrack(typeString, value);
}
@ReactProp(name = PROP_SELECTED_TEXT_TRACK)
public void setSelectedTextTrack(final ReactExoplayerView videoView,
@Nullable ReadableMap selectedTextTrack) {
String typeString = null;
String value = null;
if (selectedTextTrack != null) {
typeString = ReactBridgeUtils.safeGetString(selectedTextTrack, PROP_SELECTED_TEXT_TRACK_TYPE);
value = ReactBridgeUtils.safeGetString(selectedTextTrack, PROP_SELECTED_TEXT_TRACK_VALUE);
}
videoView.setSelectedTextTrack(typeString, value);
}
@ReactProp(name = PROP_TEXT_TRACKS)
public void setPropTextTracks(final ReactExoplayerView videoView,
@Nullable ReadableArray textTracks) {
videoView.setTextTracks(textTracks);
}
@ReactProp(name = PROP_PAUSED, defaultBoolean = false)
public void setPaused(final ReactExoplayerView videoView, final boolean paused) {
videoView.setPausedModifier(paused);
}
@ReactProp(name = PROP_MUTED, defaultBoolean = false)
public void setMuted(final ReactExoplayerView videoView, final boolean muted) {
videoView.setMutedModifier(muted);
}
@ReactProp(name = PROP_AUDIO_OUTPUT)
public void setAudioOutput(final ReactExoplayerView videoView, final String audioOutput) {
videoView.setAudioOutput(AudioOutput.get(audioOutput));
}
@ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
public void setVolume(final ReactExoplayerView videoView, final float volume) {
videoView.setVolumeModifier(volume);
}
@ReactProp(name = PROP_PROGRESS_UPDATE_INTERVAL, defaultFloat = 250.0f)
public void setProgressUpdateInterval(final ReactExoplayerView videoView, final float progressUpdateInterval) {
videoView.setProgressUpdateInterval(progressUpdateInterval);
}
@ReactProp(name = PROP_REPORT_BANDWIDTH, defaultBoolean = false)
public void setReportBandwidth(final ReactExoplayerView videoView, final boolean reportBandwidth) {
videoView.setReportBandwidth(reportBandwidth);
}
@ReactProp(name = PROP_SEEK)
public void setSeek(final ReactExoplayerView videoView, final float seek) {
videoView.seekTo(Math.round(seek * 1000f));
}
@ReactProp(name = PROP_RATE)
public void setRate(final ReactExoplayerView videoView, final float rate) {
videoView.setRateModifier(rate);
}
@ReactProp(name = PROP_MAXIMUM_BIT_RATE)
public void setMaxBitRate(final ReactExoplayerView videoView, final int maxBitRate) {
videoView.setMaxBitRateModifier(maxBitRate);
}
@ReactProp(name = PROP_MIN_LOAD_RETRY_COUNT)
public void minLoadRetryCount(final ReactExoplayerView videoView, final int minLoadRetryCount) {
videoView.setMinLoadRetryCountModifier(minLoadRetryCount);
}
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
public void setPlayInBackground(final ReactExoplayerView videoView, final boolean playInBackground) {
videoView.setPlayInBackground(playInBackground);
}
@ReactProp(name = PROP_DISABLE_FOCUS, defaultBoolean = false)
public void setDisableFocus(final ReactExoplayerView videoView, final boolean disableFocus) {
videoView.setDisableFocus(disableFocus);
}
@ReactProp(name = PROP_FOCUSABLE, defaultBoolean = true)
public void setFocusable(final ReactExoplayerView videoView, final boolean focusable) {
videoView.setFocusable(focusable);
}
@ReactProp(name = PROP_BACK_BUFFER_DURATION_MS, defaultInt = 0)
public void setBackBufferDurationMs(final ReactExoplayerView videoView, final int backBufferDurationMs) {
videoView.setBackBufferDurationMs(backBufferDurationMs);
}
@ReactProp(name = PROP_CONTENT_START_TIME, defaultInt = -1)
public void setContentStartTime(final ReactExoplayerView videoView, final int contentStartTime) {
videoView.setContentStartTime(contentStartTime);
}
@ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
videoView.setDisableBuffering(disableBuffering);
}
@ReactProp(name = PROP_DISABLE_DISCONNECT_ERROR, defaultBoolean = false)
public void setDisableDisconnectError(final ReactExoplayerView videoView, final boolean disableDisconnectError) {
videoView.setDisableDisconnectError(disableDisconnectError);
}
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
videoView.setFullscreen(fullscreen);
}
@ReactProp(name = PROP_USE_TEXTURE_VIEW, defaultBoolean = true)
public void setUseTextureView(final ReactExoplayerView videoView, final boolean useTextureView) {
videoView.setUseTextureView(useTextureView);
}
@ReactProp(name = PROP_SECURE_VIEW, defaultBoolean = true)
public void useSecureView(final ReactExoplayerView videoView, final boolean useSecureView) {
videoView.useSecureView(useSecureView);
}
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)
public void setHideShutterView(final ReactExoplayerView videoView, final boolean hideShutterView) {
videoView.setHideShutterView(hideShutterView);
}
@ReactProp(name = PROP_CONTROLS, defaultBoolean = false)
public void setControls(final ReactExoplayerView videoView, final boolean controls) {
videoView.setControls(controls);
}
@ReactProp(name = PROP_SUBTITLE_STYLE)
public void setSubtitleStyle(final ReactExoplayerView videoView, @Nullable final ReadableMap src) {
videoView.setSubtitleStyle(SubtitleStyle.parse(src));
}
@ReactProp(name = PROP_SHUTTER_COLOR, customType = "Color")
public void setShutterColor(final ReactExoplayerView videoView, final Integer color) {
videoView.setShutterColor(color == null ? Color.BLACK : color);
}
@ReactProp(name = PROP_BUFFER_CONFIG)
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
if (bufferConfig != null) {
minBufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_MIN_BUFFER_MS, minBufferMs);
maxBufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_MAX_BUFFER_MS, maxBufferMs);
bufferForPlaybackMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS, bufferForPlaybackMs);
bufferForPlaybackAfterRebufferMs = ReactBridgeUtils.safeGetInt(bufferConfig, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, bufferForPlaybackAfterRebufferMs);
maxHeapAllocationPercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT, maxHeapAllocationPercent);
minBackBufferMemoryReservePercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT, minBackBufferMemoryReservePercent);
minBufferMemoryReservePercent = ReactBridgeUtils.safeGetDouble(bufferConfig, PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT, minBufferMemoryReservePercent);
videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, maxHeapAllocationPercent, minBackBufferMemoryReservePercent, minBufferMemoryReservePercent);
}
}
@ReactProp(name = PROP_DEBUG, defaultBoolean = false)
public void setDebug(final ReactExoplayerView videoView,
@Nullable final ReadableMap debugConfig) {
boolean enableDebug = ReactBridgeUtils.safeGetBool(debugConfig, "enable", false);
boolean enableThreadDebug = ReactBridgeUtils.safeGetBool(debugConfig, "thread", false);
if (enableDebug) {
DebugLog.setConfig(Log.VERBOSE, enableThreadDebug);
} else {
DebugLog.setConfig(Log.WARN, enableThreadDebug);
}
}
private boolean startsWithValidScheme(String uriString) {
String lowerCaseUri = uriString.toLowerCase();
return lowerCaseUri.startsWith("http://")
|| lowerCaseUri.startsWith("https://")
|| lowerCaseUri.startsWith("content://")
|| lowerCaseUri.startsWith("file://")
|| lowerCaseUri.startsWith("asset://");
}
}

View File

@@ -1,50 +0,0 @@
package com.brentvatne.react;
import com.brentvatne.exoplayer.DefaultReactExoplayerConfig;
import com.brentvatne.exoplayer.ReactExoplayerConfig;
import com.brentvatne.exoplayer.ReactExoplayerViewManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ReactVideoPackage implements ReactPackage {
private ReactExoplayerConfig config;
public ReactVideoPackage() {
}
public ReactVideoPackage(ReactExoplayerConfig config) {
this.config = config;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<NativeModule>();
modules.add(new VideoDecoderPropertiesModule(reactContext));
modules.add(new VideoManagerModule(reactContext));
return modules;
}
// Deprecated RN 0.47
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
if (config == null) {
config = new DefaultReactExoplayerConfig(reactContext);
}
return Collections.singletonList(new ReactExoplayerViewManager(config));
}
}

View File

@@ -1,126 +0,0 @@
package com.brentvatne.react;
import android.annotation.SuppressLint;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaDrm;
import android.media.MediaFormat;
import android.media.UnsupportedSchemeException;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.UUID;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class VideoDecoderPropertiesModule extends ReactContextBaseJavaModule {
ReactApplicationContext reactContext;
@NonNull
@Override
public String getName() {
return "VideoDecoderProperties";
}
@SuppressLint("ObsoleteSdkInt")
@ReactMethod
public void getWidevineLevel(Promise p) {
int widevineLevel = 0;
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
p.resolve(widevineLevel);
return;
}
final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
final String WIDEVINE_SECURITY_LEVEL_1 = "L1";
final String WIDEVINE_SECURITY_LEVEL_2 = "L2";
final String WIDEVINE_SECURITY_LEVEL_3 = "L3";
final String SECURITY_LEVEL_PROPERTY = "securityLevel";
String securityProperty = null;
try {
MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
} catch (UnsupportedSchemeException e) {
e.printStackTrace();
}
if (securityProperty == null) {
p.resolve(widevineLevel);
return;
}
switch (securityProperty) {
case WIDEVINE_SECURITY_LEVEL_1: {
widevineLevel = 1;
break;
}
case WIDEVINE_SECURITY_LEVEL_2: {
widevineLevel = 2;
break;
}
case WIDEVINE_SECURITY_LEVEL_3: {
widevineLevel = 3;
break;
}
default: {
// widevineLevel 0
break;
}
}
p.resolve(widevineLevel);
}
@SuppressLint("ObsoleteSdkInt")
@ReactMethod
public void isCodecSupported(String mimeType, int width, int height, Promise p) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
p.resolve("unsupported");
return;
}
MediaCodecList mRegularCodecs = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
String codecName = mRegularCodecs.findDecoderForFormat(format);
if (codecName == null) {
p.resolve("unsupported");
return;
}
// Fallback for android < 10
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
p.resolve("software");
return;
}
boolean isHardwareAccelerated = false;
for (MediaCodecInfo codecInfo : mRegularCodecs.getCodecInfos()) {
if (codecInfo.getName().equalsIgnoreCase(codecName)) {
isHardwareAccelerated = codecInfo.isHardwareAccelerated();
break;
}
}
p.resolve(isHardwareAccelerated ? "software" : "hardware");
}
@ReactMethod
public void isHEVCSupported(Promise p) {
isCodecSupported("video/hevc", 1920, 1080, p);
}
public VideoDecoderPropertiesModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
}

View File

@@ -1,38 +0,0 @@
package com.brentvatne.react;
import android.view.View;
import androidx.annotation.NonNull;
import com.brentvatne.exoplayer.ReactExoplayerView;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.uimanager.UIManagerModule;
public class VideoManagerModule extends ReactContextBaseJavaModule {
private static final String REACT_CLASS = "VideoManager";
public VideoManagerModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@NonNull
@Override
public String getName() {
return REACT_CLASS;
}
@ReactMethod
public void setPlayerPauseState(Boolean paused, int reactTag) {
UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
uiManager.prependUIBlock(manager -> {
View view = manager.resolveView(reactTag);
if (view instanceof ReactExoplayerView) {
ReactExoplayerView videoView = (ReactExoplayerView) view;
videoView.setPausedModifier(paused);
}
});
}
}

View File

@@ -1,41 +0,0 @@
package com.brentvatne.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import androidx.core.content.ContextCompat;
public class AudioBecomingNoisyReceiver extends BroadcastReceiver {
private final Context context;
private BecomingNoisyListener listener = BecomingNoisyListener.NO_OP;
public AudioBecomingNoisyReceiver(Context context) {
this.context = context.getApplicationContext();
}
@Override
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
listener.onAudioBecomingNoisy();
}
}
public void setListener(BecomingNoisyListener listener) {
this.listener = listener;
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
ContextCompat.registerReceiver(context, this, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED);
}
public void removeListener() {
this.listener = BecomingNoisyListener.NO_OP;
try {
context.unregisterReceiver(this);
} catch (Exception ignore) {
// ignore if already unregistered
}
}
}

View File

@@ -1,13 +0,0 @@
package com.brentvatne.receiver;
public interface BecomingNoisyListener {
BecomingNoisyListener NO_OP = new BecomingNoisyListener() {
@Override public void onAudioBecomingNoisy() {
// NO_OP
}
};
void onAudioBecomingNoisy();
}

View File

@@ -1,10 +0,0 @@
package com.google.ads.interactivemedia.v3.api;
import androidx.annotation.InspectableProperty;
public abstract class AdError {
public abstract InspectableProperty getErrorCode();
public abstract InspectableProperty getErrorCodeNumber();
public abstract InspectableProperty getErrorType();
public abstract String getMessage();
}

View File

@@ -1,9 +0,0 @@
package com.google.ads.interactivemedia.v3.api;
public abstract class AdErrorEvent {
public abstract AdError getError();
public interface AdErrorListener {
public void onAdError(AdErrorEvent adErrorEvent);
}
}

View File

@@ -1,14 +0,0 @@
package com.google.ads.interactivemedia.v3.api;
import androidx.annotation.InspectableProperty;
import java.util.Map;
public abstract class AdEvent {
public abstract InspectableProperty getType();
public abstract Map<String, String> getAdData();
public interface AdEventListener {
public void onAdEvent(AdEvent adEvent);
}
}

View File

@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:background="#CC000000"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="4dp"
android:orientation="horizontal">
<ImageButton android:id="@+id/exo_prev"
style="@style/ExoMediaButton.Previous"/>
<ImageButton android:id="@+id/exo_rew"
style="@style/ExoMediaButton.Rewind"/>
<FrameLayout
android:id="@+id/exo_play_pause_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageButton android:id="@+id/exo_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@+id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
</FrameLayout>
<ImageButton android:id="@+id/exo_ffwd"
style="@style/ExoMediaButton.FastForward"/>
<ImageButton android:id="@+id/exo_next"
style="@style/ExoMediaButton.Next"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView android:id="@+id/exo_position"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<androidx.media3.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="26dp"/>
<TextView android:id="@+id/exo_duration"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<ImageButton
android:id="@+id/exo_fullscreen"
style="@style/ExoMediaButton.FullScreen"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_margin="4dp"
android:scaleType="fitCenter" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_no_secure_decoder">This device does not provide a secure decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_querying_decoders">Unable to query device decoders</string>
<string name="error_instantiating_decoder">Unable to instantiate decoder <xliff:g id="decoder_name">%1$s</xliff:g></string>
<string name="error_drm_not_supported">Protected content not supported on API levels below 18</string>
<string name="unrecognized_media_format">Unrecognized media format</string>
<string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
<string name="error_drm_unknown">An unknown DRM error occurred</string>
</resources>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="ExoMediaButton.FullScreen" tools:ignore="PrivateResource">
<item name="android:src">@drawable/exo_icon_fullscreen_enter</item>
<item name="android:contentDescription">@string/exo_controls_fullscreen_enter_description</item>
</style>
</resources>

5
biome.json Normal file
View File

@@ -0,0 +1,5 @@
{
"formatter": {
"useEditorconfig": true
}
}

5489
bun.lock Normal file

File diff suppressed because it is too large Load Diff

16
config/.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
quote_type = single

44
config/.eslintrc.js Normal file
View File

@@ -0,0 +1,44 @@
module.exports = {
root: true,
extends: ['@react-native', 'plugin:prettier/recommended'],
ignorePatterns: [
'**/node_modules',
'**/lib',
'**/.eslintrc.js',
'**/.prettierrc.js',
'**/jest.config.js',
'**/babel.config.js',
'**/metro.config.js',
'**/react-native.config.js',
'**/tsconfig.json',
],
plugins: ['@typescript-eslint', 'prettier'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2020,
sourceType: 'module',
},
rules: {
'prettier/prettier': [
'error',
{
quoteProps: 'consistent',
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
},
],
quotes: [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: true },
],
'@react-native/no-deep-imports': 'off',
},
};

36
config/tsconfig.json Normal file
View File

@@ -0,0 +1,36 @@
{
"exclude": [
"**/node_modules",
"**/lib",
"**/.eslintrc.js",
"**/.prettierrc.js",
"**/jest.config.js",
"**/babel.config.js",
"**/metro.config.js",
"**/react-native.config.js",
"**/tsconfig.json"
],
"compilerOptions": {
"composite": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ESNext", "dom"],
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitUseStrict": false,
"noStrictGenericChecks": false,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ESNext",
"verbatimModuleSyntax": true
}
}

26
docs/.gitignore vendored
View File

@@ -1,3 +1,23 @@
node_modules/
out/
.next/
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# API Reference generated files
docs/api-reference/
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,15 +1,23 @@
# react-native-video-docs
### Build
This is the documentation for the [react-native-video](github.com/react-native-video/react-native-video).
Project is using [bun](https://bun.sh) to build and run the documentation.
Framework for static site generation is [Nextra](https://nextra.site/docs)
```bash
bun install
```
$ bun run build
```
To run:
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```bash
bun run dev
```
$ USE_SSH=true bun run deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> bun run deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

2811
docs/bun.lock Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -1,8 +0,0 @@
.paragraphStyle {
margin-top: 10;
}
.spanStyle {
font-family: 'Orbitron';
font-weight: 800;
}

View File

@@ -0,0 +1,8 @@
{
"label": "Configuration",
"position": 6,
"link": {
"type": "generated-index",
"description": "React Native Video Configuration."
}
}

View File

@@ -0,0 +1,85 @@
---
sidebar_position: 1
sidebar_label: Expo Plugin
description: Expo plugin for react-native-video configuration
---
# Expo Plugin
The `react-native-video` library provides an Expo plugin to simplify the integration and configuration of specific features into your Expo project.
## Installation
To use the Expo plugin, you need to add it to your app's configuration file (`app.json` or `app.config.js`).
```json title="app.json"
{
"expo": {
"plugins": [
[
"react-native-video",
{
"enableAndroidPictureInPicture": true,
"enableBackgroundAudio": true,
"androidExtensions": {
"useExoplayerDash": true,
"useExoplayerHls": true
}
}
]
]
}
}
```
```javascript title="app.config.js"
export default {
plugins: [
[
'react-native-video',
{
enableAndroidPictureInPicture: true,
enableBackgroundAudio: true,
androidExtensions: {
useExoplayerDash: true,
useExoplayerHls: true,
},
},
],
],
};
```
## Configuration Options
The plugin accepts an optional configuration object with the following properties:
### `enableAndroidPictureInPicture` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables Picture-in-Picture (PiP) mode on Android. This will apply the necessary configurations to your Android project.
### `enableBackgroundAudio` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Enables audio playback to continue when the app is in the background on Android. Ensure you have also configured the necessary background modes capabilities in your app if required by the operating system.
### `androidExtensions` (optional)
- **Type:** `object`
- **Default:** `{ useExoplayerDash: true, useExoplayerHls: true }`
- **Description:** Allows you to specify which Android ExoPlayer extensions to include. This can help reduce the size of your app by only including the extensions you need.
- `useExoplayerDash` (boolean, default: `true`): Whether to include ExoPlayer's Dash extension.
- `useExoplayerHls` (boolean, default: `true`): Whether to include ExoPlayer's HLS extension.
### `reactNativeTestApp` (optional)
- **Type:** `boolean`
- **Default:** `false`
- **Description:** Whether to use `react-native-test-app` compatible mode.
## Usage
Once configured in your `app.json` or `app.config.js`, the plugin will automatically apply the necessary native project changes during the prebuild process (e.g., when running `npx expo prebuild`). No further manual setup is typically required for these features.

View File

@@ -0,0 +1,64 @@
---
sidebar_position: 2
sidebar_label: Manual Configuration
description: Manual configuration of react-native-video
---
# Manual Configuration
If you prefer not to use the Expo plugin you can configure **react-native-video** manually by editing the native project files directly. The steps below show the exact changes performed by the plugin so you can reproduce them in a plain React Native or bare Expo project.
---
## iOS
### Enable Background Audio
To allow video sound to continue when the app goes to the background add the `audio` mode to `Info.plist`:
```xml title="ios/YourApp/Info.plist"
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
```
## Android
### Configure ExoPlayer extensions
By default the library enables DASH & HLS extensions. You can fine-tune this by adding properties to **gradle.properties**:
```properties title="android/gradle.properties"
# Enable / disable ExoPlayer extensions used by react-native-video
RNVideo_useExoplayerDash=true # DASH playback support
RNVideo_useExoplayerHls=true # HLS playback support
```
Set a value to `false` to exclude the corresponding extension and reduce APK size.
### Enable Picture-in-Picture (PiP)
Add the `android:supportsPictureInPicture` flag to your *main* activity in **AndroidManifest.xml**:
```xml title="android/app/src/main/AndroidManifest.xml"
<application>
<activity
android:name=".MainActivity"
android:supportsPictureInPicture="true"
...>
<!-- other attributes -->
</activity>
</application>
```
PiP requires **API 26+** (Android 8.0). Make sure `minSdkVersion` is at least `26` when enabling this feature.
## Verification
After the modifications:
1. **iOS** run `cd ios && pod install` then build the app from Xcode or via `npx react-native run-ios` / `npx expo run:ios`.
2. **Android** clean & rebuild the project: `./gradlew clean && ./gradlew :app:assembleDebug` or simply run `npx react-native run-android` / `npx expo run:android`.
If the build succeeds your manual configuration is complete.
---
### Need an easier way?
Use the [Expo plugin](./expo-plugin.md) to apply exactly the same changes automatically during `expo prebuild`.

108
docs/docs/events/events.md Normal file
View File

@@ -0,0 +1,108 @@
---
sidebar_label: Events
sidebar_position: 5
---
# Handling Player Events
The `VideoPlayer` emits a variety of events that allow you to monitor and react to changes in its state and playback.
## Using the `useEvent` Hook
For React functional components, the `useEvent` hook provides a convenient way to subscribe to player events and automatically manage cleanup.
```typescript
import { useVideoPlayer, useEvent } from 'react-native-video';
import { useEffect } from 'react';
const MyVideoComponent = () => {
const player = useVideoPlayer('https://example.com/video.mp4', (_player) => {
_player.play();
});
useEvent(player, 'onLoad', (data) => {
console.log('Video loaded via useEvent! Duration:', data.duration);
});
useEvent(player, 'onProgress', (data) => {
console.log('Progress via useEvent:', data.currentTime);
});
// For onError, which is a direct property on VideoPlayer, not from VideoPlayerEvents
useEvent(player, 'onError', (error) => {
console.error('Player Error via useEvent:', error.code, error.message);
});
return <VideoView player={player} />;
};
```
## Available Events
The `VideoPlayer` class, through `VideoPlayerEvents`, supports the following events. You can subscribe to these by assigning a callback function to the corresponding property on the `VideoPlayer` instance.
| Event | Callback Signature | Description |
|----------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------------|
| `onAudioBecomingNoisy` | () => void | Fired when audio is about to become noisy (e.g., headphones unplugged). |
| `onAudioFocusChange` | (hasAudioFocus: boolean) => void | Fired when the audio focus changes (e.g., another app starts playing audio). |
| `onBandwidthUpdate` | (data: [BandwidthData](../api-reference/interfaces/BandwidthData.md)) => void | Fired with an estimate of the available bandwidth. |
| `onBuffer` | (buffering: boolean) => void | Fired when the player starts or stops buffering data. |
| `onControlsVisibleChange` | (visible: boolean) => void | Fired when the visibility of native controls changes. |
| `onEnd` | () => void | Fired when the video playback reaches the end. |
| `onExternalPlaybackChange` | (externalPlaybackActive: boolean) => void | Fired when the external playback status changes (e.g., AirPlay). |
| `onLoad` | (data: [onLoadData](../api-reference/interfaces/onLoadData.md)) => void | Fired when the video has loaded and is ready to play. |
| `onLoadStart` | (data: [onLoadStartData](../api-reference/interfaces/onLoadStartData.md)) => void | Fired when the video starts loading. |
| `onPlaybackRateChange` | (rate: number) => void | Fired when the playback rate changes. |
| `onPlaybackStateChange` | (data: [onPlaybackStateChangeData](../api-reference/interfaces/onPlaybackStateChangeData.md)) => void | Fired when the playback state changes (e.g., playing, paused, stopped). |
| `onProgress` | (data: [onProgressData](../api-reference/interfaces/onProgressData.md)) => void | Fired periodically during playback with the current time. |
| `onReadyToDisplay` | () => void | Fired when the player is ready to display the first frame of the video. |
| `onSeek` | (seekTime: number) => void | Fired when a seek operation has completed. |
| `onStatusChange` | (status: [VideoPlayerStatus](../api-reference/type-aliases/VideoPlayerStatus.md)) => void | Fired when the player status changes (detailed status updates). |
| `onTextTrackDataChanged` | (texts: string[]) => void | Fired when text track data (e.g., subtitles) changes. |
| `onTimedMetadata` | (metadata: [TimedMetadata](../api-reference/interfaces/TimedMetadata.md)) => void | Fired when timed metadata is encountered in the video stream. |
| `onTrackChange` | (track: [TextTrack](../api-reference/interfaces/TextTrack.md) \| null) => void | Fired when the selected text track changes. |
| `onVolumeChange` | (data: [onVolumeChangeData](../api-reference/interfaces/onVolumeChangeData.md)) => void | Fired when the volume changes. |
Additionally, the `VideoPlayer` instance itself has an `onError` property:
- `onError: (error: ` [VideoRuntimeError](../api-reference/classes/VideoRuntimeError.md) `) => void` — Fired when an error occurs. The callback receives the `VideoRuntimeError` object.
**Benefits of `useEvent`**:
- **Automatic Cleanup**: The event listener is automatically removed when the component unmounts or when the `player`, `event`, or `callback` dependencies change, preventing memory leaks.
- **Type Safety**: Provides better type inference for event callback parameters.
This hook is recommended for managing event subscriptions in a declarative React style.
### Initialization Timing and Events
`onLoadStart` / `onLoad` will fire automatically after construction when `initializeOnCreation` (default `true`) is enabled. If you set `initializeOnCreation: false`, these events will not fire until you call `initialize()` or `preload()`. Attach your event handlers before invoking those methods to avoid missing early events.
## Subscribing to Events
You can subscribe to an event by assigning a function to the player instance's corresponding property:
```typescript
import { VideoPlayer } from 'react-native-video';
const player = new VideoPlayer('https://example.com/video.mp4');
player.addEventListener('onLoad', (data) => {
console.log('Video loaded! Duration:', data.duration);
});
player.addEventListener('onProgress', (data) => {
console.log('Current time:', data.currentTime);
});
player.addEventListener('onError', (error) => {
console.error('Player Error:', error.code, error.message);
});
player.play();
```
## Clearing Events
- The `player.clearEvent(eventName)` method can be used to clear a specific native event handler.
- When a player instance is no longer needed and `player.release()` is called, all event listeners are automatically cleared

119
docs/docs/installation.md Normal file
View File

@@ -0,0 +1,119 @@
---
sidebar_position: 2
description: React Native Video Installation Guide and Requirements
---
# Installation
React Native Video is a library that allows you to play various kind of videos in a React Native application. It is built on top of the [`react-native-nitro-modules`](https://nitro.margelo.com/docs/what-is-nitro) type-safe and extremely fast native modules framework. React Native Video supports both New Architecture and Old Architecture.
## Requirements
### System Requirements
- iOS `15.0` or higher
- Android `6.0` or higher
### Minimal Package Requirements
- `react-native` `0.75.0` or higher
- `react-native-nitro-modules` `0.27.2` or higher
## Installation
1. Install dependencies:
```bash
npm install react-native-video@next react-native-nitro-modules
```
2. Configure Library:
You can configure the library in two ways:
- [Using expo plugins](./configuration/expo-plugin.md)
- [Manually editing needed files](./configuration/manual.md)
3. Run the project:
If you are using Expo, you will need to generate native files:
```bash
npx expo prebuild
```
And then run the project:
```bash
npx expo run:ios # run on iOS
npx expo run:android # run on Android
```
If you are using React Native CLI, you will need to install Pods for iOS:
```bash
cd ios && pod install && cd ..
```
And then run the project:
```bash
npx react-native run-ios # run on iOS
npx react-native run-android # run on Android
```
## Patch for react-native < 0.80
`react-native` < 0.80 have bug that prevents to properly handle errors by nitro modules on Android.
We highly recommend to apply bellow patch for `react-native-nitro-modules` to fix this issue.
You can apply it using `patch-package`.
:::warning
Without this patch you won't be able "recognize" errors, all will be thrown as unknown errors.
:::
<details>
<summary>For `react-native-nitro-modules` 0.27.X or higher</summary>
```diff
diff --git a/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp b/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
index efcea05..ffad3f2 100644
--- a/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
+++ b/node_modules/react-native-nitro-modules/cpp/core/HybridFunction.hpp
@@ -23,6 +23,10 @@ struct JSIConverter;
#include <string>
#include <type_traits>
+#ifdef ANDROID
+#include <fbjni/fbjni.h>
+#endif
+
namespace margelo::nitro {
using namespace facebook;
@@ -109,6 +113,15 @@ public:
std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
std::string message = exception.what();
throw jsi::JSError(runtime, funcName + ": " + message);
+#ifdef ANDROID
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wexceptions"
+ } catch (const jni::JniException& exception) {
+ std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
+ std::string message = exception.what();
+ throw jsi::JSError(runtime, funcName + ": " + message);
+#pragma clang diagnostic pop
+#endif
} catch (...) {
// Some unknown exception was thrown - add method name information and re-throw as `JSError`.
std::string funcName = getHybridFuncFullName<THybrid>(kind, name, hybridInstance.get());
```
see [raw](https://github.com/TheWidlarzGroup/react-native-video/blob/v7/example/patches/react-native-nitro-modules%2B0.27.2.patch)
</details>
## Usage
```tsx title="App.tsx"
import { VideoView, useVideoPlayer } from 'react-native-video';
export default function App() {
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
return <VideoView player={player} />;
}
```

53
docs/docs/intro.md Normal file
View File

@@ -0,0 +1,53 @@
---
sidebar_position: 1
description: Introduction to React Native Video library
---
# Intro
**One of the most popular video playback libraries for React Native applications.**
`react-native-video` provides a comprehensive solution for video playback in React Native, built on top of [`react-native-nitro-modules`](https://nitro.margelo.com/docs/what-is-nitro) framework. Whether you're building a video streaming app, media player, or educational platform, `react-native-video` gives you the tools you need to create exceptional video experiences.
## Why Choose `react-native-video`?
### Native Performance
`react-native-video` is built with native video players (AVPlayer on iOS, ExoPlayer on Android) to ensure hardware-accelerated playback for smooth performance. The library includes optimized memory management and resource handling to provide the best possible user experience.
### Rich Feature Set
The library offers advanced playback controls including play, pause, seek, volume control, and playback rate adjustment. It supports multiple source types including HTTP/HTTPS streams, local files, HLS, and DASH. `react-native-video` includes subtitle support for both built-in and external subtitle files (WebVTT, SRT), native Picture-in-Picture support on both platforms, background audio playback and native fullscreen implementation.
### Developer Experience
`react-native-video` is TypeScript-first with full TypeScript support and comprehensive type definitions. It provides intuitive React hooks for easy integration. The library is compatible with React Native's New Architecture and works seamlessly with Expo managed and bare workflows.
## Quick Start
Get started in minutes with a simple video player:
```bash
npm install react-native-video@next react-native-nitro-modules
```
```tsx
import { VideoView, useVideoPlayer } from 'react-native-video';
export default function App() {
const player = useVideoPlayer({
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
});
return <VideoView player={player} />;
}
```
## What's Next?
- **[Installation Guide](./installation.md)** - Get started with `react-native-video`
- **[VideoPlayer](./player/player.md)** - Learn about the core player functionality
- **[VideoView Component](./video-view.md)** - Understand the video display component
- **[Event Handling](./events/events.md)** - Master player events and callbacks
- **[Configuration](./configuration/expo-plugin.md)** - Configure the player
- **[API Reference](./api-reference/index.md)** - Complete API documentation
[![React Native Video](../static/baners/rnv-banner.png)](https://www.thewidlarzgroup.com/react-native-video/?utm_source=rnv&utm_medium=docs&utm_campaign=intro&utm_id=rnv-banner)

199
docs/docs/player/drm.md Normal file
View File

@@ -0,0 +1,199 @@
---
sidebar_position: 3
sidebar_label: DRM
---
# DRM
## What is DRM (Digital Rights Management)?
DRM is a set of access control technologies that are used to protect copyrighted content from unauthorized use and distribution. It allows content owners to control how their digital media is used and distributed.
### When do you need it?
If you are working with copyrighted content and want to prevent unauthorized access or distribution, you will need DRM. It is especially important for streaming services, e-learning platforms, and any application that delivers premium content that you want to protect from piracy.
### What next?
This page explains how to play DRMprotected content with React Native Video using the official DRM plugin. It covers installing and enabling the plugin, configuring sources with DRM, and platformspecific notes for Android (Widevine) and iOS/visionOS (FairPlay).
## Install and enable the DRM plugin
:::tip Pluginable Architecture
React Native Video uses a plugin architecture. DRM support is provided by the `@twg/react-native-video-drm` plugin and is not built into the core package.
:::
1) Install dependencies in your app:
```sh
npm install @twg/react-native-video-drm
```
2) Enable the plugin at app startup (before creating any players):
```ts
// App.tsx (or any place you want to initialize the plugin)
import { enable } from '@twg/react-native-video-drm';
enable();
```
The plugin autolinks on both Android and iOS. Nitro Modules are required because the plugin uses Nitro under the hood.
## Quick start
You pass DRM configuration via `VideoConfig.drm` when creating a player or using the `useVideoPlayer` hook. If `drm.type` is omitted, the default is inferred per platform (`widevine` on Android, `fairplay` on iOS/visionOS).
```tsx
import { VideoView, useVideoPlayer } from 'react-native-video';
export function Player() {
const player = useVideoPlayer({
source: {
uri: 'https://example.com/manifest.mpd', // or HLS .m3u8 for FairPlay
// On iOS these headers are also used for the default license request
headers: { Authorization: 'Bearer <token>' },
drm: {
// type: 'widevine' | 'fairplay' // optional; inferred by platform
licenseUrl: 'https://license.example.com/widevine',
},
},
});
return <VideoView player={player} />;
}
```
:::warning
You shouldn't include your authorization token directly in the code. Instead, use a backend method to retrieve it at runtime.
:::
## DRM config reference
All properties are optional unless marked otherwise for a platform. The table below describes each property, the expected type, platforms where it applies, whether it's required, and important notes.
| Property | Type | Platform | Required | Notes |
|---|---:|---|:---:|---|
| `type` | `'widevine' \| 'fairplay'` | Android, iOS, visionOS | No (defaulted) | Default inferred per platform when `drm` is present and `type` omitted (Android → `widevine`, iOS/visionOS → `fairplay`). |
| `licenseUrl` | `string` | Android, iOS, visionOS | Android: Yes; iOS/visionOS: Yes for default/custom flows | URL of the license (CKC) service. Required for license acquisition. |
| `licenseHeaders` | ``Record<string, string>`` | Android | No | Extra headers sent with the Widevine license request. (On iOS, use `source.headers` for license requests.) |
| `multiSession` | `boolean` | Android | No | Whether to allow multiple Widevine sessions. |
| `certificateUrl` | `string` | iOS, visionOS | Yes (for FairPlay) | URL to fetch the FairPlay application certificate (used to create the SPC). |
| `contentId` | `string` | iOS, visionOS | No | If omitted, derived from the `skd://` key URL. Used when creating the SPC. |
| `getLicense` | ``(payload) => Promise<string>`` | iOS, visionOS | No | Optional hook for custom FairPlay license logic; must resolve to a base64encoded CKC string. |
Payload shape passed to `getLicense` (iOS/visionOS):
| Field | Type | Description |
|---|---:|---|
| `contentId` | `string` | Content identifier for the asset. If not provided the player will try to derive it from the `skd://` key URL. |
| `licenseUrl` | `string` | The license server URL that should be used for license acquisition. |
| `keyUrl` | `string` | The key URL/identifier received from the stream (typically an `skd://` URL). |
| `spc` | `string` | The SPC (secure playback context) as a base64encoded string. Send raw SPC bytes to your license server (server side may expect raw bytes rather than base64). |
## Android: Widevine
- Set `drm.type` to `'widevine'` or omit it (the library will default to Widevine on Android if `drm` is present).
- `licenseUrl` is required; `licenseHeaders` and `multiSession` are optional.
- Implementation details:
- The plugin uses ExoPlayers `DefaultDrmSessionManager` and `HttpMediaDrmCallback` with your `licenseUrl` and `licenseHeaders`.
- If a first attempt fails due to device security level issues, the plugin retries with `L3` security level.
Example:
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/manifest.mpd',
drm: {
// type: 'widevine', // optional
licenseUrl: 'https://license.example.com/widevine',
licenseHeaders: { 'X-Custom-Header': 'value' },
multiSession: false,
},
},
});
```
## iOS and visionOS: FairPlay
Two ways to get the CKC (license):
1) Default flow (no `getLicense`):
- Required: `certificateUrl`, `licenseUrl`.
- The plugin requests the application certificate, generates the SPC, then POSTs the SPC to `licenseUrl`.
- It uses `source.headers` (not `drm.licenseHeaders`) for the license request.
2) Custom flow (provide `getLicense`):
- Required: `certificateUrl`, `licenseUrl`, and a `getLicense` implementation.
- You receive `{ contentId, licenseUrl, keyUrl, spc }` and must return a base64encoded CKC string.
Notes:
- DRM isnt supported in the iOS Simulator; the plugin returns `null` for DRM in Simulator builds.
- If `contentId` isnt provided, it is derived from the `skd://` key URL.
Default flow example:
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/fairplay.m3u8',
headers: { Authorization: 'Bearer <token>' }, // used for the license request
drm: {
// type: 'fairplay', // optional
certificateUrl: 'https://license.example.com/fps-cert',
licenseUrl: 'https://license.example.com/fps',
// contentId: 'my-content-id', // optional
},
},
});
```
Custom `getLicense` example:
:::tip
This is example code for a custom `getLicense` implementation. it may differ from your actual implementation provided by your DRM provider
:::
```tsx
useVideoPlayer({
source: {
uri: 'https://example.com/fairplay.m3u8',
drm: {
certificateUrl: 'https://license.example.com/fps-cert',
licenseUrl: 'https://license.example.com/fps',
getLicense: async ({ contentId, licenseUrl, keyUrl, spc }) => {
// Example: POST SPC to your license server and return base64 CKC
const res = await fetch(licenseUrl, {
method: 'POST',
body: Buffer.from(spc, 'base64'), // server expects raw SPC bytes
headers: {
'Content-Type': 'application/octet-stream',
'X-Content-ID': contentId,
'X-Asset-Id': keyUrl,
},
});
if (!res.ok) throw new Error(`License request failed: ${res.status}`);
const ckc = await res.arrayBuffer();
// return base64 CKC string
return Buffer.from(ckc).toString('base64');
},
},
},
});
```
## Offline
If you are looking for implementing offline playback with DRM, make sure to checkout our [Offline Video SDK](https://www.thewidlarzgroup.com/offline-video-sdk). It provides a comprehensive solution for downloading and playing Streams and DRM-protected content.
## Troubleshooting
- DRMPluginNotFound: Ensure you installed `@twg/react-native-video-drm`, imported it, and called `enable()` before creating any players.
- iOS headers: The default FairPlay flow uses `source.headers` for license requests; `drm.licenseHeaders` are not used on iOS.
- Invalid CKC: `getLicense` must return a base64 string. Returning raw bytes or JSON will fail.
- 403/415 from license server: Verify required auth headers, content type, and whether the server expects raw SPC bytes vs base64.
- Android security level issues: The plugin retries with Widevine L3 if the first attempt fails.
- iOS Simulator: DRM isnt supported in Simulator. Test on a real device.
## Notes and defaults
- If `drm` is provided without `type`, the library sets a platform default: Android → Widevine, iOS/visionOS → FairPlay.
- For custom DRM systems or advanced pipelines, you can implement your own plugin. See the Plugin Interface docs.

View File

@@ -0,0 +1,133 @@
---
sidebar_label: Player Lifecycle
sidebar_position: 2
---
# Player Lifecycle
Understanding the lifecycle of the `VideoPlayer` is crucial for managing resources effectively and ensuring a smooth user experience.
## Creation and Initialization
1. **Instantiation**: A `VideoPlayer` instance is created by calling its constructor with a video source (URL, `VideoSource`, or `VideoConfig`).
```typescript
const player = new VideoPlayer('https://example.com/video.mp4');
```
2. **Native Player Allocation**: A lightweight native player object is allocated immediately.
3. **Asset Initialization**: By default (unless you opt out) the underlying media item is prepared **asynchronously right after creation**. You can control this with `initializeOnCreation` inside `VideoConfig`.
### Deferred Initialization (Advanced)
If you pass a `VideoConfig` with `{ initializeOnCreation: false }`, the player will skip preparing the media item automatically. This is useful when:
- You need to batchcreate many players without incurring immediate decoding / network cost
- You want to attach event handlers before any network requests happen
- You want explicit control over when buffering begins (e.g. on user interaction)
To initialize later, call:
```ts
await player.initialize();
// or preload if you also want it prepared & ready
await player.preload();
```
### Initialization Methods Comparison
| Method | When to use | What it does |
|--------|-------------|--------------|
| `initialize()` | You deferred initialization and now want to create the native player item / media source | Creates & attaches the underlying player item / media source without starting playback |
| `preload()` | You want the player item prepared (buffering kicked off) ahead of an upcoming `play()` call | Ensures the media source is set and prepared; resolves once preparation started (may already be initialized) |
| Implicit (default) | `initializeOnCreation` not set or `true` | Automatically schedules initialization after JS construction |
:::info
By default, the player initializes automatically after construction. If you need to defer initialization, set `initializeOnCreation: false` in the config. You can then call `player.initialize()` or `player.preload()` later to start the player.
:::
## Playing a Video
1. **Loading**: When the player (auto) initializes, `preload()` is called, or after `replaceSourceAsync()`, the player starts loading the video metadata and buffering content.
- `onLoadStart`: Fired when the video starts loading.
- `onLoad`: Fired when the video metadata is loaded and the player is ready to play (duration, dimensions, etc., are available).
- `onBuffer`: Fired when buffering starts or ends.
2. **Playback**: Once enough data is buffered, playback begins.
- `onPlaybackStateChange`: Fired when the playback state changes (e.g., from `buffering` to `playing`).
- `onProgress`: Fired periodically with the current playback time.
- `onReadyToDisplay`: Fired when the first frame is ready to be displayed.
## Controlling Playback
- `pause()`: Pauses playback. `status` changes to `paused`.
- `seekTo(time)`, `seekBy(time)`: Changes the current playback position. `onSeek` is fired when the seek operation completes.
- `set volume(value)`, `set muted(value)`, `set loop(value)`, `set rate(value)`: Modify player properties. Corresponding events like `onVolumeChange` or `onPlaybackRateChange` might be fired.
## Changing Source
- `replaceSourceAsync(newSource)`: This method allows you to change the video source dynamically.
1. The current native player resources associated with the old source are released (similar to `release()` but specifically for the source).
2. A new native player instance (or reconfigured existing one) is prepared for the `newSource`.
3. The loading lifecycle events (`onLoadStart`, `onLoad`, etc.) will fire for the new source.
- `replaceSourceAsync(null)`: This effectively unloads the current video and releases its associated resources without loading a new one. This is useful for freeing up memory if the player is temporarily not needed but might be used again later.
## Releasing Resources
There are two main ways to release resources:
1. **`replaceSourceAsync(null)`**: This is a less destructive way to free resources related *only* to the current video source.
- The `VideoPlayer` instance itself remains usable.
- You can later call `replaceSourceAsync(newSource)` to load and play a new video.
2. **`release()`**: This is a destructive operation.
:::danger
After calling `release()`, the player instance becomes unusable. Any subsequent calls to its methods or property access will result in errors.
:::
:::tip
It is recommended to use `replaceSourceAsync(null)` when you want to free resources related to the current video source. You should call `release()` only when you are 100% sure that you don't need the player instance anymore. Anyway garbage collector will release the player instance when it is no longer needed.
:::
## Error Handling
- The `onError` callback, if provided, will be called when a `VideoRuntimeError` occurs. This allows you to handle issues like network errors, invalid source, or platform-specific playback problems.
- If `onError` is not provided, errors might be thrown as exceptions.
## Using with Hooks (`useVideoPlayer`)
The `useVideoPlayer` hook simplifies managing the `VideoPlayer` lifecycle within React components.
```typescript
import { useVideoPlayer } from 'react-native-video';
const MyComponent = () => {
const player = useVideoPlayer('https://example.com/video.mp4', (playerInstance) => {
// Optional setup function: configure the player instance after creation
playerInstance.loop = true;
});
// ... use player ...
return <VideoView player={player} />;
};
```
- **Automatic Creation**: `useVideoPlayer` creates a `VideoPlayer` instance when the component mounts or when the source dependency changes.
- **Automatic Cleanup**: It automatically cleanup resources when the component unmounts or before recreating the player due to a source change. This prevents resource leaks.
- **Dependency Management**: If the `source` prop passed to `useVideoPlayer` changes, the hook will clean up the old player instance and create a new one with the new source.
:::tip
Using `useVideoPlayer` is the recommended way to manage `VideoPlayer` instances in functional components to ensure proper lifecycle management and resource cleanup. It will also respect `initializeOnCreation` (defaults to `true`). If you need deferred initialization with the hook:
```tsx
const player = useVideoPlayer({
source: { uri: 'https://example.com/video.mp4' },
initializeOnCreation: false,
}, (instance) => {
// Attach listeners first
instance.onLoad = () => console.log('Loaded');
});
// Later (e.g. on user tap)
await player.initialize(); // or player.preload()
player.play();
```
:::

150
docs/docs/player/player.md Normal file
View File

@@ -0,0 +1,150 @@
---
sidebar_position: 1
sidebar_label: Player
---
# Player
The `VideoPlayer` class is the primary way to control video playback. It provides methods and properties to manage the video source, playback state, volume, and other aspects of the video.
## Initialization
To use the `VideoPlayer`, you first need to create an instance of it with a video source. There are two ways to do this. By default the native media item is initialized asynchronously right after creation (unless you opt out with `initializeOnCreation: false`).
using `useVideoPlayer` hook
```tsx
import { useVideoPlayer } from 'react-native-video';
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
```
:::info
`useVideoPlayer` hook is recommended for most use cases. It automatically manages the player lifecycle between the component mount and unmount.
:::
or using `VideoPlayer` class constructor directly
```typescript
import { VideoPlayer } from 'react-native-video';
// Using a URL string
const player = new VideoPlayer('https://example.com/video.mp4');
// Using a VideoSource object
const playerWithSource = new VideoPlayer({ uri: 'https://example.com/video.mp4' });
// Using a VideoConfig object
const playerWithConfig = new VideoPlayer({
source: { uri: 'https://example.com/video.mp4' },
// other configurations
});
```
:::warning
When using `VideoPlayer` class directly, you need to manually manage the player lifecycle. Once you no longer need the player, you need to call `release()` method to release the player's native resources. see [Player Lifecycle](./player-lifecycle.md) for more details.
:::
## Core Functionality
The `VideoPlayer` class offers a comprehensive set of methods and properties to control video playback:
### Playback Control
| Method | Description |
|--------|-------------|
| `play()` | Starts or resumes video playback. |
| `pause()` | Pauses video playback. |
| `seekBy(time: number)` | Seeks the video forward or backward by the specified number of seconds. |
| `seekTo(time: number)` | Seeks the video to a specific time in seconds. |
| `replaceSourceAsync(source: VideoSource \| VideoConfig \| null)` | Replaces the current video source with a new one. Pass `null` to release the current source without replacing it. |
| `initialize()` | Manually initialize the underlying native player item when `initializeOnCreation` was set to `false`. No-op if already initialized. |
| `preload()` | Ensures the media source is set and prepared (buffering started) without starting playback. If not yet initialized it will initialize first. |
| `release()` | Releases the player's native resources. The player is no longer usable after calling this method. **Note:** If you intend to reuse the player instance with a different source, use `replaceSourceAsync(null)` to clear resources instead of `release()`. |
### Properties
| Property | Access | Type | Description |
|----------|--------|------|-------------|
| `source` | Read-only | `VideoPlayerSource` | Gets the current `VideoPlayerSource` object. |
| `status` | Read-only | `VideoPlayerStatus` | Gets the current status (e.g., `playing`, `paused`, `buffering`). |
| `duration` | Read-only | `number` | Gets the total duration of the video in seconds. Returns `NaN` until metadata is loaded. |
| `volume` | Read/Write | `number` | Gets or sets the player volume (0.0 to 1.0). |
| `currentTime` | Read/Write | `number` | Gets or sets the current playback time in seconds. |
| `muted` | Read/Write | `boolean` | Gets or sets whether the video is muted. |
| `loop` | Read/Write | `boolean` | Gets or sets whether the video should loop. |
| `rate` | Read/Write | `number` | Gets or sets the playback rate (e.g., 1.0 for normal speed, 0.5 for half speed, 2.0 for double speed). |
| `mixAudioMode` | Read/Write | `MixAudioMode` | Controls how this player's audio mixes with other audio sources (see [MixAudioMode](../api-reference/type-aliases/MixAudioMode.md)). |
| `ignoreSilentSwitchMode` | Read/Write | `IgnoreSilentSwitchMode` | iOS-only. Determines how audio should behave when the hardware mute (silent) switch is on. |
| `playInBackground` | Read/Write | `boolean` | Whether playback should continue when the app goes to the background. |
| `playWhenInactive` | Read/Write | `boolean` | Whether playback should continue when the app is inactive (e.g., during a phone call). |
| `isPlaying` | Read-only | `boolean` | Returns `true` if the video is currently playing. |
| `selectedTrack` | Read-only | `TextTrack \| undefined` | Currently selected text track, or `undefined` when no track is selected. |
### Error Handling
| Property | Type | Description |
|----------|------|-------------|
| `onError?` | `(error: VideoRuntimeError) => void` | A callback function that is invoked when a runtime error occurs in the player. You can use this to catch and handle errors gracefully. |
### Buffer Config
You can finetune buffering via `bufferConfig` on the `VideoConfig` you pass to `useVideoPlayer`/`VideoPlayer`. This controls how much data is buffered, live latency targets, and iOS network constraints.
Example
```ts
const player = useVideoPlayer({
source: {
uri: 'https://example.com/stream.m3u8',
bufferConfig: {
// Android
minBufferMs: 5000,
maxBufferMs: 10000,
// iOS
preferredForwardBufferDurationMs: 3000,
// Live (crossplatform target)
livePlayback: { targetOffsetMs: 500 },
},
},
});
```
#### Android
Properties below are Androidonly
| Property | Type | Description |
|----------|------|-------------|
| `minBufferMs` | `number` | Minimum media duration the player attempts to keep buffered (ms). Default: 5000. |
| `maxBufferMs` | `number` | Maximum media duration the player attempts to buffer (ms). Default: 10000. |
| `bufferForPlaybackMs` | `number` | Media that must be buffered before playback can start or resume after user action (ms). Default: 1000. |
| `bufferForPlaybackAfterRebufferMs` | `number` | Media that must be buffered to resume after a rebuffer (ms). Default: 2000. |
| `backBufferDurationMs` | `number` | Duration kept behind the current position to allow instant rewind without rebuffer (ms). |
| `livePlayback.minPlaybackSpeed` | `number` | Minimum playback speed used to maintain target live offset. |
| `livePlayback.maxPlaybackSpeed` | `number` | Maximum playback speed used to catch up to target live offset. |
| `livePlayback.minOffsetMs` | `number` | Minimum allowed live offset (ms). |
| `livePlayback.maxOffsetMs` | `number` | Maximum allowed live offset (ms). |
| `livePlayback.targetOffsetMs` | `number` | Target live offset the player tries to maintain (ms). |
#### iOS, visionOS, tvOS
Properties below are Apple platformsonly
| Property | Type | Description |
|----------|------|-------------|
| `preferredForwardBufferDurationMs` | `number` | Preferred duration the player attempts to retain ahead of the playhead (ms). |
| `preferredPeakBitRate` | `number` | Desired limit of network bandwidth for loading the current item (bits per second). |
| `preferredMaximumResolution` | `{ width: number; height: number }` | Preferred maximum video resolution. |
| `preferredPeakBitRateForExpensiveNetworks` | `number` | Bandwidth limit for expensive networks (e.g., cellular), in bits per second. |
| `preferredMaximumResolutionForExpensiveNetworks` | `{ width: number; height: number }` | Preferred maximum resolution on expensive networks. |
| `livePlayback.targetOffsetMs` | `number` | Target live offset (ms) the player will try to maintain. |
## DRM
Protected content is supported via a plugin. See the full DRM guide: [DRM](./drm.md).
Quick notes:
- Install and enable the official plugin `@twg/react-native-video-drm` and call `enable()` at app startup before creating players.
- Pass DRM configuration on the source using the `drm` property of `VideoConfig` (see the DRM guide for platform specifics and `getLicense` examples).
- If you defer initialization (`initializeOnCreation: false`), be sure to call `await player.initialize()` (or `preload()`) before expecting DRM license acquisition events.

View File

@@ -0,0 +1,100 @@
---
title: Plugin Usage Examples
description: Simple examples of implementing common plugin scenarios for React Native Video
sidebar_position: 3
---
# Plugin Usage Examples
This document provides practical examples of implementing common plugin scenarios for React Native Video.
## Basic Plugin Template
```kotlin title="Android"
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") {
override fun onPlayerCreated(player: WeakReference<NativeVideoPlayer>) {
Log.d("MyPlugin", "Player created with uri ${player.get()?.source.uri}")
}
override fun onPlayerDestroyed(player: WeakReference<NativeVideoPlayer>) {
Log.d("MyPlugin", "Player destroyed")
}
override fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource {
Log.d("MyPlugin", "Overriding source with uri ${source.uri}")
return source
}
}
// Usage
val plugin = MyPlugin() // Automatically registered
```
```swift title="iOS"
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPlugin")
}
override func onPlayerCreated(player: Weak<NativeVideoPlayer>) {
// Custom logic when player is created
}
override func onPlayerDestroyed(player: Weak<NativeVideoPlayer>) {
// Custom cleanup when player is destroyed
}
override func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource {
// Modify source if needed
return source
}
}
// Usage
let plugin = MyPlugin() // Automatically registered
```
## DRM Plugin
Implement custom DRM handling for protected content.
:::warning
DRM plugins are not supported yet in React Native Video. `getDRMManager` is not implemented yet and will have no effect.
:::
```kotlin title="Android"
class CustomDRMPlugin : ReactNativeVideoPlugin("CustomDRM") {
override fun getDRMManager(source: NativeVideoPlayerSource): Any? {
if (source.isDRMProtected() && source.drmType == "custom") {
return CustomDRMManager(
licenseUrl = source.drmLicenseUrl,
certificateUrl = source.drmCertificateUrl,
keyId = source.drmKeyId
)
}
return null
}
}
```
```swift title="iOS"
class CustomDRMPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "CustomDRM")
}
override func getDRMManager(source: NativeVideoPlayerSource) async -> Any? {
guard source.isDRMProtected() && source.drmType == "custom" else {
return nil
}
return CustomDRMManager(
licenseUrl: source.drmLicenseUrl,
certificateUrl: source.drmCertificateUrl,
keyId: source.drmKeyId
)
}
}
```

View File

@@ -0,0 +1,355 @@
---
title: Plugin Interface
description: Reference for the React Native Video plugin interface
sidebar_position: 2
---
# Plugin Interface Reference
This document provides a complete reference for the `ReactNativeVideoPluginSpec` interface and the base `ReactNativeVideoPlugin` implementation.
## ReactNativeVideoPluginSpec Interface
### Required Properties
#### id: String
Unique identifier for the plugin. Must be unique across all registered plugins.
```kotlin
// Android
override val id: String = "my_unique_plugin_id"
```
```swift
// iOS
var id: String { "my_unique_plugin_id" }
```
#### name: String
Human-readable name for the plugin, used in debug logging.
```kotlin
// Android
override val name: String = "My Custom Plugin"
```
```swift
// iOS
var name: String { "My Custom Plugin" }
```
## Lifecycle Methods
### Player Lifecycle
#### onPlayerCreated
Called when a new player instance is created.
```kotlin
// Android
@UnstableApi
fun onPlayerCreated(player: WeakReference<NativeVideoPlayer>)
```
```swift
// iOS
func onPlayerCreated(player: Weak<NativeVideoPlayer>)
```
**Parameters:**
- `player`: Weak reference to the newly created player instance
**Use Cases:**
- Initialize player-specific resources
- Set up player event listeners
- Configure player settings
#### onPlayerDestroyed
Called when a player instance is being destroyed.
```kotlin
// Android
@UnstableApi
fun onPlayerDestroyed(player: WeakReference<NativeVideoPlayer>)
```
```swift
// iOS
func onPlayerDestroyed(player: Weak<NativeVideoPlayer>)
```
**Parameters:**
- `player`: Weak reference to the player instance being destroyed
**Use Cases:**
- Clean up player-specific resources
- Remove event listeners
- Save state or analytics data
### Video View Lifecycle
#### onVideoViewCreated
Called when a new video view is created.
```kotlin
// Android
@UnstableApi
fun onVideoViewCreated(view: WeakReference<VideoView>)
```
```swift
// iOS
func onVideoViewCreated(view: Weak<VideoComponentView>)
```
**Parameters:**
- `view`: Weak reference to the newly created video view
**Use Cases:**
- Configure view-specific settings
- Set up UI event handlers
- Initialize view overlays
#### onVideoViewDestroyed
Called when a video view is being destroyed.
```kotlin
// Android
@UnstableApi
fun onVideoViewDestroyed(view: WeakReference<VideoView>)
```
```swift
// iOS
func onVideoViewDestroyed(view: Weak<VideoComponentView>)
```
**Parameters:**
- `view`: Weak reference to the video view being destroyed
**Use Cases:**
- Clean up view-specific resources
- Remove UI event handlers
- Save view state
## Content Modification Methods
### overrideSource
Modify the video source before it's processed by the player.
```kotlin
// Android
fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource
```
```swift
// iOS
func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource
```
**Parameters:**
- `source`: The original video source
**Returns:**
- Modified video source (can be the same instance if no changes needed)
**Use Cases:**
- Add authentication headers
- Modify URLs (e.g., CDN switching)
- Add tracking parameters
- Transform source format
### getDRMManager
Provide a custom DRM manager for protected content.
```kotlin
// Android
fun getDRMManager(source: NativeVideoPlayerSource): Any?
```
```swift
// iOS
func getDRMManager(source: NativeVideoPlayerSource) async -> Any?
```
**Parameters:**
- `source`: The video source that may require DRM
**Returns:**
- DRM manager instance, or `null` if this plugin doesn't handle DRM for this source
**Use Cases:**
- Widevine DRM implementation
- FairPlay DRM implementation
- Custom DRM solutions
- License acquisition logic
## Android-Specific Methods
### getMediaDataSourceFactory
Override the data source factory used by ExoPlayer.
```kotlin
fun getMediaDataSourceFactory(
source: NativeVideoPlayerSource,
mediaDataSourceFactory: DataSource.Factory
): DataSource.Factory?
```
**Parameters:**
- `source`: The video source
- `mediaDataSourceFactory`: The default data source factory
**Returns:**
- Custom data source factory, or `null` to use the default
**Use Cases:**
- Custom caching strategies
- Network optimization
- Custom authentication
- Analytics data collection
### getMediaSourceFactory
Override the media source factory used by ExoPlayer.
```kotlin
fun getMediaSourceFactory(
source: NativeVideoPlayerSource,
mediaSourceFactory: MediaSource.Factory,
mediaDataSourceFactory: DataSource.Factory
): MediaSource.Factory?
```
**Parameters:**
- `source`: The video source
- `mediaSourceFactory`: The default media source factory
- `mediaDataSourceFactory`: The data source factory
**Returns:**
- Custom media source factory, or `null` to use the default
**Use Cases:**
- Custom media format support
- Advanced ExoPlayer configuration
- Source-specific optimizations
### getMediaItemBuilder
Override the media item builder used by ExoPlayer.
```kotlin
fun getMediaItemBuilder(
source: NativeVideoPlayerSource,
mediaItemBuilder: MediaItem.Builder
): MediaItem.Builder?
```
**Parameters:**
- `source`: The video source
- `mediaItemBuilder`: The default media item builder
**Returns:**
- Modified media item builder, or `null` to use the default
**Use Cases:**
- Add custom metadata
- Configure subtitles
- Set playback preferences
- Configure DRM settings
### shouldDisableCache
Control whether caching should be disabled for a source.
```kotlin
fun shouldDisableCache(source: NativeVideoPlayerSource): Boolean
```
**Parameters:**
- `source`: The video source
**Returns:**
- `true` to disable caching, `false` to allow caching
**Use Cases:**
- Disable caching for live streams
- Disable caching for DRM content
- Custom caching policies
## Base Implementation: ReactNativeVideoPlugin
The base class provides default implementations for all methods:
### Automatic Registration
```kotlin
// Android
init {
PluginsRegistry.shared.register(this)
}
```
```swift
// iOS
public init(name: String) {
self.name = name
self.id = "RNV_Plugin_\(name)"
PluginsRegistry.shared.register(plugin: self)
}
```
### Automatic Cleanup (iOS only)
```swift
deinit {
PluginsRegistry.shared.unregister(plugin: self)
}
```
### Default Implementations
All methods have sensible defaults:
- Lifecycle methods: No-op implementations
- `overrideSource`: Returns the original source unchanged
- `getDRMManager`: Returns `null`
- Factory methods (Android): Return `null` (use defaults)
- `shouldDisableCache`: Returns `false`
## Method Calling Order
### Source Processing Flow
1. `overrideSource` - Called for each registered plugin in order
2. `getDRMManager` - Called for each plugin until one returns non-null
3. Factory methods (Android) - Called for each plugin until one returns non-null
### Lifecycle Flow
1. View/Player creation methods called for all plugins
2. Source processing happens during playback
3. View/Player destruction methods called for all plugins
## Error Handling
### DRM Plugin Not Found
If no plugin provides a DRM manager when required:
```kotlin
// Android
throw LibraryError.DRMPluginNotFound
```
```swift
// iOS
throw LibraryError.DRMPluginNotFound.error()
```
### Best Practices
- Return `null` from optional methods when not providing custom behavior
- Handle weak reference nullability properly
- Use appropriate error handling in async methods (iOS)
- Log meaningful debug information
## Platform Differences Summary
| Feature | Android | iOS |
|---------|---------|-----|
| Async Support | No | Yes (async/await) |
| Media Factories | Full ExoPlayer support | Limited AVFoundation |
| Cache Control | Yes | No |
| Auto Cleanup | Manual | Automatic (deinit) |
| Weak References | `WeakReference<T>` | `Weak<T>` |

View File

@@ -0,0 +1,102 @@
---
title: Plugins
description: Overview of the React Native Video plugin system
sidebar_position: 6
---
# Plugins Overview
The **React Native Video** library offers a robust plugin system to extend and customize video functionality on Android and iOS. With plugins, you can override source handling, implement custom DRM, modify media factories, and respond to player lifecycle events.
:::tip Use in Third Party Library Guide
If you are looking how to import React Native Video in your native code to use plugins, you can checkout [Use in Third Party Library](../use-in-third-party-library) page.
:::
## What Are Plugins?
Plugins are modular extensions that hook into the video player's lifecycle and processing pipeline. They allow you to:
- **Customize video sources** before playback
- **Implement custom DRM** for protected content
- **Override media factories** (Android only)
- **React to player lifecycle events** for analytics, logging, or cleanup
- **Control caching behavior** for performance
## Architecture
The plugin system consists of three main components:
| Component | Description |
|:-----------------------------|:-------------------------------------------------------------------------------------------|
| `PluginsRegistry` | Singleton that manages plugin registration and coordinates plugin interactions |
| `ReactNativeVideoPluginSpec` | Interface/protocol defining the contract all plugins must implement |
| `ReactNativeVideoPlugin` | Base implementation with convenient defaults; override only the methods you care about |
:::tip
Plugins are automatically registered when you instantiate a class that extends `ReactNativeVideoPlugin`.
:::
## Core Concepts
### Plugin Lifecycle
Plugins receive notifications for key events:
- **Player creation/destruction** — react to player instance lifecycle
- **Video view creation/destruction** — handle UI component lifecycle
- **Source processing** — modify video sources before playback
- **DRM requests** — provide custom DRM handling
- **Media factory overrides** — customize ExoPlayer components (Android)
:::caution
All player and view references passed to plugins are **weak references** to prevent memory leaks.
:::
## Platform Differences
| Feature | Android (Kotlin / ExoPlayer) | iOS (Swift / AVFoundation) |
|:-------------------------------|:-----------------------------------------------|:-----------------------------------|
| Underlying player | ExoPlayer | AVFoundation |
| Media factory customization | Extensive | Limited |
| Cache control | Supported | Limited |
| Plugin method style | Synchronous | Async/await |
| Memory management | Manual cleanup | Manual cleanup |
## Getting Started
### Example: Android Plugin
```kotlin
// Android
class MyPlugin : ReactNativeVideoPlugin("MyPluginName") {
override fun onPlayerCreated(player: WeakReference<NativeVideoPlayer>) {
// Custom logic here
}
}
```
### Example: iOS Plugin
```swift
// iOS
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPluginName")
}
override func onPlayerCreated(player: Weak<NativeVideoPlayer>) {
// Custom logic here
}
}
```
## Next Steps
- [**Plugin Registry**](./registry) — Learn about plugin management and registration
- [**Plugin Interface**](./interface) — Complete API reference for plugin methods
- [**Usage Examples**](./examples) — Practical implementations and common patterns

View File

@@ -0,0 +1,188 @@
---
title: Plugin Registry
description: Overview of the React Native Video plugin registry and how to use it
sidebar_position: 1
---
# Plugin Registry
The `PluginsRegistry` is a singleton that manages all plugin instances and coordinates their interactions with the video player system. It handles registration, unregistration, and notification distribution to all active plugins.
## Singleton Pattern
Both Android and iOS use a shared singleton:
```kotlin title="Android"
// Register
PluginsRegistry.shared.register(plugin)
// Unregister
PluginsRegistry.shared.unregister(plugin)
```
```swift title="iOS"
// Register
PluginsRegistry.shared.register(plugin: plugin)
// Unregister
PluginsRegistry.shared.unregister(plugin: plugin)
```
:::tip Plugin Ordering
Plugins are processed in registration order. Later plugins can override earlier ones.
:::
## Registration Methods
### Automatic Registration
You can use the base class `ReactNativeVideoPlugin` to automatically register your plugin. This will also mock all the methods that are not implemented in your plugin.
:::danger
You still need to unregister your plugin manually when you are done with it. Otherwise, you will have a memory leak.
:::
```kotlin title="Android"
class MyPlugin : ReactNativeVideoPlugin("MyPlugin") {
// ...
}
val plugin = MyPlugin() // Auto-registered
```
```swift title="iOS"
class MyPlugin: ReactNativeVideoPlugin {
init() {
super.init(name: "MyPlugin")
}
// Auto-unregistered in deinit
}
let plugin = MyPlugin() // Auto-registered
```
### Manual Registration
You can also manually register your plugin. This is useful if you want to implement a plugin that is not a subclass of `ReactNativeVideoPlugin`.
You will need to implement the `ReactNativeVideoPluginSpec` interface. This is a protocol that defines the methods and properties that a plugin must implement.
```kotlin title="Android"
class MyCustomPlugin : ReactNativeVideoPluginSpec {
override val id = "my_custom_id"
override val name = "MyCustomPlugin"
// ...
}
val plugin = MyCustomPlugin()
PluginsRegistry.shared.register(plugin)
```
```swift title="iOS"
class MyCustomPlugin: ReactNativeVideoPluginSpec {
let id = "my_custom_id"
let name = "MyCustomPlugin"
// ...
}
let plugin = MyCustomPlugin()
PluginsRegistry.shared.register(plugin: plugin)
```
## Plugin ID Generation
When using the base class, IDs are auto-generated:
```kotlin title="Android"
ID Format: "RNV_Plugin_{name}"
Example: "RNV_Plugin_MyCustomDRM"
```
```swift title="iOS"
ID Format: "RNV_Plugin_{name}"
Example: "RNV_Plugin_MyCustomDRM"
```
## Plugin Internals
Bellow are the internals of the plugin registry, that shows logic for certain methods.
### Source Processing
The registry coordinates source modifications:
```kotlin title="Android"
internal fun overrideSource(source: NativeVideoPlayerSource): NativeVideoPlayerSource {
var overriddenSource = source
for (plugin in plugins.values) {
overriddenSource = plugin.overrideSource(overriddenSource)
}
return overriddenSource
}
```
```swift title="iOS"
internal func overrideSource(source: NativeVideoPlayerSource) async -> NativeVideoPlayerSource {
var overriddenSource = source
for plugin in plugins.values {
overriddenSource = await plugin.overrideSource(source: source)
}
return overriddenSource
}
```
### DRM Manager Resolution
Finds the first plugin that can provide a DRM manager:
```kotlin title="Android"
internal fun getDRMManager(source: NativeVideoPlayerSource): Any {
for (plugin in plugins.values) {
val manager = plugin.getDRMManager(source)
if (manager != null) return manager
}
throw LibraryError.DRMPluginNotFound
}
```
```swift title="iOS"
internal func getDrmManager(source: NativeVideoPlayerSource) async throws -> Any? {
for plugin in plugins.values {
if let drmManager = await plugin.getDRMManager(source: source) {
return drmManager
}
}
throw LibraryError.DRMPluginNotFound.error()
}
```
## Android-Specific Registry Methods
| Method Name | Purpose |
|:----------------------------------|:----------------------------------------------------------|
| `overrideMediaDataSourceFactory` | Override data source factory for custom ExoPlayer sources |
| `overrideMediaSourceFactory` | Override media source factory |
| `overrideMediaItemBuilder` | Customize the media item builder |
| `shouldDisableCache` | Control caching behavior |
Example signatures:
```kotlin
internal fun overrideMediaDataSourceFactory(
source: NativeVideoPlayerSource,
mediaDataSourceFactory: DataSource.Factory
): DataSource.Factory
internal fun overrideMediaSourceFactory(
source: NativeVideoPlayerSource,
mediaSourceFactory: MediaSource.Factory,
mediaDataSourceFactory: DataSource.Factory
): MediaSource.Factory
internal fun overrideMediaItemBuilder(
source: NativeVideoPlayerSource,
mediaItemBuilder: MediaItem.Builder
): MediaItem.Builder
internal fun shouldDisableCache(source: NativeVideoPlayerSource): Boolean
```
## Best Practices
- **Memory management**: Registry holds strong references to plugins; plugins get weak references to players/views.
- **Unregister plugins**: Use unregistration to prevent memory leaks.
- **Performance**: Minimize work in notification handlers. Cache expensive operations. Be mindful of plugin order.

19
docs/docs/projects.md Normal file
View File

@@ -0,0 +1,19 @@
---
title: Useful Projects
description: React Native Video Useful Projects
sidebar_class_name: hidden
---
# Useful Projects
This page lists open-source projects that can be helpful for your player implementation. <br/>
If you have a project that could benefit other users, feel free to open a PR to add it here.
## Our (TheWidlarzGroup) Libraries
- [react-native-video-player](https://github.com/TheWidlarzGroup/react-native-video-player): Our video player UI library.
- [Offline Video SDK](https://www.thewidlarzgroup.com/offline-video-sdk?utm_source=rnv&utm_medium=docs&utm_id=projects_offline-video-sdk): If you're building an app that needs **offline playback** (e.g., downloading HLS videos, subtitles, audio tracks, or DRM-protected content), check out our commercial Offline Video SDK. It integrates with `react-native-video` and is available with a [free trial](https://sdk.thewidlarzgroup.com/signup?utm_source=rnv&utm_medium=docs&utm_id=projects_start-trial-offline-video-sdk). To get started quickly, you can clone our [Offline Video Starter Project](https://github.com/TheWidlarzGroup/react-native-offline-video-starter?utm_source=rnv&utm_medium=docs&utm_id=projects_offline-video-starter), which includes a ready-to-run example app demonstrating offline playback, multi-audio, subtitles, and DRM setup.
## Community Libraries
- [react-native-corner-video](https://github.com/Lg0gs/react-native-corner-video): A floating video player.
- [react-native-track-player](https://github.com/doublesymmetry/react-native-track-player): A toolbox for audio playback.
- [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls): A video player UI.
- [react-native-media-console](https://github.com/criszz77/react-native-media-console): An updated version of react-native-video-controls, rewritten in TypeScript.

176
docs/docs/updating.md Normal file
View File

@@ -0,0 +1,176 @@
---
title: Updating
description: React Native Video Updating Guide
sidebar_class_name: hidden
---
## Upgrading from react-native-video v6 to v7
Version 7 of `react-native-video` introduces a significant architectural shift, separating the video player logic from the UI rendering. This change unlocks new capabilities like video preloading and a more intuitive, hook-based API. This guide will walk you through the necessary steps to migrate your application from v6 to v7.
### Key Changes in v7
The most substantial change in v7 is the move from a monolithic `<Video>` component to a more modular approach with two distinct components:
* **`VideoPlayer`**: A new class that manages the player's state and playback logic. It is not a UI component.
* **`VideoView`**: A UI component responsible for rendering the video on the screen. It takes a `VideoPlayer` instance as a prop.
* **`useVideoPlayer` hook**: The recommended way to create and manage the lifecycle of a `VideoPlayer` instance within a functional component. It automatically handles the creation and cleanup of the player.
### New Dependency
`react-native-video` v7 is now built on top of [`react-native-nitro-modules`](https://nitro.margelo.com/docs/what-is-nitro) framework. This means that you need to install the Nitro framework to use `react-native-video` v7.
### Step-by-Step Migration Guide
#### 1. Installation
First, update the `react-native-video` package to the latest v7 release:
```bash
npm install react-native-video@next --save
```
Then, install the pods for iOS:
```bash
cd ios && pod install
```
#### 2. Updating Your Component
The core of the migration involves replacing the `<Video>` component with the new `useVideoPlayer` hook and `<VideoView>` component.
**v6 Implementation:**
```jsx
import React, { useRef } from 'react';
import Video from 'react-native-video';
const VideoPlayerV6 = () => {
const videoRef = useRef(null);
return (
<Video
source={{ uri: 'https://www.w3schools.com/html/mov_bbb.mp4' }}
ref={videoRef}
style={{ width: 300, height: 200 }}
controls={true}
onLoad={() => console.log('Video loaded')}
onProgress={(data) => console.log('Progress:', data.currentTime)}
/>
);
};
```
**v7 Implementation:**
```jsx
import React from 'react';
import { useVideoPlayer, VideoView, useEvent } from 'react-native-video';
const VideoPlayerV7 = () => {
const player = useVideoPlayer({
source: {
uri: 'https://www.w3schools.com/html/mov_bbb.mp4',
},
});
useEvent(player, 'onLoad', () => {
console.log('Video loaded');
});
useEvent(player, 'onProgress', (data) => {
console.log('Progress:', data.currentTime);
});
return (
<VideoView
player={player}
style={{ width: 300, height: 200 }}
controls={true}
/>
);
};
```
### Prop and Method Migration
Many props and methods from the v6 `<Video>` component have been moved to the `VideoPlayer` instance in v7.
#### Common Props
| v6 Prop (`<Video>`) | v7 Equivalent (`VideoPlayer` properties) | Notes |
| :--- | :--- | :--- |
| `source` | `source` property in `useVideoPlayer` config | The structure of the source object remains largely the same. |
| `paused` | `paused` property on the `VideoPlayer` instance | Can be controlled via `player.pause()` and `player.play()`. |
| `muted` | `muted` property on the `VideoPlayer` instance | `player.muted = true/false;` |
| `volume` | `volume` property on the `VideoPlayer` instance | `player.volume = 0.5;` |
| `rate` | `rate` property on the `VideoPlayer` instance | `player.rate = 1.5;` |
| `loop` | `loop` property on the `VideoPlayer` instance | `player.loop = true;` |
| `resizeMode` | `resizeMode` prop on `<VideoView>` | This remains a prop on the UI component. |
| `controls` | `controls` prop on `<VideoView>` | This also remains on the UI component. |
see [VideoPlayer](./player/player.md) for more details.
#### Methods
Imperative methods previously called on the `<Video>` component's ref are now methods on the `VideoPlayer` instance.
| v6 Method (`videoRef.current`) | v7 Equivalent (`player`) |
| :--- | :--- |
| `seek(time)` | `player.seekTo(time)` |
| `presentFullscreenPlayer()` | `videoViewRef.current.enterFullscreen()` | Fullscreen is now managed by the `VideoView` ref. |
| `dismissFullscreenPlayer()` | `videoViewRef.current.exitFullscreen()` | |
| `pause()` | `player.pause()` | |
| `resume()` | `player.play()` | |
see [VideoPlayer](./player/player.md) for more details.
### Event Handling
In v7, event handling is standardized through the `useEvent` hook or by directly assigning callbacks to the `VideoPlayer` instance. The `useEvent` hook is recommended as it automatically handles listener cleanup.
**v6 Event Handling:**
```jsx
<Video
onLoad={(data) => console.log(data)}
onProgress={(data) => console.log(data.currentTime)}
onError={(error) => console.error(error)}
/>
```
**v7 Event Handling with `useEvent`:**
```jsx
import { useVideoPlayer, VideoView, useEvent } from 'react-native-video';
const MyPlayer = () => {
const player = useVideoPlayer({ source: { uri: '...' } });
useEvent(player, 'onLoad', (data) => console.log(data));
useEvent(player, 'onProgress', (data) => console.log(data.currentTime));
useEvent(player, 'onError', (error) => console.error(error.code, error.message));
return <VideoView player={player} />;
}
```
**Directly assigning callbacks in v7:**
```jsx
const player = useVideoPlayer('https://example.com/video.mp4', (_player) => {
_player.onLoad = (data) => {
console.log('Video loaded! Duration:', data.duration);
};
_player.onError = (error) => {
console.error('Player Error:', error.code, error.message);
};
});
```
### Benefits of the New Architecture
* **Preloading**: You can create a `VideoPlayer` instance and begin loading a video before it's visible in the UI. When you're ready to display it, simply pass the player instance to a `<VideoView>`.
* **Improved Performance**: Separating the player logic from the UI rendering can lead to better performance and a more responsive application.
* **Cleaner API**: The hook-based API simplifies player management and reduces boilerplate code, especially for handling the player's lifecycle.
* **Full New Architecture Support**: Version 7 fully embraces React Native's New Architecture, ensuring better performance and consistency.

View File

@@ -0,0 +1,67 @@
---
title: Use in Third Party Library
description: How to use React Native Video in a third party library
sidebar_position: 8
---
# Use in Third Party Library
You can use React Native Video in your third party library either as a dependency if you want to have specific version of the library or as a peer dependency if you want to version selection to be handled by the consumer of the library.
## In JS
Add `react-native-video` as a dependency or peer dependency.
```json title="package.json"
{
"dependencies": {
"react-native-video": "latest"
}
// OR
"peerDependencies": {
"react-native-video": "*"
}
}
```
And then you can import it in your code.
```ts
import { VideoPlayer } from 'react-native-video';
const player = new VideoPlayer({ uri: 'https://www.example.com/video.mp4' });
player.play();
```
## In Native
### iOS
Add `ReactNativeVideo` as a dependency in your `*.podspec` file.
```ruby title="*.podspec"
Pod::Spec.new do |s|
// ...
s.dependency 'ReactNativeVideo'
end
```
### Android
Add `:react-native-video` and `:react-native-nitro-modules` as a dependency in your `build.gradle` file. Also you will need to add `androidx.media3` dependencies. to use player and source in your library.
```groovy title="build.gradle"
// ...
dependencies {
// ...
implementation project(':react-native-video')
implementation project(':react-native-nitro-modules')
implementation "androidx.media3:media3-common:1.4.1"
implementation "androidx.media3:media3-exoplayer:1.4.1"
}
```

130
docs/docs/video-view.md Normal file
View File

@@ -0,0 +1,130 @@
---
sidebar_position: 4
sidebar_label: VideoView
description: React Native Video VideoView Component
---
# VideoView Component
The `VideoView` component is responsible for rendering the video content managed by a `VideoPlayer` instance onto the screen. It also provides UI functionalities like native controls, fullscreen, and picture-in-picture mode.
## Basic Usage
To use `VideoView`, you need to pass a `VideoPlayer` instance to its `player` prop.
```tsx
import React from 'react';
import { VideoPlayer, VideoView } from 'react-native-video';
import { StyleSheet } from 'react-native';
const App = () => {
const player = useVideoPlayer('https://example.com/video.mp4', (_player) => {
// This is optional setup function that will be called when the player is created.
_player.play();
});
return (
<VideoView
style={styles.video}
player={player}
controls={true}
/>
);
};
const styles = StyleSheet.create({
video: {
width: '100%',
height: 200,
},
});
export default App;
```
## Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `player` | `VideoPlayer` | Yes | - | The `VideoPlayer` instance that manages the video to be displayed. |
| `style` | `ViewStyle` | No | - | Standard React Native styles to control the layout and appearance of the `VideoView`. |
| `controls` | `boolean` | No | `false` | Whether to show the native video playback controls (play/pause, seek bar, volume, etc.). |
| `pictureInPicture` | `boolean` | No | `false` | Whether to enable and show the picture-in-picture (PiP) button in the native controls (if supported by the platform and controls are visible). |
| `autoEnterPictureInPicture` | `boolean` | No | `false` | Whether the video should automatically enter PiP mode when it starts playing and the app is backgrounded (behavior might vary by platform). |
| `resizeMode` | `'contain' \| 'cover' \| 'stretch' \| 'none'` | No | `'none'` | How the video should be resized to fit the view. |
| `keepScreenAwake` | `boolean` | No | `true` | Whether to keep the device screen awake while the video view is mounted. |
| `surfaceType` | `'surface' \| 'texture'` | No (Android only) | `'surface'` | (Android) Underlying native view type. `'surface'` uses a SurfaceView (better performance, no transforms/overlap), `'texture'` uses a TextureView (supports animations, transforms, overlapping UI) at a small performance cost. Ignored on iOS. |
## Events
`VideoView` also accepts several event callback props related to UI state changes:
| Event | Type | Description |
|-------|------|-------------|
| `onPictureInPictureChange?` | `(event: { isActive: boolean }) => void` | Fired when the picture-in-picture mode starts or stops. |
| `onFullscreenChange?` | `(event: { isFullscreen: boolean }) => void` | Fired when the fullscreen mode starts or stops. |
| `willEnterFullscreen?` | `() => void` | Fired just before the view enters fullscreen mode. |
| `willExitFullscreen?` | `() => void` | Fired just before the view exits fullscreen mode. |
| `willEnterPictureInPicture?` | `() => void` | Fired just before the view enters picture-in-picture mode. |
| `willExitPictureInPicture?` | `() => void` | Fired just before the view exits picture-in-picture mode. |
These can be used to update your component's state or UI in response to these changes.
```tsx
<VideoView
player={player}
onFullscreenChange={({ isFullscreen }) => {
console.log(isFullscreen ? 'Entered fullscreen' : 'Exited fullscreen');
}}
onPictureInPictureChange={({ isActive }) => {
console.log(isActive ? 'PiP active' : 'PiP inactive');
}}
/>
```
## Refs and Imperative Methods
You can obtain a ref to the `VideoView` component to call imperative methods:
```tsx
const videoViewRef = React.useRef<VideoViewRef>(null);
// ...
<VideoView ref={videoViewRef} player={player} />
// Later, you can call methods like:
videoViewRef.current?.enterFullscreen();
```
Available methods on the `VideoViewRef`:
| Method | Type | Description |
|--------|------|-------------|
| `enterFullscreen()` | `() => void` | Programmatically requests the video view to enter fullscreen mode. |
| `exitFullscreen()` | `() => void` | Programmatically requests the video view to exit fullscreen mode. |
| `enterPictureInPicture()` | `() => void` | Programmatically requests the video view to enter picture-in-picture mode. |
| `exitPictureInPicture()` | `() => void` | Programmatically requests the video view to exit picture-in-picture mode. |
| `canEnterPictureInPicture()` | `() => boolean` | Checks if picture-in-picture mode is currently available and supported. Returns `true` if PiP can be entered, `false` otherwise. |
## Android: Choosing a surface type
On Android the default rendering path uses a `SurfaceView` (set via `surfaceType="surface"`) for optimal decoding performance and lower latency. However `SurfaceView` lives in a separate window and can't be:
- Animated with transforms (scale, rotate, opacity fade)
- Clipped by parent views (rounded corners, masks)
- Overlapped reliably with sibling views (z-order issues)
If you need those UI effects, switch to `TextureView`:
```tsx
<VideoView
player={player}
surfaceType="texture"
style={{ width: 300, height: 170, borderRadius: 16, overflow: 'hidden' }}
resizeMode="cover"
controls
/>
```
Use `TextureView` only when required, as it can be slightly less performant and may increase power consumption on some devices.

237
docs/docusaurus.config.ts Normal file
View File

@@ -0,0 +1,237 @@
import type * as Preset from '@docusaurus/preset-classic';
import type { Config } from '@docusaurus/types';
import { themes as prismThemes } from 'prism-react-renderer';
// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
const config: Config = {
title: 'React Native Video',
tagline: 'React Native Video - Video player for React Native',
favicon: 'img/favicon.ico',
url: 'https://docs.thewidlarzgroup.com',
baseUrl: '/react-native-video',
organizationName: 'TheWidlarzGroup',
projectName: 'react-native-video',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
future: {
experimental_faster: true,
v4: true
},
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
lastVersion: '6.x',
includeCurrentVersion: true,
versions: {
current: {
label: 'v7 Alpha',
path: 'v7',
banner: 'none',
},
'6.x': {
label: 'v6',
path: 'v6',
},
},
},
theme: {
customCss: './src/css/custom.css',
},
} satisfies Preset.Options,
],
],
headTags: [
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://fonts.googleapis.com',
},
},
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://fonts.gstatic.com',
},
},
{
tagName: 'link',
attributes: {
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap',
},
},
],
themeConfig: {
image: 'img/twg-social-card.png',
navbar: {
title: 'React Native Video',
logo: {
alt: 'React Native Video Logo',
style: {
width: '60px',
height: '40px',
marginRight: '10px',
transform: 'translateY(-4px)',
},
src: 'img/twg-logo.png',
},
items: [
{
type: 'docSidebar',
sidebarId: 'docsSidebar',
position: 'left',
label: 'Documentation',
},
{
type: 'docsVersionDropdown',
versions: {
current: {label: 'v7 Alpha'},
'6.x': {label: 'v6'},
},
position: 'right',
},
{
href: 'https://github.com/TheWidlarzGroup/react-native-video',
label: 'GitHub',
position: 'right',
},
{
href: 'https://www.thewidlarzgroup.com/react-native-video?utm_source=rnv&utm_medium=docs&utm_campaign=navbar&utm_id=offer-button',
label: 'Offer',
position: 'right',
},
{
type: 'search',
position: 'right',
},
],
},
docs: {
sidebar: {
hideable: false,
},
versionPersistence: 'localStorage',
},
footer: {
style: 'light',
copyright: `Built With ❤️ By TheWidlarzGroup & React Native Video Community`,
},
colorMode: {
defaultMode: 'dark',
disableSwitch: true,
respectPrefersColorScheme: false,
},
prism: {
theme: prismThemes.oneLight,
darkTheme: prismThemes.oneDark,
},
} satisfies Preset.ThemeConfig,
plugins: [
require.resolve('docusaurus-lunr-search'),
[
'docusaurus-plugin-typedoc',
{
name: 'API Reference',
entryPoints: ['../packages/react-native-video/src'],
exclude: "../packages/react-native-video/src/index.ts",
tsconfig: '../packages/react-native-video/tsconfig.json',
out: './docs/api-reference',
watch: process.env.TYPEDOC_WATCH,
excludePrivate: true,
excludeProtected: true,
excludeExternals: true,
excludeInternal: true,
readme: "none",
sidebar: {
autoConfiguration: false,
},
parametersFormat: "table",
enumMembersFormat: "table",
useCodeBlocks: true,
},
],
// LLMs txt generation for v6
[
'docusaurus-plugin-llms',
{
id: 'llms-v6',
generateLLMsTxt: false,
generateLLMsFullTxt: false,
docsDir: "versioned_docs/version-6.x",
pathTransformation: {
ignorePaths: ['docs'],
addPaths: ['react-native-video/docs/v6']
},
version: '6.x.x',
customLLMFiles: [
{
filename: 'llms-v6.txt',
title: 'React Native Video v6 Documentation',
description: 'Complete documentation for React Native Video v6',
includePatterns: ['**/*.md', '**/*.mdx'],
fullContent: false,
},
{
filename: 'llms-v6-full.txt',
title: 'React Native Video v6 Documentation',
description: 'Complete documentation for React Native Video v6',
includePatterns: ['**/*.md', '**/*.mdx'],
fullContent: true,
},
]
}
],
// LLMs txt generation for v7
[
'docusaurus-plugin-llms',
{
id: 'llms-v7',
generateLLMsTxt: false,
generateLLMsFullTxt: false,
docsDir: "docs",
pathTransformation: {
ignorePaths: ['docs'],
addPaths: ['react-native-video/docs/v7']
},
version: '7.x.x',
customLLMFiles: [
{
filename: 'llms-v7.txt',
title: 'React Native Video v7 Documentation',
description: 'Complete documentation for React Native Video v7',
includePatterns: ['docs/**/*.md'],
fullContent: false,
},
{
filename: 'llms-v7-full.txt',
title: 'React Native Video v7 Documentation',
description: 'Complete documentation for React Native Video v7',
includePatterns: ['docs/**/*.md'],
fullContent: true,
}
]
}
]
],
};
export default config;

5
docs/next-env.d.ts vendored
View File

@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,28 +0,0 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const withNextra = require('nextra')({
theme: 'nextra-theme-docs',
themeConfig: './theme.config.jsx',
});
let assetPrefix = '';
let basePath = '';
// If we're in a GitHub Action, we need to set the assetPrefix and basePath
// to add repo_name to the path.
// eg. https://<organization>.github.io/<repo_name>
if (process.env.GITHUB_ACTIONS) {
const repo_name = 'react-native-video';
assetPrefix = `/${repo_name}/`;
basePath = `/${repo_name}`;
}
module.exports = withNextra({
output: 'export',
images: {
unoptimized: true,
},
assetPrefix,
basePath,
});

View File

@@ -1,21 +1,51 @@
{
"name": "react-native-video-docs",
"version": "0.0.1",
"name": "@react-native-video/docs",
"version": "0.0.0",
"private": true,
"description": "Documentation for react-native-video",
"scripts": {
"dev": "bun next dev",
"build": "bun next build"
"docusaurus": "docusaurus",
"start": "TYPEDOC_WATCH=true docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"next": "^13.5.4",
"nextra": "^2.13.2",
"nextra-theme-docs": "^2.13.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.4.2"
"@docusaurus/core": "^3.8.1",
"@docusaurus/faster": "^3.8.1",
"@docusaurus/preset-classic": "^3.8.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"docusaurus-lunr-search": "^3.6.0",
"prism-react-renderer": "^2.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"bun-types": "latest"
"@docusaurus/module-type-aliases": "^3.8.1",
"@docusaurus/tsconfig": "^3.8.1",
"@docusaurus/types": "^3.8.1",
"typescript": "^5.2.2",
"docusaurus-plugin-typedoc": "^1.4.0",
"docusaurus-plugin-llms": "^0.1.5"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">=18.0"
}
}

View File

@@ -1,21 +0,0 @@
{
"index": "Introduction",
"installation": "Installation",
"component": "API",
"other": "Other",
"separator_versions": {
"type": "separator",
"title": ""
},
"updating": "Updating",
"changelog": {
"title": "Changelog",
"newWindow": true,
"href": "https://github.com/react-native-video/react-native-video/blob/master/CHANGELOG.md"
},
"separator_community": {
"type": "separator",
"title": ""
},
"projects": "Useful projects"
}

View File

@@ -1,7 +0,0 @@
{
"props": "Properties",
"drm": "DRM",
"ads": "Ads",
"events": "Events",
"methods": "Methods"
}

Some files were not shown because too many files have changed in this diff Show More