Closed jboisjo closed 7 years ago
There is the metadata listener (setMetadataListener()
) that may include that information
How do you suggest that i approach it with the player? I did this, but nothing seems to be printing on the listener or starting my m3u8 with the key inside...
emVideoView.setVideoURI(Uri.parse(streamURL), getRendererBuilder(MediaSourceType.HLS, Uri.parse(streamURL), "UserAgent"));
emVideoView.setId3MetadataListener(new Id3MetadataListener() {
@Override
public void onId3Metadata(List<Id3Frame> metadata) {
emVideoView.start();
Log.d("metadata", "" + metadata);
}
});
im having this error when i'm playing my playlist....
04-21 15:01:26.573 950-1116/ E/ExoPlayerImplInternal: Internal track renderer error.
com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:262)
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:148)
at com.google.android.exoplayer.ExoPlayerImplInternal.incrementalPrepareInternal(ExoPlayerImplInternal.java:273)
at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:203)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
Caused by: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:208)
at com.google.android.exoplayer.upstream.DefaultUriDataSource.open(DefaultUriDataSource.java:133)
at com.google.android.exoplayer.chunk.DataChunk.load(DataChunk.java:85)
at com.google.android.exoplayer.upstream.Loader$LoadTask.run(Loader.java:222)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
04-21 15:01:26.577 950-950/ E/VC5: NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 1
What is your MediaDrmCallback
implementation? (what you are passing to setDrmCallback()
)
nothing for now, haven't setup the DRM yet...:/ i just need the player to read my Encrypted HLS.
Oh, my mistake; for some reason I was thinking you were trying to get DRM setup.
To make sure we are on the same page: You have a manifest url (*.m3u8
) that is not encrypted but contains the key for the encrypted *.ts
segments it references?
If that's the case you will need to provide a DataSource
that handles the AES 128 decryption. In ExoMedia 3.x
you will need to create a custom HlsRenderBuilder
and in 4.x
you can call ExoMedia.setHttpDataSourceFactoryProvider((userAgent, listener) -> {/*todo*/}));
Yes exactly!
The only way i can provide a DataSource in 3.x is with a HlsRenderBuilder, but the only function that is buildRenderers().
I tried to create an instance of RenderBuilder like this...
protected RenderBuilder getRendererBuilder(MediaSourceType renderType, Uri uri, String userAgent) {
switch (renderType) {
case HLS: //NOTE: this is your modified HlsRenderBuilder that customizes the HlsTrackSelector
return new HlsRenderBuilder(getContext().getApplicationContext(), userAgent, uri.toString());
case DASH:
return new DashRenderBuilder(getContext().getApplicationContext(), userAgent, uri.toString());
case SMOOTH_STREAM:
return new SmoothStreamRenderBuilder(getContext().getApplicationContext(), userAgent, uri.toString());
default:
return new RenderBuilder(getContext().getApplicationContext(), userAgent, uri.toString());
}
}
But i don't know where i can provide a DataSource in the HLS case... And this is where i get my m3u8 playlist
public void playVideo(final int livetrack) {
emVideoView.setVideoURI(Uri.parse(streamURL), getRendererBuilder(MediaSourceType.HLS, Uri.parse(streamURL), "UserAgent"));
}
Thank you so much for helping me btw!! :)
You will need to extend the HlsRenderBuilder
and override the createManifestDataSource
method.
Also, if you pass in a RenderBuilder
you can pass in null
for the URI to setVideoUri
public class AesHlsRenderBuilder extends HlsRenderBuilder {
@Override
protected UriDataSource createManifestDataSource(Context context, String userAgent) {
// TODO: Provide your authenticated data source
return new DefaultUriDataSource(context, userAgent);
}
@Override
protected DataSource createDataSource(Context context, TransferListener transferListener, String userAgent) {
// TODO: Provide your AES 128 decrypting data source
return new DefaultUriDataSource(context, transferListener, userAgent, true);
}
}
then
emVideoView.setVideoURI(null, new AesHlsRenderBuilder(getContext().getApplicationContext(), "UserAgent", Uri.parse(streamURL)));
still nothing when i put null for the URI to setVideoUri is it normal?
Also, none of my streams is working when i extend the HlsRenderBuilder even the one without a key in the playlist.
:(
Ok, my playlists with no keys are working if i put the setURI to my streams otherwise its not working...but the one with the key inside is not working..
04-24 13:44:25.603 3345-7721/ E/ExoPlayerImplInternal: Internal track renderer error.
com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:262)
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:148)
at com.google.android.exoplayer.ExoPlayerImplInternal.incrementalPrepareInternal(ExoPlayerImplInternal.java:273)
at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:203)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
Caused by: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:208)
at com.google.android.exoplayer.upstream.DefaultUriDataSource.open(DefaultUriDataSource.java:133)
at com.google.android.exoplayer.chunk.DataChunk.load(DataChunk.java:85)
at com.google.android.exoplayer.upstream.Loader$LoadTask.run(Loader.java:222)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
04-24 13:44:25.617 3345-3345/ E/VC5: NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = 1
Are you sure the manifest file doesn't require a session cookie (login)?
yes it needs but i can't reproduce it the connection msg...now i'm always getting this...
04-24 13:58:58.256 18085-18368/com.ebox.jaytv.androidtv_ebox W/AudioCapabilities: Unsupported mime audio/ac3
04-24 13:58:58.280 18085-18368/com.ebox.jaytv.androidtv_ebox I/VideoCapabilities: Unsupported profile 4 for video/mp4v-es
04-24 13:58:58.284 18085-18368/com.ebox.jaytv.androidtv_ebox W/VideoCapabilities: Unrecognized profile/level 0/0 for video/mpeg2
04-24 13:58:58.284 18085-18368/com.ebox.jaytv.androidtv_ebox W/VideoCapabilities: Unrecognized profile/level 0/2 for video/mpeg2
04-24 13:58:58.284 18085-18368/com.ebox.jaytv.androidtv_ebox W/VideoCapabilities: Unrecognized profile/level 0/3 for video/mpeg2
04-24 13:59:01.754 18085-18368/com.ebox.jaytv.androidtv_ebox E/ExoPlayerImplInternal: Internal track renderer error.
com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:262)
at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:148)
at com.google.android.exoplayer.ExoPlayerImplInternal.incrementalPrepareInternal(ExoPlayerImplInternal.java:273)
at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:203)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
Caused by: com.google.android.exoplayer.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 403
at com.google.android.exoplayer.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:208)
at com.google.android.exoplayer.upstream.DefaultUriDataSource.open(DefaultUriDataSource.java:133)
at com.google.android.exoplayer.chunk.DataChunk.load(DataChunk.java:85)
at com.google.android.exoplayer.upstream.Loader$LoadTask.run(Loader.java:222)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
That's the same message Response code: 403
. You will need to use an authenticated DataSource
as well
yes ur right, but i can't even reproduce the unable to login to mykey :(. It's always code: 403 now
one thing i don't understand with all this is why do i have to @Override createManifestDataSource when it looks like its only using for audio...
//Create the Sample Source to be used by the Audio Renderer
DataSource dataSourceAudio = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
Sorry to bug you like this with all my questions lol and thanks for your help again.
I've updated my example above.
There are 2 methods you will need to override; createManifestDataSource
which will need to be authenticated to access your *.m3u8
manifest file and createDataSource
which is what will be used for the .ts
segments. The createDataSource
is used for more than just audio though the line you posted looks like a bug in the 3.x
releases of ExoMedia as it should be DataSource dataSourceAudio = createDataSource(context, bandwidthMeter, userAgent);
It's probably worth mentioning that the official 4.0.0
release will most likely happen this week so it'll probably be more beneficial to just use the preview for now
Ok Perfect! i'll try and let you know if there's something! Thank you very very much!!
Just to let you know the difference inside my m3u8 playlist...
this is without a key and works well...
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:2061084
#EXTINF:9.009,
26_1_2061084.ts?m=1492714001
#EXTINF:9.009,
26_1_2061085.ts?m=1492714001
#EXTINF:9.009,
26_1_2061086.ts?m=1492714001
#EXTINF:9.009,
26_1_2061087.ts?m=1492714001
#EXTINF:9.009,
26_1_2061088.ts?m=1492714001
#EXTINF:9.009,
26_1_2061089.ts?m=1492714001
and this is the one with a AES Key
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:2078799
#EXT-X-KEY:METHOD=AES-128,URI="mykey"
#EXTINF:9.009,
248_1_2078799.ts?m=1492793899
#EXTINF:9.009,
248_1_2078800.ts?m=1492793899
#EXTINF:9.009,
248_1_2078801.ts?m=1492793899
#EXT-X-KEY:METHOD=AES-128,URI="mykey2aftersegment"
#EXTINF:9.009,
248_1_2078802.ts?m=1492793899
#EXTINF:9.009,
248_1_2078803.ts?m=1492793899
#EXTINF:9.009,
248_1_2078804.ts?m=1492793899
So the only thing, i want the player to recognize that there's a key and play the segment. There's no ecryption on the key its already decrypt but it's rotating every segment. That's my issue!
Hey Brian,
sorry to bug you again, I think i found a way to add headers thru HttpDataSource with setRequestProperty. Now my question, How i could add a new Datasource in my override method?
@Override
protected UriDataSource createManifestDataSource(Context context, String userAgent) {
// TODO: Provide your authenticated data source
return new DefaultUriDataSource(context, userAgent);
}
Thank you very much for you help again! :)
I'm not sure what you mean, you would just return your modified DataSource
from the createDataSource()
method for media playback or createManifestDataSource()
for manifest requests
Brian! i didnt want to open a new issue for that but i finally realize that i needed a session cookie to access to the manifest...:( my bad for all that! Could you give me a way to add the session cookie the player?
Thank you so much!!
The session cookie needs to be provided with the DataSource
. If you are using an OkHttpClient (e.g. App#L69) then you can add the cookie there
I'm so sorry, misunderstanding for what i shouldve have done in the first place, i need to pass a String header which are the String agent and also a Token.
This is a working example in iOS
let headers = ["Authorization": "JWT \(liveToken)", "User-Agent" : UserAgent]
self.playerAsset = AVURLAsset.init(url: urlStream! as URL, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
let item = AVPlayerItem(asset: self.playerAsset!)
self.playerItems.append(item)
As you can see, i'm adding a String token and the UserAgent...
Now in your library i added the String agent like this
@Override
protected DataSource createDataSource(Context context, TransferListener transferListener, String userAgent) {
// TODO: Provide your AES 128 decrypting data source
String userAgentLive = System.getProperty("http.agent");
return new DefaultUriDataSource(context, transferListener, userAgentLive, true);
}
the String Agent is working, but i have no clue how i could add my String Token inside that...:(
Thank you so much again for all your help!
Found it!!!
@Override
protected DataSource createDataSource(Context context, TransferListener transferListener, String userAgent) {
// TODO: Provide your AES 128 decrypting data source
String userAgentLive = System.getProperty("http.agent");
DefaultHttpDataSource defaultHttpDataSource = new DefaultHttpDataSource(userAgentLive,null);
defaultHttpDataSource.setRequestProperty("Authorization", "JWT " + streamAPI.getToken());
return defaultHttpDataSource;
}
But now, it keeps the lowest resolution :( It doesn't scale up..
Finally everything is working!!!!!!!!!!
For those you wants to add Headers into your encrypted playlist, just do like this :)
you add setRequestProperty with (name, value) of what you need!
@Override
protected DataSource createDataSource(Context context, TransferListener transferListener, String userAgent) {
DefaultHttpDataSource defaultHttpDataSource = new DefaultHttpDataSource(userAgent , null , transferListener);
defaultHttpDataSource.setRequestProperty("Authorization", "JWT " + streamAPI.getToken());
return defaultHttpDataSource;
}
Brian!
One more question about this, i need to update my streamAPI.getToken() that you see above after certain time. How could i just update the header while the player is playing?
Thank you so much again for your time.
Hi Brian,
Do you have any idea how i can tell to the player that there's a AES Key inside the playlist, so he can see it and play it if my user is auth?
EXT-X-KEY:METHOD=AES-128 -> (the key inside a m3u8 file)
Thank you!!