Merge pull request #4 from AnonymusRaccoon/dev

Reworking the transmuxer to use HLS (it is really fast now).
This commit is contained in:
Zoe Roux 2019-12-03 00:45:13 +01:00
commit 3f284fad42
8 changed files with 58 additions and 54 deletions

View File

@ -18,6 +18,7 @@ int transmux(const char *path, const char *out_path, float *playable_duration)
int *stream_map; int *stream_map;
int stream_count; int stream_count;
int ret = 0; int ret = 0;
std::string seg_path = ((std::string)out_path).substr(0, strrchr(out_path, '/') - out_path).append("/segments/");
*playable_duration = 0; *playable_duration = 0;
if (open_input_context(&in_ctx, path) != 0) if (open_input_context(&in_ctx, path) != 0)
@ -54,10 +55,10 @@ int transmux(const char *path, const char *out_path, float *playable_duration)
} }
av_dump_format(out_ctx, 0, out_path, true); av_dump_format(out_ctx, 0, out_path, true);
std::filesystem::create_directory(seg_path);
std::filesystem::create_directory(((std::string)out_path).substr(0, strrchr(out_path, '/') - out_path).append("/dash/")); av_dict_set(&options, "hls_segment_filename", seg_path.append("%v-%03d.ts").c_str(), 0);
av_dict_set(&options, "init_seg_name", "dash/init-stream$RepresentationID$.m4s", 0); av_dict_set(&options, "hls_base_url", "segment/", 0);
av_dict_set(&options, "media_seg_name", "dash/chunk-stream$RepresentationID$-$Number%05d$.m4s", 0); av_dict_set(&options, "hls_list_size", "0", 0);
av_dict_set(&options, "streaming", "1", 0); av_dict_set(&options, "streaming", "1", 0);
if (open_output_file_for_write(out_ctx, out_path, &options) != 0) if (open_output_file_for_write(out_ctx, out_path, &options) != 0)

View File

@ -32,7 +32,7 @@
"scripts": [ "scripts": [
"./node_modules/jquery/dist/jquery.min.js", "./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js", "./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"./node_modules/dashjs/dist/dash.all.min.js", "./node_modules/hls.js/dist/hls.js",
"./src/libraries/subtitles.js" "./src/libraries/subtitles.js"
] ]
}, },

View File

@ -1270,6 +1270,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/hls.js": {
"version": "0.12.5",
"resolved": "https://registry.npmjs.org/@types/hls.js/-/hls.js-0.12.5.tgz",
"integrity": "sha512-UWNROsxKxW1yASacUZzvjGZwS1xGp1gN3Yyd6XWMpqr8HLiYepVWZLxsyfPNsgSJrQ1oyz03tV7y4dS5nlSOaw==",
"dev": true
},
"@types/jasmine": { "@types/jasmine": {
"version": "3.3.16", "version": "3.3.16",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.16.tgz", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.16.tgz",
@ -2754,11 +2760,6 @@
} }
} }
}, },
"codem-isoboxer": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/codem-isoboxer/-/codem-isoboxer-0.3.6.tgz",
"integrity": "sha512-LuO8/7LW6XuR5ERn1yavXAfodGRhuY2yP60JTZIw5yNYMCE5lUVbk3NFUCJxjnphQH+Xemp5hOGb1LgUXm00Xw=="
},
"collection-visit": { "collection-visit": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@ -3146,16 +3147,6 @@
"assert-plus": "^1.0.0" "assert-plus": "^1.0.0"
} }
}, },
"dashjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/dashjs/-/dashjs-3.0.0.tgz",
"integrity": "sha512-XyVkjeHB4mqzf/Y7ARQ1IqbRBaee0osAulwCFV5ZNZ734wea8LbSC/23zKI32BulW7Kk6f7I6onW1WDMHRgz7Q==",
"requires": {
"codem-isoboxer": "0.3.6",
"fast-deep-equal": "2.0.1",
"imsc": "^1.0.2"
}
},
"date-format": { "date-format": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
@ -3999,7 +3990,8 @@
"fast-deep-equal": { "fast-deep-equal": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
}, },
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.0.0", "version": "2.0.0",
@ -4555,6 +4547,22 @@
"minimalistic-assert": "^1.0.1" "minimalistic-assert": "^1.0.1"
} }
}, },
"hls.js": {
"version": "0.12.4",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-0.12.4.tgz",
"integrity": "sha512-e8OPxQ60dBVsdkv4atdxR21KzC1mgwspM41qpozpj3Uv1Fz4CaeQy3FWoaV2O+QKKbNRvV5hW+/LipCWdrwnMQ==",
"requires": {
"eventemitter3": "3.1.0",
"url-toolkit": "^2.1.6"
},
"dependencies": {
"eventemitter3": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA=="
}
}
},
"hmac-drbg": { "hmac-drbg": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -4816,21 +4824,6 @@
"resolve-cwd": "^2.0.0" "resolve-cwd": "^2.0.0"
} }
}, },
"imsc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/imsc/-/imsc-1.1.0.tgz",
"integrity": "sha512-z2aUE3X00O39fCgkLHEVbfKG/D9cBrmsbf4NjP7K1gQb06YBjgljq5nZD72HYMFG2lRJI7QY7v+JVxg6o6I/Jg==",
"requires": {
"sax": "1.2.1"
},
"dependencies": {
"sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
}
}
},
"imurmurhash": { "imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@ -10220,6 +10213,11 @@
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
}, },
"url-toolkit": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.1.6.tgz",
"integrity": "sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw=="
},
"use": { "use": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",

View File

@ -22,9 +22,9 @@
"@angular/platform-browser-dynamic": "~8.2.0", "@angular/platform-browser-dynamic": "~8.2.0",
"@angular/router": "~8.2.0", "@angular/router": "~8.2.0",
"bootstrap": "^4.3.1", "bootstrap": "^4.3.1",
"dashjs": "^3.0.0",
"detect-browser": "^4.8.0", "detect-browser": "^4.8.0",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"hls.js": "^0.12.4",
"jquery": "^3.4.1", "jquery": "^3.4.1",
"popper.js": "^1.15.0", "popper.js": "^1.15.0",
"zone.js": "~0.9.1" "zone.js": "~0.9.1"
@ -35,6 +35,7 @@
"@angular/compiler-cli": "~8.2.0", "@angular/compiler-cli": "~8.2.0",
"@angular/language-service": "~8.2.0", "@angular/language-service": "~8.2.0",
"@types/bootstrap": "^4.3.1", "@types/bootstrap": "^4.3.1",
"@types/hls.js": "^0.12.5",
"@types/jasmine": "~3.3.8", "@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/jquery": "^3.3.31", "@types/jquery": "^3.3.31",

View File

@ -4,7 +4,7 @@ import { DomSanitizer, Title } from "@angular/platform-browser";
import { ActivatedRoute, Event, NavigationCancel, NavigationEnd, NavigationStart, Router } from "@angular/router"; import { ActivatedRoute, Event, NavigationCancel, NavigationEnd, NavigationStart, Router } from "@angular/router";
import { Track, WatchItem } from "../../models/watch-item"; import { Track, WatchItem } from "../../models/watch-item";
import { Location } from "@angular/common"; import { Location } from "@angular/common";
import { MediaPlayer } from "dashjs"; import * as Hls from "hls.js"
import { getPlaybackMethod, method } from "../../videoSupport/playbackMethodDetector"; import { getPlaybackMethod, method } from "../../videoSupport/playbackMethodDetector";
declare var SubtitleManager: any; declare var SubtitleManager: any;
@ -43,8 +43,7 @@ export class PlayerComponent implements OnInit
playMethod: method; playMethod: method;
private player: HTMLVideoElement; private player: HTMLVideoElement;
private dashPlayer: dashjs.MediaPlayerClass = MediaPlayer().create(); private hlsPlayer: Hls = new Hls();
private dashPlayerInitialized: boolean = false;
private thumb: HTMLElement; private thumb: HTMLElement;
private progress: HTMLElement; private progress: HTMLElement;
private buffered: HTMLElement; private buffered: HTMLElement;
@ -381,22 +380,27 @@ export class PlayerComponent implements OnInit
selectPlayMethod() selectPlayMethod()
{ {
if (this.dashPlayerInitialized)
this.dashPlayer.reset();
if (this.playMethod == method.direct) if (this.playMethod == method.direct)
{ {
this.player.src = "/video/" + this.item.link; this.player.src = "/video/" + this.item.link;
this.dashPlayerInitialized = false;
} }
else if (this.playMethod == method.transmux) else if (this.playMethod == method.transmux)
{ {
this.dashPlayer.initialize(this.player, "/video/transmux/" + this.item.link + "/", true); this.hlsPlayer.loadSource("/video/transmux/" + this.item.link + "/");
this.dashPlayerInitialized = true; this.hlsPlayer.attachMedia(this.player);
this.hlsPlayer.on(Hls.Events.MANIFEST_LOADED, () =>
{
this.player.play();
});
} }
else else
{ {
this.dashPlayer.initialize(this.player, "/video/transcode/" + this.item.link + "/", true); this.hlsPlayer.loadSource("/video/transcode/" + this.item.link + "/");
this.dashPlayerInitialized = true; this.hlsPlayer.attachMedia(this.player);
this.hlsPlayer.on(Hls.Events.MANIFEST_LOADED, () =>
{
this.player.play();
});
} }
} }

View File

@ -33,7 +33,7 @@ namespace Kyoo.Controllers
return NotFound(); return NotFound();
} }
[HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}/")] [HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public async Task<IActionResult> Transmux(string showSlug, long seasonNumber, long episodeNumber) public async Task<IActionResult> Transmux(string showSlug, long seasonNumber, long episodeNumber)
{ {
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
@ -42,7 +42,7 @@ namespace Kyoo.Controllers
{ {
string path = await transcoder.Transmux(episode); string path = await transcoder.Transmux(episode);
if (path != null) if (path != null)
return PhysicalFile(path, "application/dash+xml", true); return PhysicalFile(path, "application/x-mpegURL ", true);
else else
return StatusCode(500); return StatusCode(500);
} }
@ -50,13 +50,13 @@ namespace Kyoo.Controllers
return NotFound(); return NotFound();
} }
[HttpGet("transmux/{episodeLink}/dash/{chunk}")] [HttpGet("transmux/{episodeLink}/segment/{chunk}")]
public IActionResult GetTransmuxedChunk(string episodeLink, string chunk) public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
{ {
string path = Path.Combine(transmuxPath, episodeLink); string path = Path.Combine(transmuxPath, episodeLink);
path = Path.Combine(path, "dash" + Path.DirectorySeparatorChar + chunk); path = Path.Combine(path, "segments" + Path.DirectorySeparatorChar + chunk);
return PhysicalFile(path, "video/iso.segment"); return PhysicalFile(path, "video/MP2T");
} }
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]

View File

@ -43,7 +43,7 @@ namespace Kyoo.InternalAPI
public async Task<string> Transmux(WatchItem episode) public async Task<string> Transmux(WatchItem episode)
{ {
string folder = Path.Combine(transmuxPath, episode.Link); string folder = Path.Combine(transmuxPath, episode.Link);
string manifest = Path.Combine(folder, episode.Link + ".mpd"); string manifest = Path.Combine(folder, episode.Link + ".m3u8");
float playableDuration = 0; float playableDuration = 0;
bool transmuxFailed = false; bool transmuxFailed = false;

View File

@ -58,7 +58,7 @@ namespace Kyoo
ctx.Response.Headers.Remove("X-Powered-By"); ctx.Response.Headers.Remove("X-Powered-By");
ctx.Response.Headers.Remove("Server"); ctx.Response.Headers.Remove("Server");
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen"); ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"); ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self' data: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; style-src 'self' 'unsafe-inline'");
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer"); ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null"); ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");