Skip to content

Commit

Permalink
allow HTTP request headers to be customized for each video in queue
Browse files Browse the repository at this point in the history
* POST data sent in requests to "/play" and "/queue" API endpoints:
  - now recognizes the keys:
    * req-header
    * drm-header
  - ex:
      req-header: Referer: http://example.com/videos.html
      req-header: Origin:  http://example.com
      drm-header: Authorization: Bearer <TOKEN>
* Intents
  - which are:
    * used to start StartNetworkingServiceActivity
    * and forwarded to NetworkingService
  - can now include the extras:
    * reqHeader
    * drmHeader
  - which can be data type:
    * String[]
    * String
  - and each String is formatted:
    * "header-name: header-value"
* POST data sent in requests to "/start-activity" API endpoint:
  - can configure these Intent extras
    * extra-reqHeader
    * extra-drmHeader
  - ex:
      extra-reqHeader: Referer: http://example.com/videos.html
      extra-reqHeader: Origin:  http://example.com
      extra-drmHeader: Authorization: Bearer <TOKEN>

notes:
* the ability to configure "referer" is now redundant
  - keys:
    * POST data: referer
    * Intent extra: referUrl
  - this feature is legacy, and kept to avoid breaking userspace
* DRM headers are only used when
  both DRM scheme and DRM license server URL are provided
* ExoAirPlayer is configured with a default "User-Agent"
  - which impersonates a recent version of Chrome for Windows desktop
  - custom request headers can override this default value
  • Loading branch information
warren-bank committed Oct 20, 2021
1 parent 2990061 commit 570dbe7
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 108 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,14 @@ __extended APIs:__
extra-stopPos:
extra-drmScheme: widevine
extra-drmUrl: http://widevine.example.com/
extra-reqHeader: Referer: http://example.com/videos.html
extra-reqHeader: Origin: http://example.com
extra-reqHeader: X-Requested-With: XMLHttpRequest
extra-reqHeader: User-Agent: Chrome/90
extra-drmHeader: Authorization: Bearer xxxxx
extra-drmHeader: Cookie: token=xxxxx; sessionID=yyyyy
'

curl --silent -X POST \
Expand All @@ -433,6 +441,8 @@ __extended APIs:__
* _content-location_
* _caption-location_
* _referer_
* _req-header_
- use key on multiple lines to declare more than one value
* _start-position_
* _stop-position_
* _drm-license-scheme_
Expand All @@ -441,6 +451,8 @@ __extended APIs:__
* _clearkey_
* _playready_
* _drm-license-server_
* _drm-header_
- use key on multiple lines to declare more than one value
- keys required:
* _content-location_
* POST data sent in requests to `/start-activity` API endpoint:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@
<data android:mimeType="*/avi"/>
<data android:mimeType="*/mkv"/>
<data android:mimeType="application/3gpp*"/>
<data android:mimeType="application/dash+xml"/>
<data android:mimeType="application/mp4"/>
<data android:mimeType="application/mpeg*"/>
<data android:mimeType="application/ogg"/>
<data android:mimeType="application/vnd.3gp*"/>
<data android:mimeType="application/vnd.apple.mpegurl"/>
<data android:mimeType="application/vnd.ms-sstr+xml"/>
<data android:mimeType="application/x-extension-mp4"/>
<data android:mimeType="application/x-flac"/>
<data android:mimeType="application/x-matroska"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ public class Constant {
public static final String PlayURL = "playUrl";
public static final String CaptionURL = "textUrl";
public static final String RefererURL = "referUrl";
public static final String ReqHeader = "reqHeader";
public static final String Start_Pos = "startPos";
public static final String Stop_Pos = "stopPos";
public static final String DRM_Scheme = "drmScheme";
public static final String DRM_URL = "drmUrl";
public static final String DRM_Header = "drmHeader";

public static String getServerInfoResponse(String mac) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
Expand Down Expand Up @@ -221,4 +223,10 @@ public interface Status {
public static final String Status_load = "loading";
}

public interface Video_Source_Map {
public static final String DATA = "data";
public static final String REQ_HEADERS = "req-header";
public static final String DRM_HEADERS = "drm-header";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/** Manages ExoPlayer and an internal media queue */
public final class PlayerManager implements EventListener {
Expand Down Expand Up @@ -162,9 +163,10 @@ public void onAudioSessionIdChanged(int audioSessionId) {
}

String userAgent = context.getResources().getString(R.string.user_agent);
this.httpDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent);
this.rawDataSourceFactory = new DefaultDataSourceFactory(context, userAgent);
this.loadErrorHandlingPolicy = new MyLoadErrorHandlingPolicy();
this.httpDataSourceFactory = new DefaultHttpDataSourceFactory(userAgent);
this.rawDataSourceFactory = new DefaultDataSourceFactory(context, userAgent);
this.loadErrorHandlingPolicy = new MyLoadErrorHandlingPolicy();
VideoSource.DEFAULT_USER_AGENT = userAgent;

this.currentItemIndex = C.INDEX_UNSET;
this.handler = new Handler(Looper.getMainLooper());
Expand Down Expand Up @@ -311,12 +313,14 @@ public void addItem(
String uri,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server
String drm_license_server,
HashMap<String, String> drmHeadersMap
) {
addItem(uri, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server, /* remove_previous_items= */ false);
addItem(uri, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap, /* remove_previous_items= */ false);
}

/**
Expand All @@ -325,23 +329,27 @@ public void addItem(
* @param uri The URL to a video file or stream.
* @param caption The URL to a file containing text captions (srt or vtt).
* @param referer The URL to include in the 'Referer' HTTP header of requests to retrieve the video file or stream.
* @param reqHeadersMap Map of HTTP headers to include in requests to retrieve the video file or stream.
* @param startPosition The position at which to start playback within the video file or (non-live) stream. When value < 1.0 and stopPosition < value, it is interpreted to mean a percentage of the total video length. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
* @param stopPosition The position at which to stop playback within the video file or (non-live) stream. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
* @param drm_scheme The DRM scheme; value in: ["widevine","playready","clearkey"]
* @param drm_license_server The URL to obtain DRM license keys.
* @param drmHeadersMap Map of HTTP headers to include in requests to retrieve the DRM license keys.
* @param remove_previous_items A boolean flag to indicate whether all previous items in queue should be removed after new items have been appended.
*/
public void addItem(
String uri,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server,
HashMap<String, String> drmHeadersMap,
boolean remove_previous_items
) {
VideoSource sample = VideoSource.createVideoSource(uri, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server);
VideoSource sample = VideoSource.createVideoSource(uri, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap);
addItem(sample, remove_previous_items);
}

Expand Down Expand Up @@ -377,12 +385,14 @@ public void addItems(
String[] uris,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server
String drm_license_server,
HashMap<String, String> drmHeadersMap
) {
addItems(uris, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server, /* remove_previous_items= */ false);
addItems(uris, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap, /* remove_previous_items= */ false);
}

/**
Expand All @@ -391,20 +401,24 @@ public void addItems(
* @param uris Array of URLs to video files or streams.
* @param caption The URL to a file containing text captions (srt or vtt).
* @param referer The URL to include in the 'Referer' HTTP header of requests to retrieve the video file or stream.
* @param reqHeadersMap Map of HTTP headers to include in requests to retrieve the video file or stream.
* @param startPosition The position at which to start playback within the video file or (non-live) stream. When value < 1.0 and stopPosition < value, it is interpreted to mean a percentage of the total video length. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
* @param stopPosition The position at which to stop playback within the video file or (non-live) stream. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
* @param drm_scheme The DRM scheme; value in: ["widevine","playready","clearkey"]
* @param drm_license_server The URL to obtain DRM license keys.
* @param drmHeadersMap Map of HTTP headers to include in requests to retrieve the DRM license keys.
* @param remove_previous_items A boolean flag to indicate whether all previous items in queue should be removed after new items have been appended.
*/
public void addItems(
String[] uris,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server,
HashMap<String, String> drmHeadersMap,
boolean remove_previous_items
) {
VideoSource[] samples = new VideoSource[uris.length];
Expand All @@ -414,8 +428,8 @@ public void addItems(
for (int i=0; i < uris.length; i++) {
uri = uris[i];
sample = (i == 0)
? VideoSource.createVideoSource(uri, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server)
: VideoSource.createVideoSource(uri, null, referer, -1f, -1f, drm_scheme, drm_license_server)
? VideoSource.createVideoSource(uri, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap)
: VideoSource.createVideoSource(uri, null, referer, reqHeadersMap, -1f, -1f, drm_scheme, drm_license_server, drmHeadersMap)
;
samples[i] = sample;
}
Expand Down Expand Up @@ -571,20 +585,19 @@ public boolean moveItem(int fromIndex, int toIndex) {

/**
* Clears the media queue and adds the specified {@link VideoSource}.
*
* @param uri The URL to a video file or stream.
* @param startPosition The position at which to start playback within the video file or (non-live) stream. When value < 1.0, it is interpreted to mean a percentage of the total video length. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
*/
public void AirPlay_play(
String uri,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server
String drm_license_server,
HashMap<String, String> drmHeadersMap
) {
addItem(uri, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server, /* remove_previous_items= */ true);
addItem(uri, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap, /* remove_previous_items= */ true);
}

/**
Expand Down Expand Up @@ -668,21 +681,19 @@ public void AirPlay_stop(boolean play_animation) {

/**
* Appends {@link VideoSource} to the media queue.
*
* @param uri The URL to a video file or stream.
* @param referer The URL to include in the 'Referer' HTTP header of requests to retrieve the video file or stream.
* @param startPosition The position at which to start playback within the video file or (non-live) stream. When value < 1.0, it is interpreted to mean a percentage of the total video length. When value >= 1.0, it is interpreted to mean a fixed offset in seconds.
*/
public void AirPlay_queue(
String uri,
String caption,
String referer,
HashMap<String, String> reqHeadersMap,
float startPosition,
float stopPosition,
String drm_scheme,
String drm_license_server
String drm_license_server,
HashMap<String, String> drmHeadersMap
) {
addItem(uri, caption, referer, startPosition, stopPosition, drm_scheme, drm_license_server);
addItem(uri, caption, referer, reqHeadersMap, startPosition, stopPosition, drm_scheme, drm_license_server, drmHeadersMap);
}

/**
Expand Down Expand Up @@ -1083,43 +1094,17 @@ else if (Float.compare(position, 1.0f) >= 0) { // if (position >= 1.0f)
}

private void setHttpRequestHeaders(int currentItemIndex) {
if (httpDataSourceFactory == null) return;

VideoSource sample = getItem(currentItemIndex);
if (sample == null) return;
if (ExternalStorageUtils.isFileUri(sample.uri)) return;

if (sample.referer != null) {
Uri referer = Uri.parse(sample.referer);
String origin = referer.getScheme() + "://" + referer.getAuthority();

setHttpRequestHeader("origin", origin);
setHttpRequestHeader("referer", sample.referer);
}
else {
setHttpRequestHeader("origin", null);
setHttpRequestHeader("referer", null);
}

switch (sample.uri_mimeType) {
case "video/mp4":
case "video/mpeg":
case "video/x-mkv":
case "video/x-msvideo":
setHttpRequestHeader("range", "bytes=0-");
break;
default:
setHttpRequestHeader("range", null);
if (sample.reqHeadersMap != null) {
httpDataSourceFactory.setDefaultRequestProperties(sample.reqHeadersMap);
}
}

private void setHttpRequestHeader(String name, String value) {
if (httpDataSourceFactory == null) return;

if (value == null)
httpDataSourceFactory.getDefaultRequestProperties().remove(name);
else
httpDataSourceFactory.getDefaultRequestProperties().set(name, value);
}

private MediaSource buildMediaSource(VideoSource sample) {
MediaSource video = buildUriMediaSource(sample);
ArrayList<MediaSource> captions = buildCaptionMediaSources(sample);
Expand Down
Loading

0 comments on commit 570dbe7

Please sign in to comment.