├── .gitignore
├── README.md
├── demo
├── .jshintrc
├── app
│ ├── App_Resources
│ │ ├── Android
│ │ │ ├── app.gradle
│ │ │ └── src
│ │ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── res
│ │ │ │ ├── drawable-hdpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── drawable-ldpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── drawable-nodpi
│ │ │ │ └── splash_screen.xml
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── background.png
│ │ │ │ ├── icon.png
│ │ │ │ └── logo.png
│ │ │ │ ├── values-v21
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ │ ├── values-v29
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ └── iOS
│ │ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── icon-29.png
│ │ │ │ ├── icon-29@2x.png
│ │ │ │ ├── icon-29@3x.png
│ │ │ │ ├── icon-40.png
│ │ │ │ ├── icon-40@2x.png
│ │ │ │ ├── icon-40@3x.png
│ │ │ │ ├── icon-50.png
│ │ │ │ ├── icon-50@2x.png
│ │ │ │ ├── icon-57.png
│ │ │ │ ├── icon-57@2x.png
│ │ │ │ ├── icon-60@2x.png
│ │ │ │ ├── icon-60@3x.png
│ │ │ │ ├── icon-72.png
│ │ │ │ ├── icon-72@2x.png
│ │ │ │ ├── icon-76.png
│ │ │ │ ├── icon-76@2x.png
│ │ │ │ └── icon-83.5@2x.png
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.launchimage
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Default-568h@2x.png
│ │ │ │ ├── Default-667h@2x.png
│ │ │ │ ├── Default-736h@3x.png
│ │ │ │ ├── Default-Landscape.png
│ │ │ │ ├── Default-Landscape@2x.png
│ │ │ │ ├── Default-Landscape@3x.png
│ │ │ │ ├── Default-Portrait.png
│ │ │ │ ├── Default-Portrait@2x.png
│ │ │ │ ├── Default.png
│ │ │ │ └── Default@2x.png
│ │ │ ├── LaunchScreen.AspectFill.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchScreen-AspectFill.png
│ │ │ │ └── LaunchScreen-AspectFill@2x.png
│ │ │ └── LaunchScreen.Center.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchScreen-Center.png
│ │ │ │ └── LaunchScreen-Center@2x.png
│ │ │ ├── Info.plist
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── build.xcconfig
│ ├── app-root.xml
│ ├── app.css
│ ├── app.ts
│ ├── main-page.ts
│ ├── main-page.xml
│ ├── main-view-model.ts
│ ├── package.json
│ └── videos
│ │ ├── big_buck_bunny.mp4
│ │ ├── sample-ru.srt
│ │ ├── sample.srt
│ │ ├── small.mp4
│ │ └── video-ctr.enc
├── package.json
├── tsconfig.json
└── tsconfig.tns.json
├── screens
├── video.gif
└── videoplayer.gif
└── src
├── .npmignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── platforms
├── android
│ ├── include.gradle
│ └── java
│ │ ├── EncryptedFileDataSource.java
│ │ └── EncryptedFileDataSourceFactory.java
└── ios
│ └── Podfile
├── subtitle-source
├── package.json
├── subtitle-source-common.ts
├── subtitle-source.android.ts
├── subtitle-source.d.ts
└── subtitle-source.ios.ts
├── tsconfig.json
├── video-source
├── package.json
├── video-source-common.ts
├── video-source.android.ts
├── video-source.d.ts
└── video-source.ios.ts
├── videoplayer-common.ts
├── videoplayer.android.ts
├── videoplayer.d.ts
└── videoplayer.ios.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | *.js.map
3 | *.log
4 | *.d.ts
5 | !videoplayer.d.ts
6 | demo/lib
7 | demo/app/*.js
8 | demo/*.d.ts
9 | demo/platforms
10 | demo/node_modules
11 | demo/.vscode
12 | node_modules
13 | .vscode/
14 | *.DS_Store
15 |
16 | .idea/
17 | .vscode/
18 | .tscache/
19 | /demo/node_modules/
20 | /demo/platforms/
21 | node_modules/
22 | .settings/
23 |
24 | .DS_Store
25 | *js
26 | *.js.map
27 | *.tar
28 | *.tgz
29 | *.gz
30 | *.zip
31 | *.stackdump
32 | src/package*/
33 |
34 | package-lock.json
35 |
36 | !src/videoplayer.d.ts
37 | !src/subtitle-source/subtitle-source.d.ts
38 | !src/video-source/video-source.d.ts
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Archived
2 | See: https://fluentreports.com/blog/?p=1434
3 |
4 | # NativeScript-ExoPlayer
5 |
6 | The official NativeScript Exoplayer plugin
7 |
8 |
9 | ## Developed by
10 | [](https://plugins.nativescript.rocks/mastertech-nstudio)
11 |
12 |
13 |
14 | ## Documentation
15 | The [documentation](src/README.md) for the plugin is located in the [src folder](src).
16 |
--------------------------------------------------------------------------------
/demo/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "undef": true,
3 | "nonew": true,
4 | "curly": false,
5 | "noarg": true,
6 | "forin": true,
7 | "noempty": false,
8 | "eqeqeq": false,
9 | "strict": false,
10 | "bitwise": true,
11 | "newcap": false,
12 | "camelcase": false,
13 | "browser": true,
14 | "node": true,
15 | "devel": true,
16 | "shadow": true,
17 | "eqnull": true,
18 | "mocha": true,
19 | "jasmine": true,
20 | "qunit": true,
21 | "esnext": true,
22 | "predef": [ "android", "__extends", "java", "javax", "Promise" ]
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/app.gradle:
--------------------------------------------------------------------------------
1 | // Add your native dependencies here:
2 |
3 | // Uncomment to add recyclerview-v7 dependency
4 | //dependencies {
5 | // implementation 'com.android.support:recyclerview-v7:+'
6 | //}
7 |
8 | // If you want to add something to be applied before applying plugins' include.gradle files
9 | // e.g. project.ext.googlePlayServicesVersion = "15.0.1"
10 | // create a file named before-plugins.gradle in the current directory and place it there
11 |
12 | android {
13 | defaultConfig {
14 | minSdkVersion 17
15 | generatedDensities = []
16 | }
17 | aaptOptions {
18 | additionalParameters "--no-version-vectors"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-hdpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-ldpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-mdpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-nodpi/splash_screen.xml:
--------------------------------------------------------------------------------
1 |
2 | -
3 |
4 |
5 | -
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xhdpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxhdpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/background.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/icon.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/Android/src/main/res/drawable-xxxhdpi/logo.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/values-v21/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3d5afe
4 | #0000ff
5 | #ff0000
6 |
7 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
13 |
14 |
15 |
18 |
19 |
20 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/values-v29/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F5F5F5
4 | #757575
5 | #33B5E5
6 | #272734
7 | #0000ff
8 | #ff0000
9 |
10 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
21 |
22 |
23 |
31 |
32 |
34 |
35 |
36 |
42 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
61 |
62 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "icon-29.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "icon-29@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon-29@3x.png",
19 | "scale" : "3x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "icon-40@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon-40@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "57x57",
35 | "idiom" : "iphone",
36 | "filename" : "icon-57.png",
37 | "scale" : "1x"
38 | },
39 | {
40 | "size" : "57x57",
41 | "idiom" : "iphone",
42 | "filename" : "icon-57@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "icon-60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "icon-60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "29x29",
59 | "idiom" : "ipad",
60 | "filename" : "icon-29.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "icon-29@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "40x40",
71 | "idiom" : "ipad",
72 | "filename" : "icon-40.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "icon-40@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "50x50",
83 | "idiom" : "ipad",
84 | "filename" : "icon-50.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "50x50",
89 | "idiom" : "ipad",
90 | "filename" : "icon-50@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "72x72",
95 | "idiom" : "ipad",
96 | "filename" : "icon-72.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "72x72",
101 | "idiom" : "ipad",
102 | "filename" : "icon-72@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "76x76",
107 | "idiom" : "ipad",
108 | "filename" : "icon-76.png",
109 | "scale" : "1x"
110 | },
111 | {
112 | "size" : "76x76",
113 | "idiom" : "ipad",
114 | "filename" : "icon-76@2x.png",
115 | "scale" : "2x"
116 | },
117 | {
118 | "size" : "83.5x83.5",
119 | "idiom" : "ipad",
120 | "filename" : "icon-83.5@2x.png",
121 | "scale" : "2x"
122 | }
123 | ],
124 | "info" : {
125 | "version" : 1,
126 | "author" : "xcode"
127 | }
128 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-50@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-57@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "736h",
7 | "filename" : "Default-736h@3x.png",
8 | "minimum-system-version" : "8.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "extent" : "full-screen",
14 | "idiom" : "iphone",
15 | "subtype" : "736h",
16 | "filename" : "Default-Landscape@3x.png",
17 | "minimum-system-version" : "8.0",
18 | "orientation" : "landscape",
19 | "scale" : "3x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "667h",
25 | "filename" : "Default-667h@2x.png",
26 | "minimum-system-version" : "8.0",
27 | "orientation" : "portrait",
28 | "scale" : "2x"
29 | },
30 | {
31 | "orientation" : "portrait",
32 | "idiom" : "iphone",
33 | "filename" : "Default@2x.png",
34 | "extent" : "full-screen",
35 | "minimum-system-version" : "7.0",
36 | "scale" : "2x"
37 | },
38 | {
39 | "extent" : "full-screen",
40 | "idiom" : "iphone",
41 | "subtype" : "retina4",
42 | "filename" : "Default-568h@2x.png",
43 | "minimum-system-version" : "7.0",
44 | "orientation" : "portrait",
45 | "scale" : "2x"
46 | },
47 | {
48 | "orientation" : "portrait",
49 | "idiom" : "ipad",
50 | "filename" : "Default-Portrait.png",
51 | "extent" : "full-screen",
52 | "minimum-system-version" : "7.0",
53 | "scale" : "1x"
54 | },
55 | {
56 | "orientation" : "landscape",
57 | "idiom" : "ipad",
58 | "filename" : "Default-Landscape.png",
59 | "extent" : "full-screen",
60 | "minimum-system-version" : "7.0",
61 | "scale" : "1x"
62 | },
63 | {
64 | "orientation" : "portrait",
65 | "idiom" : "ipad",
66 | "filename" : "Default-Portrait@2x.png",
67 | "extent" : "full-screen",
68 | "minimum-system-version" : "7.0",
69 | "scale" : "2x"
70 | },
71 | {
72 | "orientation" : "landscape",
73 | "idiom" : "ipad",
74 | "filename" : "Default-Landscape@2x.png",
75 | "extent" : "full-screen",
76 | "minimum-system-version" : "7.0",
77 | "scale" : "2x"
78 | },
79 | {
80 | "orientation" : "portrait",
81 | "idiom" : "iphone",
82 | "filename" : "Default.png",
83 | "extent" : "full-screen",
84 | "scale" : "1x"
85 | },
86 | {
87 | "orientation" : "portrait",
88 | "idiom" : "iphone",
89 | "filename" : "Default@2x.png",
90 | "extent" : "full-screen",
91 | "scale" : "2x"
92 | },
93 | {
94 | "orientation" : "portrait",
95 | "idiom" : "iphone",
96 | "filename" : "Default-568h@2x.png",
97 | "extent" : "full-screen",
98 | "subtype" : "retina4",
99 | "scale" : "2x"
100 | },
101 | {
102 | "orientation" : "portrait",
103 | "idiom" : "ipad",
104 | "extent" : "to-status-bar",
105 | "scale" : "1x"
106 | },
107 | {
108 | "orientation" : "portrait",
109 | "idiom" : "ipad",
110 | "filename" : "Default-Portrait.png",
111 | "extent" : "full-screen",
112 | "scale" : "1x"
113 | },
114 | {
115 | "orientation" : "landscape",
116 | "idiom" : "ipad",
117 | "extent" : "to-status-bar",
118 | "scale" : "1x"
119 | },
120 | {
121 | "orientation" : "landscape",
122 | "idiom" : "ipad",
123 | "filename" : "Default-Landscape.png",
124 | "extent" : "full-screen",
125 | "scale" : "1x"
126 | },
127 | {
128 | "orientation" : "portrait",
129 | "idiom" : "ipad",
130 | "extent" : "to-status-bar",
131 | "scale" : "2x"
132 | },
133 | {
134 | "orientation" : "portrait",
135 | "idiom" : "ipad",
136 | "filename" : "Default-Portrait@2x.png",
137 | "extent" : "full-screen",
138 | "scale" : "2x"
139 | },
140 | {
141 | "orientation" : "landscape",
142 | "idiom" : "ipad",
143 | "extent" : "to-status-bar",
144 | "scale" : "2x"
145 | },
146 | {
147 | "orientation" : "landscape",
148 | "idiom" : "ipad",
149 | "filename" : "Default-Landscape@2x.png",
150 | "extent" : "full-screen",
151 | "scale" : "2x"
152 | }
153 | ],
154 | "info" : {
155 | "version" : 1,
156 | "author" : "xcode"
157 | }
158 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-568h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-667h@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-736h@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Landscape@3x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Default@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchScreen-AspectFill.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchScreen-AspectFill@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.AspectFill.imageset/LaunchScreen-AspectFill@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchScreen-Center.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchScreen-Center@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/App_Resources/iOS/Assets.xcassets/LaunchScreen.Center.imageset/LaunchScreen-Center@2x.png
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiresFullScreen
28 |
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/build.xcconfig:
--------------------------------------------------------------------------------
1 | // You can add custom settings here
2 | // for example you can uncomment the following line to force distribution code signing
3 | // CODE_SIGN_IDENTITY = iPhone Distribution
4 | // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
5 | // DEVELOPMENT_TEAM = YOUR_TEAM_ID;
6 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
7 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
8 |
--------------------------------------------------------------------------------
/demo/app/app-root.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/demo/app/app.css:
--------------------------------------------------------------------------------
1 | /*
2 | In NativeScript, the app.css file is where you place CSS rules that
3 | you would like to apply to your entire application. Check out
4 | http://docs.nativescript.org/ui/styling for a full list of the CSS
5 | selectors and properties you can use to style UI components.
6 |
7 | /*
8 | In many cases you may want to use the NativeScript core theme instead
9 | of writing your own CSS rules. You can learn more about the
10 | NativeScript core theme at https://github.com/nativescript/theme
11 | The imported CSS rules must precede all other types of rules.
12 | */
13 | @import "~@nativescript/theme/css/core.css";
14 | @import "~@nativescript/theme/css/default.css";
15 |
16 | /* Place any CSS rules you want to apply on both iOS and Android here.
17 | This is where the vast majority of your CSS code goes. */
18 |
19 | /*
20 | The following CSS rule changes the font size of all Buttons that have the
21 | "-primary" class modifier.
22 | */
23 | Button.-primary {
24 | font-size: 18;
25 | }
26 |
--------------------------------------------------------------------------------
/demo/app/app.ts:
--------------------------------------------------------------------------------
1 | import * as application from 'application';
2 | application.run({ moduleName: 'main-page' });
3 |
--------------------------------------------------------------------------------
/demo/app/main-page.ts:
--------------------------------------------------------------------------------
1 | import { Page } from 'ui/page';
2 | import { EventData } from 'data/observable';
3 | import { HelloWorldModel } from './main-view-model';
4 | import { isAndroid, device } from "platform";
5 | import { Color } from "color";
6 | import { android } from "application";
7 |
8 | // Event handler for Page "loaded" event attached in main-page.xml
9 | export function pageLoaded(args: EventData) {
10 | // Get the event sender
11 | let page = args.object;
12 | page.bindingContext = new HelloWorldModel(page);
13 |
14 | if (isAndroid && device.sdkVersion >= "21") {
15 | let window = android.startActivity.getWindow();
16 | window.setStatusBarColor(new Color("#d32f2f").android);
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/demo/app/main-page.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/demo/app/main-view-model.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from "tns-core-modules/data/observable";
2 | import { Page } from "tns-core-modules/ui/page";
3 | import { isAndroid } from "tns-core-modules/platform";
4 | import { setInterval } from "tns-core-modules/timer";
5 | import { VideoFill } from "nativescript-exoplayer";
6 |
7 | export class HelloWorldModel extends Observable {
8 | public videoSrc: string;
9 | public subtitlesSrc: string;
10 | public currentTime: any;
11 | public videoDuration: any;
12 | public videoFill: VideoFill = VideoFill.default;
13 | private _videoPlayer: any;
14 | private completed: boolean;
15 |
16 | constructor(mainpage: Page) {
17 | super();
18 |
19 | this.completed = false;
20 | this._videoPlayer = mainpage.getViewById("nativeVideoPlayer");
21 | this.currentTime = "";
22 | this.videoDuration = "";
23 | this.videoSrc = "~/videos/big_buck_bunny.mp4";
24 | this.subtitlesSrc = "~/videos/sample.srt";
25 | this.trackVideoCurrentPosition();
26 | }
27 |
28 | public setEnglishSubtitles() {
29 | this._videoPlayer.subtitles = "~/videos/sample.srt";
30 | }
31 |
32 | public setRussianSubtitles() {
33 | this._videoPlayer.subtitles = "~/videos/sample-ru.srt";
34 | }
35 |
36 | public disableSubtitles() {
37 | this._videoPlayer.subtitles = "";
38 | }
39 |
40 | /**
41 | * Video Finished callback
42 | */
43 | public videoFinished(args) {
44 | this.completed = true;
45 | }
46 |
47 | public playbackStart(args) {
48 | this.completed = false;
49 | }
50 |
51 | /**
52 | * Pause the video
53 | */
54 | public pauseVideo() {
55 | this._videoPlayer.pause();
56 | }
57 |
58 |
59 | /**
60 | * Play the video
61 | */
62 | public playVideo() {
63 | this._videoPlayer.play();
64 | this.completed = false;
65 | }
66 |
67 |
68 | /**
69 | * Stop the video player
70 | */
71 | public stopVideo() {
72 | if (isAndroid) {
73 | this._videoPlayer.stop();
74 | }
75 | }
76 |
77 |
78 | /**
79 | * Get the video duration
80 | */
81 | public getVideoDuration() {
82 | let videoDuration = this._videoPlayer.getDuration();
83 | console.log("Video Duration: " + videoDuration);
84 | this.set("videoDuration", videoDuration);
85 | }
86 |
87 |
88 | /**
89 | * Go to 30 seconds
90 | */
91 | public goToTime() {
92 | try {
93 | this._videoPlayer.seekToTime(30000);
94 | } catch (err) {
95 | console.log(err);
96 | }
97 | }
98 |
99 |
100 | public animate() {
101 | console.log("Animation");
102 |
103 | const enums = require("ui/enums");
104 | this._videoPlayer.animate({
105 | rotate: 360,
106 | duration: 3000,
107 | curve: enums.AnimationCurve.spring
108 | }).then(() => {
109 | return this._videoPlayer.animate({
110 | rotate: 0,
111 | duration: 3000,
112 | curve: enums.AnimationCurve.spring
113 | });
114 | }).then(() => {
115 | return this._videoPlayer.animate({
116 | scale: { x: .5, y: .5 },
117 | duration: 1000,
118 | curve: enums.AnimationCurve.spring
119 | });
120 |
121 | }).then(() => {
122 | return this._videoPlayer.animate({
123 | scale: { x: 1.5, y: 1.5 },
124 | duration: 3000,
125 | curve: enums.AnimationCurve.spring
126 | });
127 | }).then(() => {
128 | return this._videoPlayer.animate({
129 | scale: { x: 1.0, y: 1.0 },
130 | duration: 3000,
131 | curve: enums.AnimationCurve.spring
132 | });
133 |
134 | });
135 |
136 | }
137 |
138 | public muteVideo() {
139 | this._videoPlayer.mute(true);
140 | }
141 |
142 | public unmuteVideo() {
143 | this._videoPlayer.mute(false);
144 | }
145 |
146 |
147 | /**
148 | * Get the video current time
149 | */
150 | public getVideoCurrentTime() {
151 | try {
152 | let currentTime = this._videoPlayer.getCurrentTime();
153 | console.log("Current Time: " + currentTime);
154 | } catch (err) {
155 | console.log(err);
156 | }
157 | }
158 |
159 |
160 | public encVideoSource() {
161 | this._videoPlayer.encryption = "CTR";
162 | this._videoPlayer.encryptionKey = "2BB80D537B1DA3E38BD30361AA855686BDE0EACD7162FEF6A25FE97BF527A25B";
163 | this._videoPlayer.encryptionIV = "015E42FF678B2B90B743111A396EF850";
164 | this._videoPlayer.src = "~/videos/video-ctr.enc";
165 | }
166 |
167 | /**
168 | * Change the video src property
169 | */
170 | public changeVideoSource() {
171 | // Disable encryption if it is on...
172 | this._videoPlayer.encryption = "";
173 |
174 | if (this.videoSrc === "~/videos/small.mp4" || this.videoSrc === "~/videos/video-ctr.enc") {
175 | this._videoPlayer.src = "https://dash.akamaized.net/dash264/TestCases/1a/netflix/exMPD_BIP_TC1.mpd";
176 | } else {
177 | this._videoPlayer.src = "~/videos/small.mp4";
178 | }
179 | }
180 |
181 |
182 |
183 | private trackVideoCurrentPosition(): number {
184 | let trackInterval = setInterval(() => {
185 | let x, y;
186 | if (this.completed) {
187 | x = "";
188 | y = "";
189 | } else {
190 | x = this._videoPlayer.getCurrentTime();
191 | y = this._videoPlayer.getDuration();
192 | }
193 | this.set("currentTime", x);
194 | this.set("videoDuration", y);
195 | }, 200);
196 | return trackInterval;
197 |
198 | }
199 |
200 |
201 | }
--------------------------------------------------------------------------------
/demo/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tns-template-hello-world",
3 | "main": "app.js",
4 | "version": "1.7.0",
5 | "author": {
6 | "name": "Telerik",
7 | "email": "support@telerik.com"
8 | },
9 | "description": "Nativescript hello-world project template",
10 | "license": "Apache-2.0",
11 | "keywords": [
12 | "telerik",
13 | "mobile",
14 | "nativescript",
15 | "{N}",
16 | "tns",
17 | "appbuilder",
18 | "template"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "git://github.com/NativeScript/template-hello-world.git"
23 | },
24 | "bugs": {
25 | "url": "https://github.com/NativeScript/template-hello-world/issues"
26 | },
27 | "homepage": "https://github.com/NativeScript/template-hello-world",
28 | "android": {
29 | "v8Flags": "--expose_gc",
30 | "markingMode": "none"
31 | },
32 | "readme": "ERROR: No README data found!",
33 | "_id": "tns-template-hello-world@1.7.0",
34 | "_from": "tns-template-hello-world@1.7.0"
35 | }
36 |
--------------------------------------------------------------------------------
/demo/app/videos/big_buck_bunny.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/videos/big_buck_bunny.mp4
--------------------------------------------------------------------------------
/demo/app/videos/sample-ru.srt:
--------------------------------------------------------------------------------
1 | 1
2 | 00:00:00,000 --> 00:00:02,500
3 | Привет из субтитров!
4 |
5 | 2
6 | 00:00:03,000 --> 00:00:05,000
7 | Все огонь работает, поддерживает курсив.
8 |
9 | 3
10 | 00:00:05,000 --> 00:00:08,000
11 | И жирный шрифт,
12 | в несколько строк
13 |
14 | 4
15 | 00:00:08,000 --> 00:00:15,000
16 | Теперь опять обычный текст
17 | в несколько строк
18 |
--------------------------------------------------------------------------------
/demo/app/videos/sample.srt:
--------------------------------------------------------------------------------
1 | 1
2 | 00:00:00,000 --> 00:00:02,500
3 | Hello from subtitle!
4 |
5 | 2
6 | 00:00:03,000 --> 00:00:05,000
7 | This super plugin support italic.
8 |
9 | 3
10 | 00:00:05,000 --> 00:00:08,000
11 | And bold,
12 | with multiline
13 |
14 | 4
15 | 00:00:08,000 --> 00:00:15,000
16 | And regular text again,
17 | now multiline
--------------------------------------------------------------------------------
/demo/app/videos/small.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/videos/small.mp4
--------------------------------------------------------------------------------
/demo/app/videos/video-ctr.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/demo/app/videos/video-ctr.enc
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "nativescript": {
3 | "id": "org.nativescript.demo",
4 | "tns-android": {
5 | "version": "6.5.0"
6 | },
7 | "tns-ios": {
8 | "version": "6.5.0"
9 | }
10 | },
11 | "dependencies": {
12 | "nativescript-exoplayer": "file:../src",
13 | "@nativescript/theme": "~2.3.0",
14 | "tns-core-modules": "~6.5.0"
15 | },
16 | "devDependencies": {
17 | "babel-traverse": "6.4.5",
18 | "babel-types": "6.4.5",
19 | "babylon": "6.4.5",
20 | "nativescript-dev-webpack": "latest",
21 | "lazy": "1.0.11",
22 | "typescript": "~3.8.3",
23 | "tns-platform-declarations": "6.0.1"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es5",
5 | "experimentalDecorators": true,
6 | "emitDecoratorMetadata": true,
7 | "noEmitHelpers": true,
8 | "noEmitOnError": true,
9 | "allowJs": false,
10 | "lib": [
11 | "es6",
12 | "dom"
13 | ],
14 | "baseUrl": ".",
15 | "paths": {
16 | "*": [
17 | "./node_modules/tns-core-modules/*",
18 | "./node_modules/*"
19 | ],
20 | "~/*": [
21 | "app/*"
22 | ]
23 | }
24 | },
25 | "exclude": [
26 | "node_modules",
27 | "platforms",
28 | "**/*.aot.ts"
29 | ]
30 | }
--------------------------------------------------------------------------------
/demo/tsconfig.tns.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "compilerOptions": {
4 | "module": "esNext",
5 | "moduleResolution": "node"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/screens/video.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/screens/video.gif
--------------------------------------------------------------------------------
/screens/videoplayer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NathanaelA/nativescript-exoplayer/1d72b4405bcbeecc70572d4cf8b55823220d5998/screens/videoplayer.gif
--------------------------------------------------------------------------------
/src/.npmignore:
--------------------------------------------------------------------------------
1 | demo/
2 | screens/
3 | app/
4 | .vscode/
5 | .idea/
6 | *.png
7 | *.log
8 | *.ts
9 | !index.d.ts
10 | !videoplayer.d.ts
11 | !subtitle-source/subtitle-source.d.ts
12 | !video-source/video-source.d.ts
13 | tsconfig.json
14 | README.md
15 | *.tgz
16 | *.zip
17 | *.tar
18 | .npmignore
19 | .idea
20 | *.map
21 | *.stackdump
22 | package*/
23 | package-lock.json
24 | platforms/android/*.aar
25 |
--------------------------------------------------------------------------------
/src/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2019 Nathanael Anderson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/nativescript-exoplayer)
2 | [](https://www.npmjs.com/package/nativescript-exoplayer)
3 | [](https://www.npmjs.com/package/nativescript-exoplayer)
4 | [](https://twitter.com/congocart)
5 |
6 | # NativeScript ExoPlayer
7 | A NativeScript plugin to provide the ability to play local and remote videos using Google's ExoPlayer.
8 |
9 | ## Developed by
10 | [](https://plugins.nativescript.rocks/mastertech-nstudio)
11 |
12 |
13 | ## Platform controls used:
14 | Android | iOS
15 | ---------- | -----------
16 | [Google ExoPlayer](https://github.com/google/ExoPlayer) | [iOS AVPlayer](https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html)
17 | For a 100% NativeScript plugin use the [NativeScript-VideoPlayer](https://github.com/bradmartin/nativescript-videoplayer).
18 |
19 |
20 | ## Based on
21 | This is based on the awesome [NativeScript-VideoPlayer](https://github.com/bradmartin/nativescript-videoplayer) by Brad Martin (nStudio, llc); the Android side was re-written to use Google's enhanced ExoPlayer. The iOS side is the same thing as what was in the original NativeScript-VideoPlayer.
22 |
23 | Since there is a lot of cases where you might still want a 100% NativeScript plugin, Brad and I decided to make this a separate plugin so that you can use the original NativeScript-VideoPlayer for those cases where you want a pure JavaScript plugin.
24 |
25 | The Google ExoPlayer adds about a meg and a half plugin to the Android application.
26 |
27 |
28 | ## Sample Usage
29 |
30 | Sample 1 | Sample 2
31 | -------------------------------------| -------------------------------------
32 |  | 
33 |
34 |
35 | ## Installation
36 | From your command prompt/terminal go to your app's root folder and execute:
37 |
38 | `tns plugin add nativescript-exoplayer`
39 |
40 | ## Usage
41 |
42 | ###
43 | ```XML
44 |
46 |
47 |
48 |
52 |
53 |
54 |
55 |
56 |
57 | ```
58 |
59 | ## Angular Native (NativeScript Angular) Usage
60 | ``` TS
61 | // somewhere at top of your component or bootstrap file
62 | import {registerElement} from "nativescript-angular/element-registry";
63 | registerElement("exoplayer", () => require("nativescript-exoplayer").Video);
64 | // documentation: https://docs.nativescript.org/angular/plugins/angular-third-party.html#simple-elements
65 | ```
66 | *With AngularNative you have to explicitly close all components so the correct template code is below.*
67 | ``` XML
68 |
72 | ```
73 |
74 | ## Properties
75 | - **src** - *required*
76 |
77 | Set the video file to play, for best performance use local video files if possible. The file must adhere to the platforms accepted video formats. For reference check the platform specs on playing videos.
78 |
79 | - **srcType** - (Android Only)
80 |
81 | * 0 = DETECT (from src)
82 | * 1 = SS
83 | * 2 = DASH
84 | * 3 = HLS
85 | * 4 = OTHER
86 |
87 | - **enableSubtitles**
88 |
89 | By default, subtitle support is off. Use this flag to turn them on.
90 |
91 | - **subtitles**
92 |
93 | Set `.srt` file with subtitles for given video. This can be local file or internet url. Currently only `.srt` format is supported.
94 |
95 |
96 | - **autoplay - (boolean)** - *optional*
97 |
98 | Set if the video should start playing as soon as possible or to wait for user interaction.
99 |
100 | - **finished - (function)** - *optional*
101 |
102 | Attribute to specify an event callback to execute when the video reaches the end of its duration.
103 |
104 | - **controls - (boolean)** - *optional*
105 |
106 | Set to use the native video player's media playback controls.
107 |
108 | - **muted - (boolean)** - *optional*
109 |
110 | Mutes the native video player.
111 |
112 | - **loop - (boolean)** - *optional*
113 |
114 | Sets the native video player to loop once playback has finished.
115 |
116 | - **backgroundAudio - (boolean)** - *optional*
117 |
118 | If set to true, the audio for the video won't pause any existing audio playing. Instead it will play simultaneously with the existing audio. This is similar to how instagram and facebook play their video audio.
119 |
120 | - **fill - (VideoFill)** - *optional*
121 |
122 | Android: When set to VideoFill.aspectFill, the aspect ratio of the video will not be honored and it will fill the entire space available.
123 |
124 | iOS:
125 | * VideoFill.default = AVLayerVideoGravityResize
126 | * VideoFill.aspect = AVLayerVideoGravityResizeAspect
127 | * VideoFill.aspectFill = AVLayerVideoGravityResizeAspectFill
128 |
129 | See [here for explanation](https://developer.apple.com/documentation/avfoundation/avlayervideogravity).
130 |
131 | - **playbackReady - (function)** - *optional*
132 |
133 | Attribute to specify an event callback to execute when the video is ready to play.
134 |
135 | - **seekToTimeComplete - (function)** - *optional*
136 |
137 | Attribute to specify an event callback to execute when the video has finished seekToTime.
138 |
139 | - **observeCurrentTime - (boolean)** - *optional*
140 |
141 | If set to true, currentTimeUpdated callback is possible.
142 |
143 | - **currentTimeUpdated - (function)** - *optional*
144 |
145 | Attribute to specify an event callback to execute when the time is updated.
146 |
147 |
148 | ## API
149 |
150 | - **play()** - Start playing the video
151 | - **pause()** - Pause the video
152 | - **seekToTime(time: number)** - Seek the video to a time (milliseconds)
153 | - **getCurrentTime()** - Returns the current time in the video duration (milliseconds)
154 | - **getDuration()** - Returns the duration of the video (milliseconds)
155 | - **destroy()** - Destroy the video player and free resources
156 | - **mute(boolean)** - Mute the current video
157 | - **setVolume()** - Set the volume - Must be between 0 and 1.
158 |
159 | ### Android only
160 |
161 | - **stop()** - Stop the playback - this resets the player and remove the video src
162 |
163 |
164 | ## Breaking Changes
165 |
166 | - Android will now attach/detach to the application suspend/resume and de-register/re-register video
167 | - Subtitle support will by default be disabled.
168 |
169 | ## ExoPlayer Encryption (Android only)
170 | ### Create a key based on the password "secret", outputs salt, key, and iv... (You can redirect to a file if you want)
171 | - openssl enc -aes-256-ctr -k secret -P --nosalt
172 | Will output because we aren't using a salt:
173 | ```
174 | key=2BB80D537B1DA3E38BD30361AA855686BDE0EACD7162FEF6A25FE97BF527A25B
175 | iv =015E42FF678B2B90B743111A396EF850
176 | ```
177 |
178 | Normally you would not want to add the `--nosalt`, but to make this easier to follow as the key & iv will be the same with --nosalt
179 | Which would then give you output **like** this, but every difference in salt you get a different key/iv:
180 | ```
181 | salt=42D57450DAF116BD
182 | key=E8E82C95A1A4FEFE5334578678CAD5699091D34322FDD5811A786BE82961DD00
183 | iv =ED07304DF8D0D0AFA2EB9B13D75BD817
184 | ```
185 |
186 |
187 |
188 | ### Create the Encrypted video file
189 | - openssl enc --nosalt -aes-256-ctr -in small.mp4 -out video.enc -K 2BB80D537B1DA3E38BD30361AA855686BDE0EACD7162FEF6A25FE97BF527A25B -iv 015E42FF678B2B90B743111A396EF850
190 | - - you can use `-S ` to set the salt value instead of `--nosalt`
191 |
192 |
193 |
194 | ### Contributors
195 |
196 | - Alex Semenov
197 | - Alex Ziskind [@digitalix](https://twitter.com/digitalix)
198 | - Blake Nussey
199 | - Brad Martin [@BradWayneMartin](https://twitter.com/BradWayneMartin)
200 | - Jibon Lawrence Costa
201 | - Nathanael Anderson [@CongoCart](https://twitter.com/CongoCart)
202 | - Nathan Walker [@wwwalkerrun](https://twitter.com/wwwalkerrun)
203 | - Osie Fortune
204 | - Alex Gritton
205 | - gaudsonu98
--------------------------------------------------------------------------------
/src/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nativescript-exoplayer",
3 | "version": "4.0.2",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "typescript": {
8 | "version": "file:../demo/node_modules/typescript",
9 | "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
10 | "dev": true
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nativescript-exoplayer",
3 | "version": "4.2.0",
4 | "main": "videoplayer",
5 | "typings": "videoplayer.d.ts",
6 | "description": "A NativeScript plugin that uses the ExoPlayer video player on Android to play local and remote videos.",
7 | "nativescript": {
8 | "platforms": {
9 | "android": "3.0.0",
10 | "ios": "3.0.0"
11 | },
12 | "plugin": {
13 | "nan": "true",
14 | "pan": "true",
15 | "core3": "true",
16 | "core6": "true",
17 | "core7": "true",
18 | "category": "Interface"
19 | }
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/nathanaela/nativescript-exoplayer.git"
24 | },
25 | "keywords": [
26 | "NativeScript",
27 | "Android",
28 | "iOS",
29 | "TypeScript",
30 | "video",
31 | "video player",
32 | "movie",
33 | "exoplayer",
34 | "subtitles"
35 | ],
36 | "contributors": [
37 | {
38 | "name": "Nathanael Anderson",
39 | "email": "nathan@master-technology",
40 | "url": "https://github.com/nathanaela"
41 | },
42 | {
43 | "name": "Brad Martin",
44 | "email": "bradmartin0905@gmail.com",
45 | "url": "https://github.com/bradmartin"
46 | },
47 | {
48 | "name": "Alexander Ziskind",
49 | "email": "alex@nuvious.com",
50 | "url": "https://github.com/alexziskind1"
51 | },
52 | {
53 | "name": "Blake Nussey",
54 | "email": "blake@stageme.com",
55 | "url": "https://github.com/bnussey"
56 | },
57 | {
58 | "name": "Juan Manuel Campos Olvera",
59 | "email": "juan4106@hotmail.com",
60 | "url": "https://github.com/juanmcampos"
61 | },
62 | {
63 | "name": "Ivo Georgiev",
64 | "email": "ivo@strem.io",
65 | "url": "https://github.com/Ivshti"
66 | },
67 | {
68 | "name": "Alexander Semenov",
69 | "email": "asemenovboyarka@gmail.com",
70 | "url": "https://github.com/SemenovAlexander"
71 | },
72 | {
73 | "name": "Nathan Walker",
74 | "email": "walkerrunpdx@gmail.com",
75 | "url": "https://github.com/NathanWalker"
76 | },
77 | {
78 | "name": "Alex Gritton",
79 | "email": "alexgritton@ymail.com",
80 | "url": "https://github.com/alexgritton"
81 | },
82 | {
83 | "name": "gaudsonu98",
84 | "email": "gaud.sonu.98@gmail.com",
85 | "url": "https://github.com/gaudsonu98"
86 | }
87 | ],
88 | "author": {
89 | "name": "Nathanael Anderson",
90 | "email": "nathan@master-technology.com",
91 | "url": "https://github.com/nathanaela"
92 | },
93 | "license": "Apache-2.0",
94 | "bugs": {
95 | "url": "https://github.com/nathanaela/nativescript-exoplayer/issues"
96 | },
97 | "homepage": "https://github.com/nathanaela/nativescript-exoplayer",
98 | "readmeFilename": "README.md",
99 | "scripts": {
100 | "tsc": "tsc",
101 | "prepack": "tsc",
102 | "setup": "cd ../demo && npm i"
103 | },
104 | "devDependencies": {
105 | "typescript": "file:../demo/node_modules/typescript"
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/platforms/android/include.gradle:
--------------------------------------------------------------------------------
1 | //default elements
2 | android {
3 |
4 | }
5 |
6 | dependencies {
7 | implementation 'com.google.android.exoplayer:exoplayer:2.9.6'
8 | }
9 |
--------------------------------------------------------------------------------
/src/platforms/android/java/EncryptedFileDataSource.java:
--------------------------------------------------------------------------------
1 | package technology.master.exoplayer;
2 |
3 | // Under MIT license
4 | // Code liberally borrowed from
5 | // https://github.com/moagrius/EncryptedExoPlayerDemo
6 |
7 | import android.util.Log;
8 | import android.net.Uri;
9 | import android.support.annotation.Nullable;
10 |
11 | import com.google.android.exoplayer2.C;
12 | import com.google.android.exoplayer2.upstream.BaseDataSource;
13 | import com.google.android.exoplayer2.upstream.DataSource;
14 | import com.google.android.exoplayer2.upstream.DataSpec;
15 | import com.google.android.exoplayer2.upstream.TransferListener;
16 |
17 | import java.io.EOFException;
18 | import java.io.File;
19 | import java.io.FileInputStream;
20 | import java.io.FileNotFoundException;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.math.BigInteger;
24 | import java.util.Arrays;
25 |
26 | import javax.crypto.Cipher;
27 | import javax.crypto.CipherInputStream;
28 | import javax.crypto.spec.IvParameterSpec;
29 | import javax.crypto.spec.SecretKeySpec;
30 |
31 | public final class EncryptedFileDataSource extends BaseDataSource {
32 |
33 | private StreamingCipherInputStream mInputStream;
34 | private Uri mUri;
35 | private long mBytesRemaining;
36 | private boolean mOpened;
37 | private Cipher mCipher;
38 | private SecretKeySpec mSecretKeySpec;
39 | private IvParameterSpec mIvParameterSpec;
40 |
41 | public EncryptedFileDataSource(Cipher cipher, SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec, @Nullable TransferListener listener) {
42 | super(false);
43 | mCipher = cipher;
44 | mSecretKeySpec = secretKeySpec;
45 | mIvParameterSpec = ivParameterSpec;
46 | if (listener != null) { this.addTransferListener(listener); }
47 | }
48 |
49 | @Override
50 | public long open(DataSpec dataSpec) throws EncryptedFileDataSourceException {
51 | // if we're open, we shouldn't need to open again, fast-fail
52 | if (mOpened) {
53 | return mBytesRemaining;
54 | }
55 | //Log.d("EDS", "opening ds");
56 |
57 | this.transferInitializing(dataSpec);
58 |
59 | // #getUri is part of the contract...
60 | mUri = dataSpec.uri;
61 | // put all our throwable work in a single block, wrap the error in a custom Exception
62 | try {
63 | setupInputStream();
64 | skipToPosition(dataSpec);
65 | computeBytesRemaining(dataSpec);
66 | } catch (IOException e) {
67 | throw new EncryptedFileDataSourceException(e);
68 | }
69 | // if we made it this far, we're open
70 | mOpened = true;
71 | // notify
72 | this.transferStarted(dataSpec);
73 | //Log.d("EDS", "opened: "+mBytesRemaining);
74 |
75 | // report
76 | return mBytesRemaining;
77 | }
78 |
79 | private void setupInputStream() throws FileNotFoundException {
80 | File encryptedFile = new File(mUri.getPath());
81 | FileInputStream fileInputStream = new FileInputStream(encryptedFile);
82 | //Log.d("EDS", "File:"+mUri.getPath());
83 | mInputStream = new StreamingCipherInputStream(fileInputStream, mCipher, mSecretKeySpec, mIvParameterSpec);
84 | }
85 |
86 | private void skipToPosition(DataSpec dataSpec) throws IOException {
87 | //Log.d("EDS", "Skip:"+dataSpec.position);
88 | mInputStream.forceSkip(dataSpec.position);
89 | }
90 |
91 | private void computeBytesRemaining(DataSpec dataSpec) throws IOException {
92 | if (dataSpec.length != C.LENGTH_UNSET) {
93 | mBytesRemaining = dataSpec.length;
94 | } else {
95 | mBytesRemaining = mInputStream.available();
96 | if (mBytesRemaining == Integer.MAX_VALUE) {
97 | mBytesRemaining = C.LENGTH_UNSET;
98 | }
99 | }
100 | }
101 |
102 | @Override
103 | public int read(byte[] buffer, int offset, int readLength) throws EncryptedFileDataSourceException {
104 | // fast-fail if there's 0 quantity requested or we think we've already processed everything
105 | if (readLength == 0) {
106 | return 0;
107 | } else if (mBytesRemaining == 0) {
108 | return C.RESULT_END_OF_INPUT;
109 | }
110 |
111 | // Dec Header should look like: 00 00 00 1C 66 74 79 70 6D 70 34 32 00 00 00 00
112 | // Enc Header should look like: 24 73 41 39 7D A0 CB B5 39 6C 10 50 7B 3C 92 DD
113 | // constrain the read length and try to read from the cipher input stream
114 | int bytesToRead = getBytesToRead(readLength);
115 | //Log.d("EDS", "RL: "+readLength+", BTR: "+ bytesToRead);
116 | int bytesRead;
117 | try {
118 | bytesRead = mInputStream.read(buffer, offset, bytesToRead);
119 | } catch (IOException e) {
120 | throw new EncryptedFileDataSourceException(e);
121 | }
122 |
123 | //if (offset < 16) {
124 | //Log.d("EDS", "Bytes read: "+bytesRead+", Offset: " + offset + ", Value:" + buffer[offset]);
125 | //}
126 | // if we get a -1 that means we failed to read - we're either going to EOF error or broadcast EOF
127 | if (bytesRead == -1) {
128 | if (mBytesRemaining != C.LENGTH_UNSET) {
129 | throw new EncryptedFileDataSourceException(new EOFException());
130 | }
131 | return C.RESULT_END_OF_INPUT;
132 | }
133 |
134 | // we can't decrement bytes remaining if it's just a flag representation (as opposed to a mutable numeric quantity)
135 | if (mBytesRemaining != C.LENGTH_UNSET) {
136 | mBytesRemaining -= bytesRead;
137 | }
138 | // notify
139 | this.bytesTransferred(bytesRead);
140 |
141 | // report
142 | return bytesRead;
143 | }
144 |
145 | private int getBytesToRead(int bytesToRead) {
146 | if (mBytesRemaining == C.LENGTH_UNSET) {
147 | return bytesToRead;
148 | }
149 | return (int) Math.min(mBytesRemaining, bytesToRead);
150 | }
151 |
152 | @Override
153 | public Uri getUri() {
154 | return mUri;
155 | }
156 |
157 | @Override
158 | public void close() throws EncryptedFileDataSourceException {
159 | mUri = null;
160 | try {
161 | if (mInputStream != null) {
162 | mInputStream.close();
163 | }
164 | } catch (IOException e) {
165 | throw new EncryptedFileDataSourceException(e);
166 | } finally {
167 | mInputStream = null;
168 | if (mOpened) {
169 | mOpened = false;
170 | this.transferEnded();
171 | }
172 | }
173 | }
174 |
175 | public static final class EncryptedFileDataSourceException extends IOException {
176 | public EncryptedFileDataSourceException(IOException cause) {
177 | super(cause);
178 | }
179 | }
180 |
181 | public static class StreamingCipherInputStream extends CipherInputStream {
182 |
183 | private static final int AES_BLOCK_SIZE = 16;
184 |
185 | private InputStream mUpstream;
186 | private Cipher mCipher;
187 | private SecretKeySpec mSecretKeySpec;
188 | private IvParameterSpec mIvParameterSpec;
189 |
190 | public StreamingCipherInputStream(InputStream inputStream, Cipher cipher, SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec) {
191 | super(inputStream, cipher);
192 | mUpstream = inputStream;
193 | mCipher = cipher;
194 | mSecretKeySpec = secretKeySpec;
195 | mIvParameterSpec = ivParameterSpec;
196 | }
197 |
198 | @Override
199 | public int read(byte[] b, int off, int len) throws IOException {
200 | return super.read(b, off, len);
201 | }
202 |
203 | public long forceSkip(long bytesToSkip) throws IOException {
204 | long skipped = mUpstream.skip(bytesToSkip);
205 | try {
206 | // Figure out where we need to jump to...
207 | int skip = (int) (bytesToSkip % AES_BLOCK_SIZE);
208 | long blockOffset = bytesToSkip - skip;
209 | long numberOfBlocks = blockOffset / AES_BLOCK_SIZE;
210 |
211 | // TODO: This is designed for CTS mode, for other modes this routine code has to be changed
212 | // So that we don't have to read the entire stream, we have to compute what the IV should be at this point in time in CTS mode
213 | BigInteger ivForOffsetAsBigInteger = new BigInteger(1, mIvParameterSpec.getIV()).add(BigInteger.valueOf(numberOfBlocks));
214 | byte[] ivForOffsetByteArray = ivForOffsetAsBigInteger.toByteArray();
215 | IvParameterSpec computedIvParameterSpecForOffset;
216 | if (ivForOffsetByteArray.length < AES_BLOCK_SIZE) {
217 | byte[] resizedIvForOffsetByteArray = new byte[AES_BLOCK_SIZE];
218 | System.arraycopy(ivForOffsetByteArray, 0, resizedIvForOffsetByteArray, AES_BLOCK_SIZE - ivForOffsetByteArray.length, ivForOffsetByteArray.length);
219 | computedIvParameterSpecForOffset = new IvParameterSpec(resizedIvForOffsetByteArray);
220 | } else {
221 | computedIvParameterSpecForOffset = new IvParameterSpec(ivForOffsetByteArray, ivForOffsetByteArray.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
222 | }
223 |
224 | // Setup the cipher to use the new IV at the proper offset...
225 | mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, computedIvParameterSpecForOffset);
226 | byte[] skipBuffer = new byte[skip];
227 |
228 | // And read/update the buffer to be decrypted
229 | mCipher.update(skipBuffer, 0, skip, skipBuffer);
230 | Arrays.fill(skipBuffer, (byte) 0);
231 | } catch (Exception e) {
232 | return 0;
233 | }
234 | return skipped;
235 | }
236 |
237 | // We need to return the available bytes from the upstream.
238 | // In this implementation we're front loading it, but it's possible the value might change during the lifetime
239 | // of this instance, and reference to the stream should be retained and queried for available bytes instead
240 | @Override
241 | public int available() throws IOException {
242 | return mUpstream.available();
243 | }
244 |
245 | }
246 |
247 | }
--------------------------------------------------------------------------------
/src/platforms/android/java/EncryptedFileDataSourceFactory.java:
--------------------------------------------------------------------------------
1 | package technology.master.exoplayer;
2 |
3 | import com.google.android.exoplayer2.upstream.DataSource;
4 | import com.google.android.exoplayer2.upstream.TransferListener;
5 |
6 | import android.support.annotation.Nullable;
7 |
8 | import javax.crypto.Cipher;
9 | import javax.crypto.spec.IvParameterSpec;
10 | import javax.crypto.spec.SecretKeySpec;
11 |
12 | public class EncryptedFileDataSourceFactory implements DataSource.Factory {
13 |
14 | private Cipher mCipher;
15 | private SecretKeySpec mSecretKeySpec;
16 | private IvParameterSpec mIvParameterSpec;
17 | private TransferListener mTransferListener;
18 |
19 | public EncryptedFileDataSourceFactory(Cipher cipher, SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec, @Nullable TransferListener listener) {
20 | mCipher = cipher;
21 | mSecretKeySpec = secretKeySpec;
22 | mIvParameterSpec = ivParameterSpec;
23 | mTransferListener = listener;
24 | }
25 |
26 | @Override
27 | public EncryptedFileDataSource createDataSource() {
28 | return new EncryptedFileDataSource(mCipher, mSecretKeySpec, mIvParameterSpec, mTransferListener);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/platforms/ios/Podfile:
--------------------------------------------------------------------------------
1 | pod 'ASBPlayerSubtitling'
2 |
--------------------------------------------------------------------------------
/src/subtitle-source/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "subtitle-source",
3 | "main": "subtitle-source.js",
4 | "typings": "subtitle-source.d.ts",
5 | "version": "0.1.0",
6 | "nativescript": {
7 | "platforms": {
8 | "android": "1.7.0",
9 | "ios": "1.7.0"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/subtitle-source/subtitle-source-common.ts:
--------------------------------------------------------------------------------
1 | const utils = require("utils/utils");
2 |
3 | // This is used for definition purposes only, it does not generate JavaScript for it.
4 |
5 | import { SubtitleSource } from './subtitle-source';
6 |
7 | export function fromResource(name: string): SubtitleSource {
8 | const video = new SubtitleSource();
9 | return video.loadFromResource(name) ? video : null;
10 | }
11 |
12 | export function fromFile(path: string): SubtitleSource {
13 | const video = new SubtitleSource();
14 | return video.loadFromFile(path) ? video : null;
15 | }
16 |
17 | export function fromUrl(url: string): SubtitleSource {
18 | const video = new SubtitleSource();
19 | return video.loadFromUrl(url) ? video : null;
20 | }
21 |
22 | export function fromFileOrResource(path: string): SubtitleSource {
23 | if (!isFileOrResourcePath(path)) {
24 | throw new Error("Path \"" + "\" is not a valid file or resource.");
25 | }
26 |
27 | if (path.indexOf(utils.RESOURCE_PREFIX) === 0) {
28 | return fromResource(path.substr(utils.RESOURCE_PREFIX.length));
29 | }
30 | return fromFile(path);
31 | }
32 |
33 | export function isFileOrResourcePath(path: string): boolean {
34 | return utils.isFileOrResourcePath(path);
35 | }
--------------------------------------------------------------------------------
/src/subtitle-source/subtitle-source.android.ts:
--------------------------------------------------------------------------------
1 | import definition = require("./subtitle-source");
2 | const types = require("utils/types");
3 | const utils = require("utils/utils");
4 | const fs = require("file-system");
5 |
6 |
7 | export * from './subtitle-source-common';
8 |
9 | declare var android: any;
10 |
11 | export class SubtitleSource implements definition.SubtitleSource {
12 | public android: any; /// String - url or resource
13 | public ios: any; /// NSString
14 |
15 | public loadFromResource(name: string): boolean {
16 | this.android = null;
17 |
18 | var res = utils.ad.getApplicationContext().getResources();
19 | if (res) {
20 | var UrlPath = "android.resource://org.nativescript.videoPlayer/R.raw." + name;
21 | this.android = UrlPath;
22 |
23 | }
24 |
25 | return this.android != null;
26 | }
27 |
28 | public loadFromUrl(url: string): boolean {
29 | this.android = null;
30 | this.android = url;
31 | return this.android != null;
32 | }
33 |
34 | public loadFromFile(path: string): boolean {
35 |
36 | var fileName = types.isString(path) ? path.trim() : "";
37 | if (fileName.indexOf("~/") === 0) {
38 | fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
39 | }
40 |
41 | this.android = fileName;
42 | return this.android != null;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/subtitle-source/subtitle-source.d.ts:
--------------------------------------------------------------------------------
1 | export declare class SubtitleSource {
2 | android: any; /// String - url or resource
3 | ios: any; /// NSString
4 | loadFromResource(name: string): boolean;
5 | loadFromFile(path: string): boolean;
6 | loadFromUrl(url: string): boolean;
7 | }
8 |
9 | export declare function fromResource(name: string): SubtitleSource;
10 | export declare function fromFile(path: string): SubtitleSource;
11 | export declare function fromUrl(url: string): SubtitleSource;
12 | export declare function fromFileOrResource(path: string): SubtitleSource;
13 | export declare function isFileOrResourcePath(path: string): boolean;
14 |
--------------------------------------------------------------------------------
/src/subtitle-source/subtitle-source.ios.ts:
--------------------------------------------------------------------------------
1 | import types = require("utils/types");
2 | import fs = require("file-system");
3 | import common = require("./subtitle-source-common");
4 | import definition = require("./subtitle-source");
5 |
6 | declare var NSString, NSBundle, NSURL;
7 |
8 | global.moduleMerge(common, exports);
9 |
10 | export class SubtitleSource implements definition.SubtitleSource {
11 | public android: any; /// String - url or resource
12 | public ios: any; /// NSString
13 |
14 | public loadFromResource(name: string): boolean {
15 | let subtitleUrl = NSBundle.mainBundle().URLForResourceWithExtension(name, null);
16 | let subtitles = NSString.stringWithContentsOfURLEncodingError(subtitleUrl, NSUTF8StringEncoding, null);
17 | this.ios = subtitles;
18 | return this.ios != null;
19 | }
20 |
21 | public loadFromFile(path: string): boolean {
22 | var fileName = types.isString(path) ? path.trim() : "";
23 |
24 | if (fileName.indexOf("~/") === 0) {
25 | fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
26 | }
27 |
28 | let subtitleUrl = NSURL.fileURLWithPath(fileName);
29 | let subtitles = NSString.stringWithContentsOfURLEncodingError(subtitleUrl, NSUTF8StringEncoding, null);
30 | this.ios = subtitles;
31 | return this.ios != null;
32 | }
33 |
34 | public loadFromUrl(url: string): boolean {
35 | let subtitleUrl = NSURL.URLWithString(url);
36 | let subtitles = NSString.stringWithContentsOfURLEncodingError(subtitleUrl, NSUTF8StringEncoding, null);
37 | this.ios = subtitles;
38 | return this.ios != null;
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es5",
5 | "sourceMap": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "preserveConstEnums": true,
9 | "declaration": true,
10 | "noLib": false,
11 | "noEmitHelpers": true,
12 | "experimentalDecorators": true,
13 | "noEmitOnError": false,
14 | "lib": [
15 | "es2017",
16 | "dom",
17 | "es6"
18 | ],
19 | "baseUrl": ".",
20 | "paths": {
21 | "*": [
22 | "../demo/node_modules/tns-core-modules/*",
23 | "../demo/node_modules/*"
24 | ]
25 | },
26 | "skipLibCheck": true
27 | },
28 | "files": [
29 | // reference
30 | "../demo/node_modules/tns-platform-declarations/android.d.ts",
31 | "../demo/node_modules/tns-platform-declarations/ios.d.ts",
32 | // sources
33 | "videoplayer.d.ts",
34 | "videoplayer-common.ts",
35 | "videoplayer.ios.ts",
36 | "videoplayer.android.ts",
37 | "subtitle-source/subtitle-source-common.ts",
38 | "subtitle-source/subtitle-source.ios.ts",
39 | "subtitle-source/subtitle-source.android.ts",
40 | "video-source/video-source-common.ts",
41 | "video-source/video-source.ios.ts",
42 | "video-source/video-source.android.ts"
43 | ],
44 | "exclude": [
45 | "node_modules",
46 | "platforms"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/src/video-source/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "video-source",
3 | "main": "video-source",
4 | "typings": "video-source.d.ts",
5 | "version": "0.1.0",
6 | "nativescript": {
7 | "platforms": {
8 | "android": "1.7.0",
9 | "ios": "1.7.0"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/video-source/video-source-common.ts:
--------------------------------------------------------------------------------
1 | const utils = require("utils/utils");
2 |
3 | // This is used for definition purposes only, it does not generate JavaScript for it.
4 | //import definition = require("./video-source");
5 | import { VideoSource } from './video-source';
6 |
7 | export function fromResource(name: string): VideoSource {
8 | const video = new VideoSource();
9 | return video.loadFromResource(name) ? video : null;
10 | }
11 |
12 | export function fromFile(path: string): VideoSource {
13 | const video = new VideoSource();
14 | return video.loadFromFile(path) ? video : null;
15 | }
16 |
17 | export function fromNativeSource(source: any): VideoSource {
18 | const video = new VideoSource();
19 | return video.setNativeSource(source) ? video : null;
20 | }
21 |
22 | export function fromUrl(url: string): VideoSource {
23 | const video = new VideoSource();
24 | return video.loadFromUrl(url) ? video : null;
25 | }
26 |
27 | export function fromFileOrResource(path: string): VideoSource {
28 | if (!isFileOrResourcePath(path)) {
29 | throw new Error("Path \"" + "\" is not a valid file or resource.");
30 | }
31 |
32 | if (path.indexOf(utils.RESOURCE_PREFIX) === 0) {
33 | return fromResource(path.substr(utils.RESOURCE_PREFIX.length));
34 | }
35 | return fromFile(path);
36 | }
37 |
38 | export function isFileOrResourcePath(path: string): boolean {
39 | return utils.isFileOrResourcePath(path);
40 | }
--------------------------------------------------------------------------------
/src/video-source/video-source.android.ts:
--------------------------------------------------------------------------------
1 | import definition = require("./video-source");
2 |
3 | const app = require('@nativescript/core/application');
4 | const types = require("utils/types");
5 |
6 | const utils = require("utils/utils");
7 | const fs = require("file-system");
8 |
9 | export * from './video-source-common';
10 |
11 | export class VideoSource implements definition.VideoSource {
12 | public android: any; /// android.widget.VideoView
13 | public ios: any; /// AVPlayer
14 |
15 | public loadFromResource(name: string): boolean {
16 | this.android = null;
17 |
18 | const res = utils.ad.getApplicationContext().getResources();
19 | if (res) {
20 | const packageName = app.android.context.getPackageName();
21 | this.android = `android.resource://${packageName}/R.raw.${name}`;
22 | }
23 |
24 | return this.android != null;
25 | }
26 |
27 | public loadFromUrl(url: string): boolean {
28 | this.android = null;
29 | this.android = url;
30 | return this.android != null;
31 | }
32 |
33 | public loadFromFile(path: string): boolean {
34 |
35 | let fileName = types.isString(path) ? path.trim() : "";
36 | if (fileName.indexOf("~/") === 0) {
37 | fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
38 | }
39 |
40 | this.android = fileName;
41 | return this.android != null;
42 | }
43 |
44 | public setNativeSource(source: any): boolean {
45 | this.android = source;
46 | return source != null;
47 | }
48 |
49 |
50 | get height(): number {
51 | if (this.android && typeof this.android.getHeight === 'function') {
52 | return this.android.getHeight();
53 | }
54 |
55 | return NaN;
56 | }
57 |
58 | get width(): number {
59 | if (this.android && typeof this.android.getWidth === 'function') {
60 | return this.android.getWidth();
61 | }
62 |
63 | return NaN;
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/src/video-source/video-source.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains the VideoSource class, which encapsulates the common abstraction behind a platform specific object that is used as a source for videos.
3 | */
4 | // declare module "video-source" {
5 |
6 | /**
7 | * Encapsulates the common abstraction behind a platform specific object that is used as a source for videos.
8 | */
9 | export class VideoSource {
10 |
11 |
12 | /**
13 | * Gets the height of this instance. This is a read-only property.
14 | */
15 | height: number;
16 |
17 | /**
18 | * Gets the width of this instance. This is a read-only property.
19 | */
20 | width: number;
21 |
22 | /**
23 | * The iOS-specific [AVPlayer](https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html) instance. Will be undefined when running on Android.
24 | */
25 | ios: any /* AVPlayer */;
26 |
27 | /**
28 | * The Android-specific [VideoView](http://developer.android.com/intl/zh-tw/reference/android/widget/VideoView.html) instance. Will be undefined when running on iOS.
29 | */
30 | android: any /* android.widget.VideoView */;
31 |
32 | /**
33 | * Loads this instance from the specified resource name.
34 | * @param name The name of the resource (without its extension).
35 | */
36 | loadFromResource(name: string): boolean;
37 |
38 | /**
39 | * Loads this instance from the specified file.
40 | * @param path The location of the file on the file system.
41 | */
42 | loadFromFile(path: string): boolean;
43 |
44 | /*
45 | * Loads this instance from the specified url.
46 | * @param url location of the video file
47 | */
48 | loadFromUrl(url: string): boolean;
49 |
50 | /**
51 | * Sets the provided native source object.
52 | * This will update either the android or ios properties, depending on the target os.
53 | * @param source The native image object. Will be either a Bitmap for Android or a UIImage for iOS.
54 | */
55 | setNativeSource(source: any): boolean;
56 | }
57 |
58 | /**
59 | * Creates a new ImageSource instance and loads it from the specified resource name.
60 | * @param name The name of the resource (without its extension).
61 | */
62 | export function fromResource(name: string): VideoSource;
63 |
64 | /**
65 | * Creates a new ImageSource instance and loads it from the specified file.
66 | * @param path The location of the file on the file system.
67 | */
68 | export function fromFile(path: string): VideoSource;
69 |
70 |
71 | /**
72 | * Creates a new ImageSource instance and sets the provided native source object (typically a Bitmap).
73 | * The native source object will update either the android or ios properties, depending on the target os.
74 | * @param source The native image object. Will be either a Bitmap for Android or a UIImage for iOS.
75 | */
76 | export function fromNativeSource(source: any): VideoSource;
77 |
78 | /**
79 | * Downloads the video from the provided Url and creates a new VideoSource instance from it.
80 | * @param url The link to the remote video file. This operation will download the video.
81 | */
82 | export function fromUrl(url: string): VideoSource;
83 |
84 | /**
85 | * Creates a new ImageSource instance and loads it from the specified local file or resource(if spexified with "res://" prefix)
86 | * @param path The location of the file on the file system.
87 | */
88 | export function fromFileOrResource(path: string): VideoSource;
89 |
90 | /**
91 | * [Obsolete. Please use utils.isFileOrResourcePath instead!] Returns true if the specified path points to a resource or local file.
92 | * @param path The path.
93 | */
94 | export function isFileOrResourcePath(path: string): boolean
95 | // }
96 |
--------------------------------------------------------------------------------
/src/video-source/video-source.ios.ts:
--------------------------------------------------------------------------------
1 | import types = require("utils/types");
2 | import fs = require("file-system");
3 | import common = require("./video-source-common");
4 | import enums = require("ui/enums");
5 | import definition = require("./video-source");
6 |
7 | declare var android, AVPlayerItem, NSBundle, NSURL;
8 |
9 | global.moduleMerge(common, exports);
10 |
11 | export class VideoSource implements definition.VideoSource {
12 | public android: any; /// android.widget.VideoView
13 | public ios: any; /// AVPlayerItem
14 | height: any;
15 | width: any;
16 |
17 | public loadFromResource(name: string): boolean {
18 | let videoURL = NSBundle.mainBundle().URLForResourceWithExtension(name, null);
19 | let player = new AVPlayerItem(videoURL);
20 | this.ios = player;
21 | return this.ios != null;
22 | }
23 |
24 | public loadFromFile(path: string): boolean {
25 | var fileName = types.isString(path) ? path.trim() : "";
26 |
27 | if (fileName.indexOf("~/") === 0) {
28 | fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
29 | }
30 |
31 | let videoURL = NSURL.fileURLWithPath(fileName);
32 | let player = new AVPlayerItem(videoURL);
33 | this.ios = player;
34 | return this.ios != null;
35 | }
36 |
37 | public loadFromUrl(url: string): boolean {
38 | let videoURL = NSURL.URLWithString(url);
39 | let player = new AVPlayerItem(videoURL);
40 | this.ios = player;
41 | return this.ios != null;
42 | }
43 |
44 | public setNativeSource(source: any): boolean {
45 | this.ios = source;
46 | return source != null;
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/videoplayer-common.ts:
--------------------------------------------------------------------------------
1 | import * as videoSource from "./video-source/video-source";
2 | import * as subtitleSource from "./subtitle-source/subtitle-source";
3 | import { isFileOrResourcePath } from "utils/utils";
4 | import { isString } from "utils/types"
5 | import { View, Property, booleanConverter } from "tns-core-modules/ui/core/view";
6 | import * as imageSource from "tns-core-modules/image-source";
7 |
8 | // on Android we explicitly set propertySettings to None because android will invalidate its layout (skip unnecessary native call).
9 | // var AffectsLayout = platform.device.os === platform.platformNames.android ? dependencyObservable.PropertyMetadataSettings.None : dependencyObservable.PropertyMetadataSettings.AffectsLayout;
10 |
11 | function onSrcPropertyChanged(view, oldValue, newValue) {
12 |
13 | const video = view;
14 | let value = newValue;
15 |
16 | if (isString(value)) {
17 | value = value.trim();
18 | video.videoSource = null;
19 | video["_url"] = value;
20 | video.isLoadingProperty = true;
21 | if (isFileOrResourcePath(value)) {
22 | video.videoSource = videoSource.fromFileOrResource(value);
23 | video.isLoadingProperty = false;
24 | } else {
25 | if (video["_url"] === value) {
26 | video.videoSource = videoSource.fromUrl(value);
27 | video.isLoadingProperty = false;
28 | }
29 | }
30 | } else if (value instanceof videoSource.VideoSource) {
31 | video.videoSource = value;
32 | } else {
33 | video.videoSource = videoSource.fromNativeSource(value);
34 | }
35 | }
36 |
37 | function onSubtitlesPropertyChanged(view, oldValue, newValue) {
38 | const video = view;
39 | if (isString(newValue)) {
40 | let value = newValue.trim();
41 | video.subtitleSource = null;
42 | if (isFileOrResourcePath(value)) {
43 | video.subtitleSource = subtitleSource.fromFileOrResource(value);
44 | } else {
45 | video.subtitleSource = subtitleSource.fromUrl(value);
46 | }
47 | }
48 | }
49 |
50 | function onImgSrcPropertyChanged(view, oldValue, newValue) {
51 | const video = view;
52 | let value = newValue;
53 |
54 | if (isString(value)) {
55 | value = value.trim();
56 | video["_url"] = value;
57 | video.isLoadingProperty = true;
58 | if (isFileOrResourcePath(value)) {
59 | video.imageSource = imageSource.fromFileOrResource(value);
60 | video.isLoadingProperty = false;
61 | } else {
62 | if (video["_url"] === value) {
63 | video.imageSource = imageSource.fromUrl(value);
64 | video.isLoadingProperty = false;
65 | }
66 | }
67 | } else if (value instanceof imageSource.ImageSource) {
68 | video.imageSource = value;
69 | } else {
70 | video.imageSource = imageSource.fromNativeSource(value);
71 | }
72 | }
73 |
74 | /**
75 | * Video aspect/fill handling
76 | */
77 | export enum VideoFill {
78 | default = "default",
79 | aspect = "aspect",
80 | aspectFill = "aspectFill"
81 | }
82 |
83 | export class Video extends View {
84 | public static finishedEvent: string = "finished";
85 | public static playbackReadyEvent: string = "playbackReady";
86 | public static playbackStartEvent: string = "playbackStart";
87 | public static seekToTimeCompleteEvent: string = "seekToTimeComplete";
88 | public static currentTimeUpdatedEvent: string = "currentTimeUpdated";
89 |
90 | public _emit: any;
91 | public android: any;
92 | public ios: any;
93 | public src: string; /// video source file
94 | public srcType: number = 0; /// video source file type
95 | public imgSrc: string;
96 | public imgType: number = 1;
97 | public subtitles: string; /// subtitles source file
98 | public subtitleSource: string; /// subtitle source content
99 | public observeCurrentTime: boolean; // set to true if want to observe current time.
100 | public autoplay: boolean = false; /// set true for the video to start playing when ready
101 | public controls: boolean = true; /// set true to enable the media player's playback controls
102 | public loop: boolean = false; /// whether the video loops the playback after extends
103 | public muted: boolean = false;
104 | public fill: VideoFill = VideoFill.default;
105 | public backgroundAudio: boolean = false;
106 |
107 | // Used for Android Encryption
108 | public encryptionKey: string = null;
109 | public encryptionIV: string = null;
110 | public encryption: string = "";
111 |
112 | public static IMAGETYPEMONO = 1;
113 | public static IMAGETYPESTEREOTOPBOTTOM = 2;
114 | public static IMAGETYPESTEREOLEFTRIGHT = 3;
115 | }
116 |
117 | export const encryptionKeyProperty = new Property