Widevine DRM and Android videoplayback issue - android

I am facing a really annoying problem with the Widevine Library for Android. For some reason when trying to stream HLS with Widevine some devices (specially Samsung devices) give the following error when trying to play:
WV_Info_GetCodecConfig ESDS returned error 2002
After this, as it does not starts playing I stop, reset and release the media player as well as the DrmClient.
#Override
public void onCompletion(MediaPlayer mp) {
mPlayIndicator.setEnabled(false);
mSurfaceView.setVisibility(View.INVISIBLE);
mTimelineSeekBar.setMax(0);
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("VideoPlayer", "ClosingVideoThread");
}
hideLoading();
if (mScheduleTaskExecutor != null) {
mScheduleTaskExecutor.shutdown();
}
if (mp != null) {
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("VideoPlayer", "Stop Playing");
}
if (mp.isPlaying()) {
mp.stop(); // It's always safe to call stop()
}
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("VideoPlayer", "Reset");
}
mp.reset();
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("VideoPlayer", "Release");
}
mp.release(); // release resources internal to the MediaPlayer
mMediaPlayer = null; // remove reference to MediaPlayer to allow GC
}
// IMPORTANT: It is important to release the DRM client after releasing the media player other wise there are
// situations where the media player it is left in a bad state and does not play any more DRM protected content
// until the restart of the device.
// If the video is DRM protected then release the resource associated with the DRM.
if (mIsDrmProtected) {
mDrmManager.releaseDrmClient();
}
if (!mStopEventFired) {
// Fire the event into the bus to the subscribed views to replace the views accordingly
fireStopVideoPlaybackEvent();
}
}
And the code in the DrmManager to release the DrmClient:
#SuppressLint("NewApi")
public void releaseDrmClient() {
BusProvider.getInstance().unregister(this);
if (mDrmManagerClient != null) {
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("DRMManager", "Releasing DRM");
}
mDrmManagerClient.removeAllRights();
// Starting from API 16 they included this function to release the drm client.
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
if (currentApiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
mDrmManagerClient.release();
}
// Set to null so will be removed by the garbage collector
mDrmManagerClient = null;
if (PlayerEnvConfig.USE_DEBUG_LOGGING) {
Log.d("DRMManager", "Releasing DRM Finally");
}
}
}
Ok well it does not play. I can confirm that all this code it is executed as they appear in the logs. BUT here is the really big problem, after this situation a process is left in the background (I cannot manage to find anywhere in the device which is the process) as if the video was still being played and the following error is shown constantly in the logs.
WVSession::SetWarning: status=2014, desc=MPEG2-TS continuity counter error
I realised because fir-stable the device gets really hot and second-able a used wireshark to sniff the traffic and I can see the requests made in the background.
This only happens when using HLS and Widevine, and when this last one fails playing. (The rights are actually retrieved and installed correctly but when trying to play fails).
Does anyone have any clue why this could be happening and specially how to avoid it??
Btw: The media player is embedded in a fragment, and this fragment inside another fragment.
Thanks!

Related

Android MediaPlayer IllegalStateExceptoion when called pause()

I want to have two different background music loops playing depending on the state of the app. To do so I tried that code:
private void backgroundMusicPlayer() {
if (gameMode == 0) {
if (backgroundloop2 != null) {
backgroundloop2.pause();
backgroundloop2.stop();
backgroundloop2.release();
backgroundloop2 = null;
}
backgroundloop1 = MediaPlayer.create(getContext(), R.raw.gameloop1);
backgroundloop1.setLooping(true);
backgroundloop1.start();
}
else {
if (backgroundloop1 != null) {
backgroundloop1.pause();
backgroundloop1.stop();
backgroundloop1.release();
backgroundloop1 = null;
}
backgroundloop2 = MediaPlayer.create(getContext(), R.raw.gameloop2);
backgroundloop2.setLooping(true);
backgroundloop2.start();
}
}
But I just get errors:
"MediaPlayer: start called in state 64" "MediaPlayer: pause called in
state 8" "Failed to open libwvm.so: dlopen failed: library "libwvm.so"
not found" "Media Player called in state 0, error (-38,0)"
How can I do it properly?
Why IllegalStateException in onPause()?
In Android Documentation for onPause:
IllegalStateException
If the internal player engine has not been initialized.
In the document, you can see a maximum of media player methods would throw you IllegalStateException for some reason.
So use try catch for all of your media player operations.
Android recommends looking for Exceptions while using media player object.
It is good programming practice to always look out for
IllegalArgumentException and IOException that may be thrown from the
overloaded setDataSource methods.

Play music synchronous using 3 MediaPlayer Objects on Android/Eclipse

What i have:
I have implemented three MediaPlayer.Objects in my App.
All Three are created using a thread:
protected void onResume() {
// Threads
mTT1 = new TrackThread(this, R.raw.audiofile1, 1, mHandler);
mTT2 = new TrackThread(this, R.raw.audiofile2, 2, mHandler);
mTT3 = new TrackThread(this, R.raw.audiofile3, 3, mHandler);
// start thread
mTT1.start();
mTT2.start();
mTT3.start();
super.onResume();
}
"simplified" Code in the Thread for creating:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
public void run() {
super.run();
try {
mMp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getDeclaredLength());
mMp.prepare();
} catch (IllegalArgumentException | IllegalStateException
| IOException e) {
Log.e(TAG, "Unable to play audio queue do to exception: "
+ e.getMessage(), e);
}
}
As I read in several Tutorials the "prepare()" methode takes a little bit of time to finish. Therefore i implemented a "Waiting loop" which waits until all MPs are prepared and created.
When "prepare and create" are done i enable the Start button and i want to start all 3 Mediaplayers SIMULTANEOUSLY.
I again use a Thread for dooing so:
public void onClick(View v) {
// Button 1
if (mBtn.getId() == v.getId()) {
mTT1.startMusic();
mTT2.startMusic();
mTT3.startMusic();
}
Code in the thread:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
// start
public void startMusic() {
if (mMp == null)
return;
mMp.start();
}
Please note that the code above is not the full code, but it should be enough to define my problem.
What i want, My problem:
All MPs should play their Music in Sync, unfortunately sometimes when i start the music, there is a time delay between them.
The MPs must start at the exact same time as the 3Audio-files must be played simultaneously (and exactly in sync)
What i have already tried:
+) using SoundPool: My Audio-files are to big(5Megabyte and larger) for SoundPool
+) seekTo(msec): i wanted to seek every MP to a Specific time: eg.: 0, but this did not solve the problem.
+) to reach more Programmers i also asked this question on: coderanch.com
I hope somebody can help me!
Thanks in advance
The bottleneck here will certainly be preparing the mediaplayers to play. The Android framework provides an asynchronous method to perform this loading, and so with a bit of synchronization code you should be able to get these audio sources to play at roughly the same time. To keep from sound artifacting, you'll want less than 10ms of latency.
Initialize an atomic counter, C, to the number of things to load.
Use the prepareAsync() functions within MediaPlayer to prepare all three. Immediately after calling prepareAsync, supply a listener using setOnPreparedListener(listener).
Inside this listener, decrement C and check the value. If the value is greater than 0, wait on an object using the java object .wait() function. If the value is equal to 0, call notifyAll() on the object to wake up all of the other mediaplayer prepared-listener callback threads.
public void startMediaPlayers(List<MediaPlayer> mediaPlayers) {
private AtomicInteger counter = new AtomicInteger(mediaPlayers.size());
Object barrier = new Object();
/* start off all media players */
for (MediaPlayer player : mediaPlayers) {
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
int value = counter.decrementAndGet();
if (value == 0) {
// all media players are done loading.
// wake up all the ones that are asleep
barrier.notifyAll();
} else {
while (value > 0) {
try {
// wait for everyone else to load
barrier.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
mediaPlayer.start();
callback.success(true);
}
player.prepareAsync();
}
}
As nobody could help me I found a solution on my own. MediaPlayer did not fulfill my requirements but Android JETPlayer in combination with JETCreator did.
CAUTION: Installing Python for using JETCreator is very tricky, therfore
follow this tutorial. And be careful with the versions of python and wxpython, not all versions support the JETCreator.
I used:
Python Version 2.5.4 (python-2.5.4.msi)
wxPython 2.8 (wxPython2.8-win32-unicode-2.8.7.1-py25.exe)
For those who do not know how to implement the Jetplayer watch this video
(at min.5 he starts with programming the Jetplayer).
Unfortunately I do not speak French so I just followed the code which worked for me.
Using Android JETCreator you can create your own JET Files and use them as your resource.
Useful links:
Demo data
Manual
Code/class

Is it possible to reduce the period between audio tracks being played with MediaPlayer?

I have a number of audio tracks that I need to play in sequence. I'm using MediaPlayer's OnCompletionListener as follows:
public void OnCompletion(MediaPlayer mp)
{
_completed++;
mp.Reset();
if (_completed < _tracks.Length)
{
try
{
AssetFileDescriptor afd = _context.Resources.OpenRawResourceFd(_tracks[_completed]);
if (afd != null)
{
mp.SetDataSource(afd.FileDescriptor, afd.StartOffset, afd.Length);
afd.Close();
mp.Prepare();
mp.Start();
}
}
catch (Exception ex)
{
throw;
}
}
}
This works, but there is a noticeable delay between tracks. I'd like to reduce that period to as close to zero as possible. Is this possible?
MediaPlayer "chaining" was added with API level 16 (Jellybean). The method you'd be interested in is setNextMediaPlayer(MediaPlayer next):
Set the MediaPlayer to start when this MediaPlayer finishes playback (i.e. reaches the end of the stream). The media framework will attempt to transition from this player to the next as seamlessly as possible. The next player can be set at any time before completion. The next player must be prepared by the app, and the application should not call start() on it.
Not with the standard android player though there were many requests to add gapless playback - https://code.google.com/p/android/issues/detail?id=3461
You can try to create and prepare a second instance of MediaPlayer and switch them quickly in the OnCompletion callback.

MediaPlayer.prepare is throwing an IllegalStateException when playing m4a file

I have a list of songs that I'm streaming using the MediaPlayer. Some of the songs consistently work and others consistently do not work. I can't see a difference between these files, and they seem to play fine in itunes and such.
When the songs fail it is throwing an IllegalStateException on the mediaPlayer.prepare() line. The IllegalStateException that is thrown has no useful info in it, (detailMessage is null, stackState is null)
Here is my code
try {
mediaPlayer.setDataSource(media.url);
setPlayerState(PlayerState.PREPARING);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "bad stream");
}
Here is a url to the file that does NOT work:
skdy.bryceb.dev.mediarain.com/song.m4a
Here is one that DOES work:
skdy.bryceb.dev.mediarain.com/song2.m4a
Any ideas why this works on some songs and fails on others?
Thanks MisterSquonk I'm sure that way would work.
In my particular case after beating my head against the wall for a while I realized that on some songs, I was getting to the buffered amount before the player state was getting set to prepared. So I added a check to make sure that the MediaPlayer was in the "PREPARED" state and then it worked great:
// Media prepared listener
mediaPlayer.setOnPreparedListener(
new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
setPlayerState(PlayerState.PREPARED);
}
});
// Media buffer listener
mediaPlayer.setOnBufferingUpdateListener(
new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
// Sometimes the song will finish playing before the 100% loaded in has been
// dispatched, which result in the song playing again, so check to see if the
// song has completed first
if(getPlayerState() == PlayerState.COMPLETED)
return;
if(getPlayerState() == PlayerState.PAUSED)
return;
// If the music isn't already playing, and the buffer has been reached
if(!mediaPlayer.isPlaying() && percent > PERCENT_BUFFER) {
if(getPlayerState() == PlayerState.PREPARED)
{
mediaPlayer.start();
setPlayerState(PlayerState.PLAYING);
}
//if it isn't prepared, then we'll wait till the next buffering
//update
return;
}
}
});
OK, I hacked together a minimal Mediaplayer implementation in a 'sandbox' app/activity I always keep spare for testing.
I might be wrong but if you're streaming these songs over the net, you'll need to prefix the url with http://.
I tried the urls with Winamp and Chrome verbatim (no protocol prefix string) and they worked fine although it's likely both of those applications will use some form of intelligence to work out how to connect/stream.
If I tried that in my mediaPlayer code, I get the same exception as you but if I prefix the urls with http:// the songs play fine.
Example...
// Activity scope
Button button;
CheckBox checkBox;
String url = "";
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//button declared in my activity
button = (Button)findViewById(R.id.button);
button.setOnClickListener(this);
if (!checkBox.isChecked())
url = getString(R.string.url_song1);
else
url = getString(R.string.url_song2);
mediaPlayer = new MediaPlayer();
}
#Override
public void onClick(View arg0) {
try {
Log.i(TAG, "onClick() entered...");
mediaPlayer.setDataSource(url);
Log.i(TAG, "Preparing mediaplayer...");
mediaPlayer.prepare();
Log.i(TAG, "Starting mediaplayer...");
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "bad stream");
}
}
If I copy the songs to my SD card both play fine and as long as the internet url strings have an 'http://' prefix then they also work.

Android MediaPlayer is preparing too long

Hey,
I'm using MediaPlayer to play a regular ShoutCast stream. The code is straightforward with prepareAsync() and a handler to start the playback. While it works flawlessly with some streams like DI.FM or ETN.FM (http://u10.di.fm:80/di_progressive), with others (http://mp3.wpsu.org:8000/) it won't go past the prepare state. No other listeners are called either.
//Uri streamUri = Uri.parse("http://u10.di.fm:80/di_progressive"); /* works */
Uri streamUri = Uri.parse("http://mp3.wpsu.org:8000/"); /* stuck on prepare state */
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(this.getBaseContext(), streamUri);
mediaPlayer.prepareAsync();
Any feedback is appreciated!
I think that there are some compatibility problems with the server end.
This is rather strange since the emulator handles it ok in my case - just not on my Froyo Galaxy S, even though it is the same API version.
It could be a codec issue, http streaming issue, I do not know.
But all the servers that fail tend to be old ones, with "Copyright 1998 - 2004" at the bottom... Not exactly recent or up to date you would think.
One potential workaround (which I have not tried yet) would be to use the StreamProxy, which would also make your code compatible with 2.1 and possibly earlier versions too. At the cost of extra work, extra code, and without doubt extra bugs...
In case you are not aware of it, there is another player bug report for 2.2 which may be relevant too:
Basic streaming audio works in 2.1 but not in 2.2
I'm facing an issue when MP "hangs" at preparing state too long (stream) and i'm trying to stop it using reset(). This causes MP to hang and thus my whole app freezes. Seems like there is no way to stop MP at preparing state. Im thinking on use prepare() wrapped in thread instead of prepareAsync(). Then i'll be able to kill that thread. As for now i did it in following way:
private void actionCancel(){
try {
mp.setDataSource(new String());
} catch (Exception e) {
e.printStackTrace();
android.util.Log.d(TAG,"actionCancel(): mp.setDataSource() exception");
mp.reset();
}
}
and it works 4me.
Additionally i have a following counter:
#Override
public void onBufferingUpdate(final MediaPlayer mp, final int percent) {
if (!mp.isPlaying()){
// android.util.Log.d(TAG,"onBufferingUpdate(): onBufferingUpdateCount = "+onBufferingUpdateCount);
if (onBufferingUpdateCount>MAX_BUFFERING_UPDATES_AT_PREPARING_STATE)
restartMP();
onBufferingUpdateCount++;
return;
}
}
i'd discover this listener always triggers at preparing state. So if it triggers more than 10 times and MP is still not playing i'm just restarting it:
private void restartMP(){
if (mp!=null)
if (mpState==MediaPlayerState.Preparing)
actionCancel();
else
mp.reset();
else
mp = new MediaPlayer();
mpState = MediaPlayerState.Idle;
onBufferingUpdateCount=0;
//isRequestCancelled=false;
requestTrackInfoStartedAt=0;
requestPlay();
}
note MediaPlayerState is my custom enum which has "Preparing" value. Also mpState is a class property/field which holds current MediaPlayerState state. Before starting prepareAsync() im setting mpState to MediaPlayerState.Preparing after it completes im setting it to MediaPlayerState.Started or other corresponding value.

Categories

Resources