Widevine Android SDK Guide
Overview
PallyCon Widevine Android SDK makes it easy to apply Google’s Widevine Modular DRM when developing media service apps for Android. This document describes how to use the libraries and sample project included in the SDK.
Details of PallyCon Multi DRM service integration are provided in License Token Guide. For technical questions about using the SDK, please visit our Helpdesk site.
Requirements
- Android version 5.0 (Lollipop) or later. AndroidX library is used.
- PallyCon Widevine Android SDK uses Google Media3 framework internally.
Tutorial Video
This video is a tutorial for playing DRM content using the sample project included in the SDK.
For optimal playback, select ‘1080p’ as the video quality and enable subtitle (Korean or English) before starting playback.
Quick Start
You can apply PallyCon Widevine Android SDK to your development project by following these steps:
-
Extract SDK zip file.
-
Copy
PallyconWVMSDK.aar
file toproject/module/libs/
folder in your project. -
Apply the below configuration in
build.gradle
(project).buildscript { repositories { jcenter() google() } dependencies { classpath "com.android.tools.build:gradle:7.2.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" } } allprojects { repositories { jcenter() google() } }
-
Apply the below configuration in
build.gradle
(app).android { defaultConfig { minSdkVersion 21 targetSdkVersion 32 multiDexEnabled true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.aar']) implementation 'androidx.appcompat:appcompat:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "androidx.core:core-ktx:1.8.0" implementation "com.google.android.material:material:1.6.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" // Exo implementation 'com.google.android.exoplayer:exoplayer:2.18.1' implementation "com.google.android.exoplayer:exoplayer-core:2.18.1" implementation "com.google.android.exoplayer:exoplayer-dash:2.18.1" implementation "com.google.android.exoplayer:extension-okhttp:2.18.1" // Gson implementation "com.google.code.gson:gson:2.9.1" // Secure implementation "androidx.security:security-crypto-ktx:1.1.0-alpha03" }
-
Implement
PallyConEventListener
inMainActivity
. (Please refer to sample project)val pallyConEventListener: PallyConEventListener = object : PallyConEventListener { override fun onCompleted(currentUrl: String?) { // Called when download is complete: Please refer to the API Guide. } override fun onProgress(currentUrl: String?, percent: Float, downloadedBytes: Long) { // Call from start to end of download: Please refer to the API Guide. } override fun onStopped(currentUrl: String?) { // Called when download is stopped: Refer to the API Guide. } override fun onRestarting(currentUrl: String?) { // Called when download is restarting: Refer to the API Guide. } override fun onRemoved(currentUrl: String?) { // Called when downloaded content is removed: Refer to the API Guide. } override fun onPaused(currentUrl: String?) { // Called when download is pause: Refer to the API Guide. } override fun onFailed(currentUrl: String?, e: PallyConException?) { // Called when an error occurs while downloading content or an error occurs in the license: Refer to the API Guide. } override fun onFailed(currentUrl: String?, e: PallyConLicenseServerException?) { // Called when error sent from server when acquiring license: Refer to the API Guide. } }
-
Create a
PallyConWvSDK
object with content information to download. Set your Site ID shown on PallyCon Console. (Please refer to sample project)// Enter DRM related information. val config = PallyConDrmConfigration( "site id", "site key", // Set to an empty string if you don't know "content token", "custom data", mutableMapOf(), // custom header "cookie" ) val data = PallyConData( contentId = "content id", url = "content URL", localPath = "Download location where content will be stored", drmConfig = config, cookie = null ) val wvSDK = PallyConWvSDK.createPallyConWvSDK( Context, // Context data ) wvSDK.setPallyConEventListener(pallyConEventListener)
-
Get the track information of the content to be downloaded.
// The device must be connected to a network. // When track information is acquired, the license is also automatically downloaded. val trackInfo = wvSDK.getContentTrackInfo()
-
Select the track you want to download from the track information.
// In our sample, we use TrackSelectDialog to select. trackInfo.video[0].isDownload = true trackInfo.audio[0].isDownload = true
-
Execute the download after checking if the content has already been downloaded.
val state = wvSDK.getDownloadState() if (state != COMPLETED) { wvSDK.download(trackInfo) }
-
To play downloaded content, obtain
MediaItem
orMediaSource
using the following API.// use MediaSource or MediaItem val mediaSource = wvSDK.getMediaSource() val mediaItem = wvSDK.getMediaItem()
-
Implement player in PlayerActivity.java using the below development guide. http://google.github.io/ExoPlayer/guide.html
Please refer to the below guide from Google for more information about Exoplayer. https://developer.android.com/guide/topics/media/exoplayer.html
-
Check
license duration
andplayback duration
of DRM license.val drmInfo = wvSDK.getDrmInformation() val licenseDuration = drmInfo.licenseDuration val playbackDuration = drmInfo.playbackDuration if (licenseDuration <= 0 || playbackDuration <= 0) { // DRM License Expired }
-
Set up ExoPlayer as follows.
ExoPlayer.Builder(this).build() .also { player -> exoPlayer = player binding.exoplayerView.player = player exoPlayer?.setMediaSource(mediaSource) //use mediaSource. exoPlayer?.addListener(object : Player.Listener { override fun onPlayerError(error: PlaybackException) { super.onPlayerError(error) } override fun onIsPlayingChanged(isPlaying: Boolean) { super.onIsPlayingChanged(isPlaying) } }) }
Registering download services
-
To support background notifications for downloads, register your download service as shown below.
wvSDK.setDownloadService(DemoDownloadService::class.java)
-
Register the download service in the AndroidManifest.xml.
<service android:name="com.pallycon.pallyconsample.DemoDownloadService" android:exported="false"> <intent-filter> <action android:name="com.google.android.exoplayer.downloadService.action.RESTART" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
Managing licenses
You can download and delete DRM licenses as below.
Downloading license
val uri = Uri.parse("content url")
// val dataSource = FileDataSource.Factory() // local file
val okHttpClient = OkHttpClient.Builder().build()
val dataSource = OkHttpDataSource.Factory(okHttpClient) // remote content
val dashManifest =
DashUtil.loadManifest(dataSource.createDataSource(), uri)
val format = DashUtil.loadFormatWithDrmInitData(
dataSource.createDataSource(),
dashManifest.getPeriod(0)
// The format parameter does not need to be entered unless it is a local file.
// If the format value is NULL, it is automatically defined inside the SDK via the REMOTE CONTENT URL.
wvSDK.downloadLicense(format = format, {
Toast.makeText(this@MainActivity, "success download license", Toast.LENGTH_SHORT).show()
}, { e ->
Toast.makeText(this@MainActivity, "${e.message()}", Toast.LENGTH_SHORT).show()
print(e.msg)
})
Removing license
wvSDK.removeLicense()
Additional Guides
Blocking screen recording
To prevent content leaks with screen recording apps, you should block the capture function by adding the following code to your application:
val view = binding.exoplayerView.videoSurfaceView as SurfaceView
view.setSecure(true)
Migration from previous SDK version
Since the download method is different from widevine sdk 3.0.0, customers who are using the existing widevine sdk 2.x.x version must migrate the downloaded content.
You can use the needsMigrateDownloadedContent
function to determine if the content needs to be migrated.
Since the migration function operates only when there is migration content inside, it does not matter if it is called multiple times, and the parameter values of the function should be set identically to the values used in the existing 2.x.x version.
The localPath used when creating the PallyConData
object should not be set as the parent directory of the existing downloaded contents. Therefore, if a MigrationLocalPathException
exception occurs, the localPath value used when creating the PallyConData
object must be modified for normal operation.
try {
if (wvSDK.needsMigrateDownloadedContent(
url = contents[index].content.url!!,
contentId = contents[index].cid,
siteId = contents[index].content.drmConfig!!.siteId!!)
) {
val isSuccess = wvSDK.migrateDownloadedContent(
url = "", // remote content URL
contentId = "", // ID of content
siteId = "", // inputs Site ID which is issued on PallyCon service registration
contentName = "", // content's name which will be used for the name of downloaded content's folder
downloadedFolderName = null // content download folder name
)
}
} catch (e: PallyConException.MigrationException) {
print(e)
} catch (e: PallyConException.MigrationLocalPathException) {
// you have to change localPath
// ex) val localPath = File(fi, "downloads_v2").toString()
print(e)
}
If the migration is successful, you can delete the 2.x.x version db by yourself like the code below.
val isSuccess = wvSDK.removeOldDownloadedContentDB(
url = "", // remote content URL
contentId = "", // ID of content
siteId = "", // inputs Site ID which is issued on PallyCon service registration
)
SDK API Reference
Please refer to the doc/en/api_reference.html file of the SDK zip file for detailed explanation of each API.