-
Notifications
You must be signed in to change notification settings - Fork 295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds load info to DrmSessionEventListeneronDrmKeysLoaded()
#1134
base: main
Are you sure you want to change the base?
Adds load info to DrmSessionEventListeneronDrmKeysLoaded()
#1134
Conversation
@@ -644,6 +651,19 @@ public void handleMessage(Message msg) { | |||
break; | |||
case MSG_KEYS: | |||
response = callback.executeKeyRequest(uuid, (KeyRequest) requestTask.request); | |||
if (currentKeyLoadInfo != null) { | |||
String requestUrl = ((KeyRequest) requestTask.request).getLicenseServerUrl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs some work, the knowledge of URL's and such is really all private to the MediaDrmCallback
so either we need to:
- Ignore this and provide vacuous URL's (which, unless the
KeyRequest
has the data this is already true) - add methods to
MediaDrmInfo
to fill in theLoadEventInfo
Our use case does not care about the URL, simply the load times and retries. So either way is good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, i wonder if there's another approach here where we add plumbing to pass a TransferListener
into HttpMediaDrmCallback
which then passes it on to the underlying DataSource
impl. That would give you access to the 'real' URL (including redirects handled inside the DataSource
as I flagged below).
Downsides of this approach:
- Not (easily) compatible with
LocalMediaDrmCallback
and otherMediaDrmCallback
impls. So what would we do if we didn't have an instance ofHttpMediaDrmCallback
? - It leaks the
DataSource
instance back out toDefaultDrmSession
(probably not that big a deal).
Our use case does not care about the URL, simply the load times and retries. So either way is good.
Or maybe we can skip URL for now then (in the spirit of YAGNI). That means we can't use LoadEventInfo
. What is the minimal set of fields you need? I feel we should include some way to identify which DRM keys are being loaded - maybe the @Nullable List<SchemeData>
that we pass into ExoMediaDrm.getKeyRequest
to generate the request bytes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@icbaker, thanks all great ideas... How about we just let the MediaDrmCallback
built the object, most of the info is already in the HttpMediaDrmCallback
, the other implementations MediaDrmCallback
don't have to change as the default (null return) makes sense for them.
/**
* Get the {@link LoadEventInfo} for the last executed request.
* This can be null if no load is performed to execute the request
*
* @return the {@link LoadEventInfo} or null if no load was performed
*/
@Nullable default LoadEventInfo getLastLoadEventInfo() { return null; }
With this, the LoadEventInfo
is easy to produce so it is completely internally consistent (redirect included).
BTW, a few WV License Proxies we work with use delayed re-directs to manage load (Apache Traffic Server's "thundering herd" for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See latest commit, adds this method. With the LoadEventInfo
produced in the object responsible for the actual load request it is completely consistant with what Loader
would produce.
@@ -699,6 +719,7 @@ private boolean maybeRetryRequest(Message originalMsg, MediaDrmCallbackException | |||
// The error is fatal. | |||
return false; | |||
} | |||
currentKeyLoadInfo.addRetryLoadRequest(loadEventInfo); | |||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be useful if you want the trails of LoadEventInfo
's including redirects in the path. The main LoadEventInfo
should include all the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will report all (most? any?) redirects - because at least some types of redirect will be 'silently' handled inside the HttpDataSource
implementation underneath HttpMediaDrmCallback
, e.g.
media/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java
Line 594 in 0480bc3
url = handleRedirect(url, location, dataSpec); |
So I think we need to be quite careful we can usefully describe what this field holds, because i think it will only show retries that happened at this DefaultDrmSession
layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is certainly true for DefaultHttpDataSource
, not sure for Cronet
. something we'll look into, as our servers are using this as a method to hold off clients when they are loaded. Also, the POST data is retained in the HttpMediaDrmCallback
so not sure if the POST redirect in the stack will do this correctly.
We'll have a look and let you know, definitely preference would be to do it in the HttpMediaDrmCallback
as the default redirect following code in lower layers usually is much higher than 5 tries.
FWIW this is the same as how other Loader
/ Loadable
implementations handle this.
Definitely something to look into.
*/ | ||
default void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {} | ||
default void onDrmKeysLoaded( | ||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, @Nullable KeyLoadInfo keyLoadInfo) {} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
followed the pattern to deprecate then call the deprecated method.
@@ -489,6 +492,9 @@ private long getLicenseDurationRemainingSec() { | |||
|
|||
private void postKeyRequest(byte[] scope, int type, boolean allowRetry) { | |||
try { | |||
if (type == ExoMediaDrm.KEY_TYPE_STREAMING) { | |||
currentKeyLoadInfo = new KeyLoadInfo(); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably just check for mode != DefaultDrmSessionManager.MODE_RELEASE
. That way we cover download requests too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think we should probably include release calls too, since they can theoretically also take time right?
In any case, we should indicate what type of request is being reported in KeyLoadInfo
. But not sure exactly how to do that. @Mode
is on DefaultDrmSessionManager
so we can't really reference it from a 'generic' DRM context. Maybe @ExoMediaDrm.KeyType
would work instead (though it feels kinda low-level).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that sounds like a good idea. We can start a list for what to add to KeyLoadInfo
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done in the latest commit, except for adding the KeyLoadInfo
to the drmKeysRemoved()
method. I'm fine with doing this too, however then we should rename KeyLoadInfo
to KeyRequestInfo
. I'm all for the YAGNI thinking too ;-)
But, seems most of the work will be rebasing all the files open for this change so it is probably now or never.
I'll follow your lead on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with doing this too, however then we should rename
KeyLoadInfo
toKeyRequestInfo
Yeah, I think we should do this rename and include key releasing - I agree it's going to be easier to do this now than later.
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/KeyLoadInfo.java
Outdated
Show resolved
Hide resolved
@@ -489,6 +492,9 @@ private long getLicenseDurationRemainingSec() { | |||
|
|||
private void postKeyRequest(byte[] scope, int type, boolean allowRetry) { | |||
try { | |||
if (type == ExoMediaDrm.KEY_TYPE_STREAMING) { | |||
currentKeyLoadInfo = new KeyLoadInfo(); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think we should probably include release calls too, since they can theoretically also take time right?
In any case, we should indicate what type of request is being reported in KeyLoadInfo
. But not sure exactly how to do that. @Mode
is on DefaultDrmSessionManager
so we can't really reference it from a 'generic' DRM context. Maybe @ExoMediaDrm.KeyType
would work instead (though it feels kinda low-level).
@@ -699,6 +719,7 @@ private boolean maybeRetryRequest(Message originalMsg, MediaDrmCallbackException | |||
// The error is fatal. | |||
return false; | |||
} | |||
currentKeyLoadInfo.addRetryLoadRequest(loadEventInfo); | |||
synchronized (this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will report all (most? any?) redirects - because at least some types of redirect will be 'silently' handled inside the HttpDataSource
implementation underneath HttpMediaDrmCallback
, e.g.
media/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java
Line 594 in 0480bc3
url = handleRedirect(url, location, dataSpec); |
So I think we need to be quite careful we can usefully describe what this field holds, because i think it will only show retries that happened at this DefaultDrmSession
layer.
new LoadEventInfo( | ||
requestTask.taskId, | ||
new DataSpec.Builder().setUri(requestUrl).build(), | ||
Uri.parse(requestUrl), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also meant to the be the URI after redirects, so we need to be careful here too.
@@ -644,6 +651,19 @@ public void handleMessage(Message msg) { | |||
break; | |||
case MSG_KEYS: | |||
response = callback.executeKeyRequest(uuid, (KeyRequest) requestTask.request); | |||
if (currentKeyLoadInfo != null) { | |||
String requestUrl = ((KeyRequest) requestTask.request).getLicenseServerUrl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, i wonder if there's another approach here where we add plumbing to pass a TransferListener
into HttpMediaDrmCallback
which then passes it on to the underlying DataSource
impl. That would give you access to the 'real' URL (including redirects handled inside the DataSource
as I flagged below).
Downsides of this approach:
- Not (easily) compatible with
LocalMediaDrmCallback
and otherMediaDrmCallback
impls. So what would we do if we didn't have an instance ofHttpMediaDrmCallback
? - It leaks the
DataSource
instance back out toDefaultDrmSession
(probably not that big a deal).
Our use case does not care about the URL, simply the load times and retries. So either way is good.
Or maybe we can skip URL for now then (in the spirit of YAGNI). That means we can't use LoadEventInfo
. What is the minimal set of fields you need? I feel we should include some way to identify which DRM keys are being loaded - maybe the @Nullable List<SchemeData>
that we pass into ExoMediaDrm.getKeyRequest
to generate the request bytes?
Unless we want to go for renaming I'm good either way. |
d835d9b
to
d245f71
Compare
SystemClock.elapsedRealtime(), | ||
/* loadDurationMs= */ SystemClock.elapsedRealtime() - startTimeMs, | ||
((byte []) response).length); | ||
return response; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating the LoadEventInfo
in the actual HttpMediaDrmCallback
addresses all the todo's with its correctness issues. Also note now the loadDurationMs
does not include any queuing time in the RequestHandler
(so it's pure network and proxy overhead measured).
Lastly, DataSource.getLastOpenedUri()
should get the redirected URI (if any), at least this is the contract for DataSource.getUri()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks generally good, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry this took me so long to get back to, I'll try harder not to leave it so long on the next round of comments!
byte[] response = Util.toByteArray(inputStream); | ||
lastLoadEventInfo = | ||
new LoadEventInfo( | ||
-1, // note this is replaced with the actual taskId from the request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about plumbing an (optional) taskId
into ExoMediaDrm.KeyRequest
and ExoMediaDrm.ProvisionRequest
? Then it would be available here and you wouldn't need to use this placeholder value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is a bit ugly this way... I stopped short because changing the MediaDrmCallback.executeProvisionRequest()
signature, as it has lots of implementations (unless we add new methods, which is also kind of clumsy feeling. What if we added the value to ProvisionRequest
and KeyRequest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@icbaker this may fall into the category of perfect is the enemy of done category, but yes... It looks distatesful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we added the value to
ProvisionRequest
andKeyRequest
This is what I originally intended, but I just had a play around with this and it would require adding taskId
to ExoMediaDrm.getKeyRequest
, so it also has interface evolution problems (and this would also be the first piece of KeyRequest
that isn't derived directly (or slightly indirectly) from the framework MediaDrm.getKeyRequest
.
I agree that your current approach is probably the least bad option here.
However unfortunately while thinking about this I spotted a different/bigger problem which I'll raise in a new comment.
SystemClock.elapsedRealtime(), | ||
/* loadDurationMs= */ SystemClock.elapsedRealtime() - startTimeMs, | ||
((byte []) response).length); | ||
return response; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks generally good, thanks
|
||
/** The DRM {@link SchemeData} that identifes the loaded key | ||
*/ | ||
public final List<SchemeData> schemeDatas; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please make these two ImmutableList
and take defensive copies in the constructor below
You can use ImmutableList.Builder
for retriedLoadRequests
in the builder, and then ImmutableList.copyOf
in the constructor will be a no-op because it does an instanceof ImmutableList
check first.
*/ | ||
public final List<SchemeData> schemeDatas; | ||
|
||
public KeyLoadInfo(Builder builder) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stylistic/consistency nit: Make this private, and add an explicit public Builder buildUpon()
method if needed (though I don't think you do need it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, made it private in the renamed class. Sorry for this, should have fixed it first then renamed rather than rename then fix.
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/MediaDrmCallback.java
Outdated
Show resolved
Hide resolved
@@ -489,6 +492,9 @@ private long getLicenseDurationRemainingSec() { | |||
|
|||
private void postKeyRequest(byte[] scope, int type, boolean allowRetry) { | |||
try { | |||
if (type == ExoMediaDrm.KEY_TYPE_STREAMING) { | |||
currentKeyLoadInfo = new KeyLoadInfo(); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with doing this too, however then we should rename
KeyLoadInfo
toKeyRequestInfo
Yeah, I think we should do this rename and include key releasing - I agree it's going to be easier to do this now than later.
No problem, we both have day jobs too!
I’ve assigned some tasks here internally to move our most important pull
requests over from ExoPlayer to AndroidX
…On Tue, Feb 27, 2024 at 1:05 AM Ian Baker ***@***.***> wrote:
***@***.**** requested changes on this pull request.
------------------------------
In
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/KeyLoadInfo.java
<#1134 (comment)>:
> @@ -0,0 +1,26 @@
+package androidx.media3.exoplayer.drm;
+
+import androidx.media3.exoplayer.source.LoadEventInfo;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates info for the sequence of load requests ***@***.*** LoadEventInfo}, which were required
+ * to complete loading a DRM key
+ */
+public class KeyLoadInfo {
I think we should make this immutable. If we need the addRetry
accumulation logic here then it should be part of a KeyLoadInfo.Builder,
but maybe we don't need that and can just accumulate in a local in
DefaultDrmSession.
------------------------------
In
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java
<#1134 (comment)>:
> @@ -644,6 +651,19 @@ public void handleMessage(Message msg) {
break;
case MSG_KEYS:
response = callback.executeKeyRequest(uuid, (KeyRequest) requestTask.request);
+ if (currentKeyLoadInfo != null) {
+ String requestUrl = ((KeyRequest) requestTask.request).getLicenseServerUrl();
+ LoadEventInfo loadEventInfo =
+ new LoadEventInfo(
+ requestTask.taskId,
+ new DataSpec.Builder().setUri(requestUrl).build(),
+ Uri.parse(requestUrl),
This is also meant to the be the URI after redirects, so we need to be
careful here too.
------------------------------
In
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java
<#1134 (comment)>:
> @@ -644,6 +651,19 @@ public void handleMessage(Message msg) {
break;
case MSG_KEYS:
response = callback.executeKeyRequest(uuid, (KeyRequest) requestTask.request);
+ if (currentKeyLoadInfo != null) {
+ String requestUrl = ((KeyRequest) requestTask.request).getLicenseServerUrl();
Hmmm, i wonder if there's another approach here where we add plumbing to
pass a TransferListener into HttpMediaDrmCallback which then passes it on
to the underlying DataSource impl. That would give you access to the
'real' URL (including redirects handled inside the DataSource as I
flagged below).
Downsides of this approach:
1. Not (easily) compatible with LocalMediaDrmCallback and other
MediaDrmCallback impls. So what would we do if we didn't have an
instance of HttpMediaDrmCallback?
2. It leaks the DataSource instance back out to DefaultDrmSession
(probably not that big a deal).
------------------------------
Our use case does not care about the URL, simply the load times and
retries. So either way is good.
Or maybe we can skip URL for now then (in the spirit of YAGNI
<https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it>). That means
we can't use LoadEventInfo. What is the minimal set of fields you need? I
feel we should include some way to identify *which* DRM keys are being
loaded - maybe the @nullable List<SchemeData> that we pass into
ExoMediaDrm.getKeyRequest to generate the request bytes?
------------------------------
In
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java
<#1134 (comment)>:
> @@ -699,6 +719,7 @@ private boolean maybeRetryRequest(Message originalMsg, MediaDrmCallbackException
// The error is fatal.
return false;
}
+ currentKeyLoadInfo.addRetryLoadRequest(loadEventInfo);
synchronized (this) {
I don't think this will report all (most? any?) redirects - because at
least some types of redirect will be 'silently' handled inside the
HttpDataSource implementation underneath HttpMediaDrmCallback, e.g.
https://github.com/androidx/media/blob/0480bc31a8fbec2aed354bee14e8f15e7fe9012a/libraries/datasource/src/main/java/androidx/media3/datasource/DefaultHttpDataSource.java#L594
So I think we need to be quite careful we can usefully describe what this
field holds, because i think it will only show retries that happened at
this DefaultDrmSession layer.
------------------------------
In
libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSession.java
<#1134 (comment)>:
> @@ -489,6 +492,9 @@ private long getLicenseDurationRemainingSec() {
private void postKeyRequest(byte[] scope, int type, boolean allowRetry) {
try {
+ if (type == ExoMediaDrm.KEY_TYPE_STREAMING) {
+ currentKeyLoadInfo = new KeyLoadInfo();
+ }
Actually I think we should probably include release calls too, since they
can theoretically also take time right?
In any case, we should indicate what type of request is being reported in
KeyLoadInfo. But not sure exactly how to do that. @mode is on
DefaultDrmSessionManager so we can't really reference it from a 'generic'
DRM context. Maybe @ExoMediaDrm.KeyType would work instead (though it
feels kinda low-level).
—
Reply to this email directly, view it on GitHub
<#1134 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADBF6GK7DWFHTJHJRPHCJDYVWOVZAVCNFSM6AAAAABD24S3JCVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMYTSMBSHA3DAMZTGU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
I'm on vacation next week @icbaker so it will take a bit to get to these changes. Only one I need your input on is best path for the |
This should cover the comments: 1. androidx#1134 (comment) 2. androidx#1134 (comment) Also, fixed lint issues and deaI will the nullability of `schemeDatas` note will need addtional idenfitying info in KeyRequestInfo to support offline
public Builder(List<SchemeData> schemeDatas) { | ||
this.schemeDatas = schemeDatas; | ||
retriedLoadRequests = new ArrayList<>(); | ||
loadEventInfo = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in the renamed version handled the case where schemeDatas
can be null (the caller has a Nullable member here).
* Encapsulates info for the sequence of load requests ({@link LoadEventInfo}, which were required | ||
* to complete loading a DRM key | ||
*/ | ||
public class KeyRequestInfo { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor renamed, in prep for other license requests beside Key load.
@MonotonicNonNull private LoadEventInfo loadEventInfo; | ||
private final List<LoadEventInfo> retriedLoadRequests; | ||
@Nullable private final List<SchemeData> schemeDatas; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clean up the lint, schemeDatas
is nullable for offline keys (we may want to add an additional identifying member for this case).
loadEventInfo
is non-null after the main request is indemnified (which must be before build()
This is a first pass at what we talked about in issue androidx#1001 * added the wrapper class `KeyLoadInfo` to embody the requests required to fetch the key * added `KeyLoadInfo` to the `DrmSessionEventListener.drmKeysLoaded()` method * Deprecated `DrmSessionEventListener.onDrmKeysLoaded(int, MediaPeriodId)` * added new `onDrmKeysLoaded()` with the `KeyLoadInfo`, setup so both method are called Next steps are to add it to `AnalyticsListener` and update any broken test cases possibly add some new ones
Addressed comments from pull request: 1. Converted `KeyLoadInfo` to builder pattern, made immutable 2. Added `SchemaData` for the loaded key to the info 3. Added method `MediaDrmCallback.getLastLoadEventInfo()`, allows the called object to create a true to spec `LoadEventInfo` object
This is just renaming. I will squash this commit out ultimately, but making it explict for pull request review purpose. Only other change was making the constructor private
This should cover the comments: 1. androidx#1134 (comment) 2. androidx#1134 (comment) Also, fixed lint issues and deaI will the nullability of `schemeDatas` note will need addtional idenfitying info in KeyRequestInfo to support offline
9d27d45
to
180b20e
Compare
@@ -666,6 +672,12 @@ public void handleMessage(Message msg) { | |||
break; | |||
case MSG_KEYS: | |||
response = callback.executeKeyRequest(uuid, (KeyRequest) requestTask.request); | |||
if (currentKeyRequestInfo != null) { | |||
LoadEventInfo loadEventInfo = callback.getLastLoadEventInfo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately because of the threading model used here, I don't think we can always guarantee that getLastLoadEventInfo()
here corresponds to the request made above on L674.
This can happen because each DefaultDrmSession
instance has its own request thread, but two instances can easily share the same MediaDrmCallback
instance.
Concretely this can happen during a fairly normal playback if a separate DefaultDrmSession
is opened for audio and video (encrypted with different keys I guess?).
So then T1 can execute callback.executeKeyRequest
then pause while T2 executes callback.executeKeyRequest
before T1 then calls getLastLoadEventInfo()
and gets the request from T2.
To resolve this I think its best to tightly couple each request to the reported LoadEventInfo
, so we can keep MediaDrmCallback
impls 'stateless' so it doesn't matter if they're invoked on multiple threads at the same time.
Which I think leads us to either:
- Pass
TransferListener
intoMediaDrmCallback.executeXXXRequest
(my previous suggestion in Adds load info toDrmSessionEventListeneronDrmKeysLoaded()
#1134 (comment)) - Enrich the return type of
MediaDrmCallback.executeKeyRequest
toKeyResponse
, which hasbyte[] data
field and optional@Nullable LoadEventInfo
too.- This requires also passing
taskId
in somehow, either insideKeyRequest
, or as a separater parameter (as we discussed: Adds load info toDrmSessionEventListeneronDrmKeysLoaded()
#1134 (comment)) - both of which are a bit disruptive. I think we shouldn't passtaskId
down intoExoMediaDrm
, but it does seem to make sense to pass it insideKeyRequest
. So maybe we can do that by adding aKeyRequest.copyWithTaskId
method? I'll push some commits that sketch that idea.
- This requires also passing
(1) has the issues I mentioned before with 'this listener might never be called if it's not supoprted by this MediaDrmCallback
impl', but I think we might just have to document that limitation (so e.g. a caller must never block waiting for onTransferEnd
unless they know more specifics about the MediaDrmCallback
type they're dealing with. It also mixes up abstractions a bit, feels like maybe we shouldn't be naively re-using the datasource.TransferListener
type in a not-necessarily-DataSource
context.
(1) also means moving the LoadEventInfo
instantiation back into DefaultDrmSession
instead of HttpMediaDrmCallback
.
On the other hand, (2) feels a bit weird because we end up returning (possibly) several 'time-based' even things all in one go when the request is fully completed. But it glues more easily into your existing changes to DrmSessionEventListener
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately because of the threading model used here, I don't think we can always guarantee that
getLastLoadEventInfo()
here corresponds to the request made above on L674.This can happen because each
DefaultDrmSession
instance has its own request thread, but two instances can easily share the sameMediaDrmCallback
instance.
Ah, great catch... I feel pretty stupid, given that I just solved this same problem with my custom LoadErrorHandlingPolicy
I wound up keeping the state object key'd by the load taskId
When I get back from vacation next week I'll move my original commits to a branch and download your changes (the force push would make a mess if I just pulled it) so I can see what is going on and have a closer look at your suggestions. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the force push would make a mess if I just pulled it
Ah sorry, the force push was part of me planning to send this PR for review internally before I realised this larger issue. I didn't push any 'meaningful' changes though, just formatted the changes in a commit, then rebased on top of the latest main
commit. You should be able to force the branch pointer back to the 'old' version of ebec293 which I think is 9d27d45a65, then do your own force push to 'take back control' of the PR.
Probably something like this (though I'm no git expert, so there may be a better way to achieve this!):
$ git branch --force p-add-loadinfo-to-ondrmkeysloaded 9d27d45a65
$ git push --force
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately because of the threading model used here, I don't think we can always guarantee that
getLastLoadEventInfo()
here corresponds to the request made above on L674.This can happen because each
DefaultDrmSession
instance has its own request thread, but two instances can easily share the sameMediaDrmCallback
instance.
Yes, stashing the LoadEventInfo
anywhere other than the RequestTask
, which is new'd for each request will violate the statelessness of whatever it is stuffed into. There is no clean way out of just doing this.
To resolve this I think its best to tightly couple each request to the reported
LoadEventInfo
, so we can keepMediaDrmCallback
impls 'stateless' so it doesn't matter if they're invoked on multiple threads at the same time.Which I think leads us to either:
- Pass
TransferListener
intoMediaDrmCallback.executeXXXRequest
(my previous suggestion in Adds load info toDrmSessionEventListeneronDrmKeysLoaded()
#1134 (comment))- Enrich the return type of
MediaDrmCallback.executeKeyRequest
toKeyResponse
, which hasbyte[] data
field and optional@Nullable LoadEventInfo
too.
also, we could
- Add to or lift a common base class from
ProvisionRequest
andKeyRequest
that includes theLoadEventInfo
as a member.
Number 3 avoids a signature change for executeXXXRequest
, but it seems uglier than your number 2, which is pretty strait forward.
So I would vote for your number 2. I can do it or feel free to branch the current code and make a pull request onto my repo with your idea, I can build and test it. Branches and pull request are best way to collaborate (once we agree the basic approach), but no worries about the force push and thanks for re-basing to keep up with head of main
(this is a big set of changes to keep on the side for long)
Thanks again for catching this.
This is a first pass at what we talked about in issue #1001
KeyLoadInfo
to embody the requests required to fetch the keyKeyLoadInfo
to theDrmSessionEventListener.drmKeysLoaded()
methodDrmSessionEventListener.onDrmKeysLoaded(int, MediaPeriodId)
onDrmKeysLoaded()
with theKeyLoadInfo
, setup so both method are calledNext steps are to add it to
AnalyticsListener
and update any broken test cases possibly add some new ones