Creating a Transmux function and cleaning up the Transcoder code.

This commit is contained in:
Zoe Roux 2019-09-25 01:06:37 +02:00
parent 6534ea2e99
commit 3c47c88767
12 changed files with 409 additions and 117 deletions

View File

@ -21,23 +21,168 @@ int Init()
return 42;
}
#pragma region InternalProcess
int open_input_context(AVFormatContext **inputContext, const char *path)
{
if (avformat_open_input(inputContext, path, NULL, NULL))
{
std::cout << "Error: Can't open the file at " << path << std::endl;
return 1;
}
if (avformat_find_stream_info(*inputContext, NULL) < 0)
{
std::cout << "Error: Could't find streams informations for the file at " << path << std::endl;
return 1;
}
av_dump_format(*inputContext, 0, path, false);
return 0;
}
AVStream* copy_stream_to_output(AVFormatContext *outputContext, AVStream *inputStream)
{
AVStream *outputStream = avformat_new_stream(outputContext, NULL);
if (outputStream == NULL)
{
std::cout << "Error: Couldn't create stream." << std::endl;
return NULL;
}
if (avcodec_parameters_copy(outputStream->codecpar, inputStream->codecpar) < 0)
{
std::cout << "Error: Couldn't copy parameters to the output file." << std::endl;
return NULL;
}
outputStream->codecpar->codec_tag = 0;
avformat_transfer_internal_stream_timing_info(outputContext->oformat, outputStream, inputStream, AVTimebaseSource::AVFMT_TBCF_AUTO);
outputStream->time_base = av_add_q(av_stream_get_codec_timebase(outputStream), AVRational{ 0, 1 });
outputStream->duration = av_rescale_q(inputStream->duration, inputStream->time_base, outputStream->time_base);
outputStream->disposition = inputStream->disposition;
av_dict_copy(&outputStream->metadata, inputStream->metadata, NULL);
//if (inputStream->nb_side_data)
//{
// for (int i = 0; i < inputStream->nb_side_data; i++)
// {
// std::cout << "Copying side packet #" << i << std::endl;
// AVPacketSideData *sidePkt = &inputStream->side_data[i];
// uint8_t *newPkt = av_stream_new_side_data(outputStream, sidePkt->type, sidePkt->size);
// if (newPkt == NULL)
// {
// std::cout << "Error copying side package." << std::endl;
// //Should handle return here
// return;
// }
// memcpy(newPkt, sidePkt->data, sidePkt->size);
// }
//}
return outputStream;
}
int open_output_file_for_write(AVFormatContext *outputContext, const char* outputPath)
{
if (!(outputContext->flags & AVFMT_NOFILE))
{
if (avio_open(&outputContext->pb, outputPath, AVIO_FLAG_WRITE) < 0)
{
std::cout << "Error: Couldn't open file at " << outputPath << std::endl;
return 1;
}
}
else
std::cout << "Output flag set to AVFMT_NOFILE." << std::endl;
if (avformat_write_header(outputContext, NULL) < 0)
{
std::cout << "Error: Couldn't write headers to file at " << outputPath << std::endl;
return 1;
}
return 0;
}
void process_packet(AVPacket pkt, AVStream* inputStream, AVStream* outputStream)
{
pkt.pts = av_rescale_q_rnd(pkt.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, inputStream->time_base, outputStream->time_base);
pkt.pos = -1;
}
#pragma endregion
//Should add goto end;
int Transmux(const char *path, const char *outPath)
{
AVFormatContext *inputContext = NULL;
int ret = 0;
if (open_input_context(&inputContext, path) != 0)
return 1;
AVFormatContext *outputContext = NULL;
if (avformat_alloc_output_context2(&outputContext, NULL, NULL, outPath) < 0)
{
std::cout << "Error: Couldn't create an output file." << std::endl;
return 1;
}
for (unsigned int i = 0; i < inputContext->nb_streams; i++)
{
AVStream *stream = inputContext->streams[i];
if(stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) //Should support multi-audio on a good format.
if (copy_stream_to_output(outputContext, stream) == NULL)
return 1;
}
av_dump_format(outputContext, 0, outPath, true);
if (open_output_file_for_write(outputContext, outPath) != 0)
return 1;
AVPacket pkt;
while (av_read_frame(inputContext, &pkt) == 0)
{
if (pkt.stream_index >= outputContext->nb_streams)
continue;
AVStream *inputStream = inputContext->streams[pkt.stream_index];
AVStream *outputStream = outputContext->streams[pkt.stream_index];
process_packet(pkt, inputStream, outputStream);
if (av_interleaved_write_frame(outputContext, &pkt) < 0)
std::cout << "Error while writing a packet to the output file." << std::endl;
av_packet_unref(&pkt);
}
avformat_close_input(&inputContext);
av_write_trailer(outputContext);
if (outputContext && !(outputContext->flags & AVFMT_NOFILE))
avio_closep(&outputContext->pb);
avformat_free_context(outputContext);
return ret;
}
Stream *ExtractSubtitles(const char *path, const char *outPath, int *streamCount)
{
AVFormatContext *inputContext = NULL;
if (avformat_open_input(&inputContext, path, NULL, NULL))
{
std::cout << "Error: Can't open the file at " << path << std::endl;
if (open_input_context(&inputContext, path) != 0)
return 0;
}
if (avformat_find_stream_info(inputContext, NULL) < 0)
{
std::cout << "Error: Could't find streams informations for the file at " << path << std::endl;
return 0;
}
av_dump_format(inputContext, 0, path, false);
std::vector<Stream> *subtitleStreams = new std::vector<Stream>();
const unsigned int outputCount = inputContext->nb_streams;
@ -62,7 +207,7 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
NULL); //Path builder references
//Create the language subfolder
std::stringstream outStream /*= new std::stringstream()*/;
std::stringstream outStream;
outStream << outPath << (char)std::filesystem::path::preferred_separator << stream.language;
std::filesystem::create_directory(outStream.str());
@ -99,63 +244,14 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
av_dict_copy(&outputContext->metadata, inputContext->metadata, NULL);
AVStream* outputStream = avformat_new_stream(outputContext, NULL);
AVStream* outputStream = copy_stream_to_output(outputContext, inputStream);
if (outputStream == NULL)
{
std::cout << "Error: Couldn't create stream." << std::endl;
goto end;
}
if (avcodec_parameters_copy(outputStream->codecpar, inputCodecpar) < 0)
{
std::cout << "Error: Couldn't copy parameters to the output file." << std::endl;
goto end;
}
outputStream->codecpar->codec_tag = 0;
avformat_transfer_internal_stream_timing_info(outputContext->oformat, outputStream, inputStream, AVTimebaseSource::AVFMT_TBCF_AUTO);
outputStream->time_base = av_add_q(av_stream_get_codec_timebase(outputStream), AVRational { 0, 1 });
outputStream->duration = av_rescale_q(inputStream->duration, inputStream->time_base, outputStream->time_base);
outputStream->disposition = inputStream->disposition;
av_dict_copy(&outputStream->metadata, inputStream->metadata, NULL);
//if (inputStream->nb_side_data)
//{
// for (int i = 0; i < inputStream->nb_side_data; i++)
// {
// std::cout << "Copying side packet #" << i << std::endl;
// AVPacketSideData* sidePkt = &inputStream->side_data[i];
// uint8_t* newPkt = av_stream_new_side_data(outputStream, sidePkt->type, sidePkt->size);
// if (newPkt == NULL)
// {
// std::cout << "Error copying side package." << std::endl;
// //Should handle return here
// return;
// }
// memcpy(newPkt, sidePkt->data, sidePkt->size);
// }
//}
av_dump_format(outputContext, 0, stream.path, true);
if (!(outputContext->flags & AVFMT_NOFILE))
{
if (avio_open(&outputContext->pb, stream.path, AVIO_FLAG_WRITE) < 0)
{
std::cout << "Error: Couldn't open file at " << stream.path << std::endl;
if (open_output_file_for_write(outputContext, stream.path) != 0)
goto end;
}
}
else
std::cout << "Output flag set to AVFMT_NOFILE." << std::endl;
if (avformat_write_header(outputContext, NULL) < 0)
{
std::cout << "Error: Couldn't write headers to file at " << stream.path << std::endl;
goto end;
}
outputList[i] = outputContext;
@ -190,11 +286,7 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
AVStream *outputStream = outputContext->streams[0];
pkt.stream_index = 0;
pkt.pts = av_rescale_q_rnd(pkt.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, inputStream->time_base, outputStream->time_base);
pkt.pos = -1;
process_packet(pkt, inputStream, outputStream);
if (av_interleaved_write_frame(outputContext, &pkt) < 0)
{

View File

@ -13,3 +13,5 @@ extern "C" API int Init();
//Take the path of the file and the path of the output directory. It will return the list of subtitle streams in the streams variable. The int returned is the number of subtitles extracted.
extern "C" API Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamsCount);
extern "C" API int Transmux(const char* path, const char* outPath);

View File

@ -32,7 +32,8 @@
"scripts": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.bundle.min.js",
"./src/libraries/subtitles.js"
"./src/libraries/subtitles.js",
"./node_modules/video.js/dist/video.min.js"
]
},
"configurations": {

View File

@ -1134,6 +1134,21 @@
"integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==",
"dev": true
},
"@babel/runtime": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz",
"integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==",
"requires": {
"regenerator-runtime": "^0.13.2"
},
"dependencies": {
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
}
}
},
"@babel/template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
@ -1349,6 +1364,20 @@
}
}
},
"@videojs/http-streaming": {
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-1.10.6.tgz",
"integrity": "sha512-uPBuunHnxWeFRYxRX0j6h1IIWv3+QKvSkZGmW9TvqxWBqeNGSrQymR6tm1nVjQ2HhMVxVphQTUhUTTPDVWqmQg==",
"requires": {
"aes-decrypter": "3.0.0",
"global": "^4.3.0",
"m3u8-parser": "4.4.0",
"mpd-parser": "0.8.1",
"mux.js": "5.2.1",
"url-toolkit": "^2.1.3",
"video.js": "^6.8.0 || ^7.0.0"
}
},
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@ -1575,6 +1604,16 @@
"integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==",
"dev": true
},
"aes-decrypter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.0.0.tgz",
"integrity": "sha1-eEihwUW5/b9Xrj4rWxvHzwZEqPs=",
"requires": {
"commander": "^2.9.0",
"global": "^4.3.2",
"pkcs7": "^1.0.2"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
@ -2791,8 +2830,7 @@
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"dev": true
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
},
"commondir": {
"version": "1.0.1",
@ -3203,7 +3241,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
@ -3415,6 +3452,11 @@
"void-elements": "^2.0.0"
}
},
"dom-walk": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
"integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -3631,7 +3673,6 @@
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
"integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.0",
"function-bind": "^1.1.1",
@ -3645,7 +3686,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
"integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
@ -4130,6 +4170,14 @@
}
}
},
"for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"requires": {
"is-callable": "^1.1.3"
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -4250,8 +4298,7 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"genfun": {
"version": "5.0.0",
@ -4324,6 +4371,22 @@
}
}
},
"global": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
"integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
"requires": {
"min-document": "^2.19.0",
"process": "~0.5.1"
},
"dependencies": {
"process": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
"integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
}
}
},
"globals": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
@ -4409,7 +4472,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@ -4455,8 +4517,7 @@
"has-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
"dev": true
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
},
"has-value": {
"version": "1.0.0",
@ -4803,6 +4864,11 @@
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
"dev": true
},
"individual": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz",
"integrity": "sha1-gzsJfa0jKU52EXqY+zjg2a1hu5c="
},
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
@ -4956,8 +5022,7 @@
"is-callable": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
"integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
"dev": true
"integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
},
"is-data-descriptor": {
"version": "0.1.4",
@ -4982,8 +5047,7 @@
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
},
"is-descriptor": {
"version": "0.1.6",
@ -5037,6 +5101,11 @@
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-function": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz",
"integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU="
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
@ -5101,7 +5170,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
"has": "^1.0.1"
}
@ -5116,7 +5184,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
"integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
"dev": true,
"requires": {
"has-symbols": "^1.0.0"
}
@ -6312,6 +6379,11 @@
"source-map-support": "^0.5.5"
}
},
"keycode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
"integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@ -6488,6 +6560,14 @@
"yallist": "^3.0.2"
}
},
"m3u8-parser": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.4.0.tgz",
"integrity": "sha512-iH2AygTFILtato+XAgnoPYzLHM4R3DjATj7Ozbk7EHdB2XoLF2oyOUguM7Kc4UVHbQHHL/QPaw98r7PbWzG0gg==",
"requires": {
"global": "^4.3.2"
}
},
"magic-string": {
"version": "0.25.3",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz",
@ -6787,6 +6867,14 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
"requires": {
"dom-walk": "^0.1.0"
}
},
"mini-css-extract-plugin": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz",
@ -6933,6 +7021,15 @@
"run-queue": "^1.0.3"
}
},
"mpd-parser": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.8.1.tgz",
"integrity": "sha512-WBTJ1bKk8OLUIxBh6s1ju1e2yz/5CzhPbgi6P3F3kJHKhGy1Z+ElvEnuzEbtC/dnbRcJtMXazE3f93N5LLdp9Q==",
"requires": {
"global": "^4.3.2",
"url-toolkit": "^2.1.1"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -6961,6 +7058,11 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
"mux.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/mux.js/-/mux.js-5.2.1.tgz",
"integrity": "sha512-1t2payD3Y8izfZRq7tfUQlhL2fKzjeLr9v1/2qNCTkEQnd9Abtn1JgzsBgGZubEXh6lM5L8B0iLGoWQiukjtbQ=="
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
@ -7270,8 +7372,7 @@
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
},
"object-visit": {
"version": "1.0.1",
@ -7586,6 +7687,15 @@
"safe-buffer": "^5.1.1"
}
},
"parse-headers": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz",
"integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==",
"requires": {
"for-each": "^0.3.3",
"string.prototype.trim": "^1.1.2"
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
@ -7743,6 +7853,11 @@
"pinkie": "^2.0.0"
}
},
"pkcs7": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.2.tgz",
"integrity": "sha1-ttulJ1KMKUK/wSLOLa/NteWQdOc="
},
"pkg-dir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
@ -8557,6 +8672,14 @@
"aproba": "^1.1.1"
}
},
"rust-result": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz",
"integrity": "sha1-NMdbLm3Dn+WHXlveyFteD5FTb3I=",
"requires": {
"individual": "^2.0.0"
}
},
"rxjs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
@ -8571,6 +8694,14 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"safe-json-parse": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-4.0.0.tgz",
"integrity": "sha1-fA9XjPzNEtM6ccDgVBPi7KFx6qw=",
"requires": {
"rust-result": "^1.0.0"
}
},
"safe-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
@ -9579,6 +9710,16 @@
}
}
},
"string.prototype.trim": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz",
"integrity": "sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.13.0",
"function-bind": "^1.1.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@ -10172,6 +10313,11 @@
"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": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@ -10286,6 +10432,34 @@
"extsprintf": "^1.2.0"
}
},
"video.js": {
"version": "7.6.5",
"resolved": "https://registry.npmjs.org/video.js/-/video.js-7.6.5.tgz",
"integrity": "sha512-r0kC9SNJhXz9th/wwbRaLVOIZTvXkF+rhFq9/FWU+e+EJClwClRCgP8STGmfrPHDXrfWiJwH9YY21JZK61vGGQ==",
"requires": {
"@babel/runtime": "^7.4.5",
"@videojs/http-streaming": "1.10.6",
"global": "4.3.2",
"keycode": "^2.2.0",
"safe-json-parse": "4.0.0",
"videojs-font": "3.2.0",
"videojs-vtt.js": "^0.14.1",
"xhr": "2.4.0"
}
},
"videojs-font": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz",
"integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA=="
},
"videojs-vtt.js": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.14.1.tgz",
"integrity": "sha512-YxOiywx6N9t3J5nqsE5WN2Sw4CSqVe3zV+AZm2T4syOc2buNJaD6ZoexSdeszx2sHLU/RRo2r4BJAXFDQ7Qo2Q==",
"requires": {
"global": "^4.3.1"
}
},
"vm-browserify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz",
@ -11994,6 +12168,17 @@
"ultron": "~1.1.0"
}
},
"xhr": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.0.tgz",
"integrity": "sha1-4W5mpF+GmGHu76tBbV7/ci3ECZM=",
"requires": {
"global": "~4.3.0",
"is-function": "^1.0.1",
"parse-headers": "^2.0.0",
"xtend": "^4.0.0"
}
},
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
@ -12027,8 +12212,7 @@
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"y18n": {
"version": "4.0.0",

View File

@ -27,6 +27,7 @@
"popper.js": "^1.15.0",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
"video.js": "^7.6.5",
"zone.js": "~0.9.1"
},
"devDependencies": {

View File

@ -1,10 +1,8 @@
<div id="root">
<div class="player">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="tooglePlayback()">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="tooglePlayback()" class="video-js">
<source src="/api/video/{{this.item.link}}" />
<source src="/api/video/{{this.item.link}}/stream" type="video/mp4" />
<!--<track src="/api/subtitle/clannad-s1e1.eng.vtt" lang="English" srclang="en" default/>-->
<source src="/api/video/transmux/{{this.item.link}}" type="video/mp4" />
</video>
</div>

View File

@ -1,10 +1,8 @@
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { WatchItem, Track } from "../../models/watch-item";
import { ActivatedRoute, Router } from "@angular/router";
import { DomSanitizer, Title } from "@angular/platform-browser";
import { Location } from "@angular/common";
import { MatSliderChange } from "@angular/material/slider";
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { MatSnackBar } from "@angular/material/snack-bar";
import { DomSanitizer, Title } from "@angular/platform-browser";
import { ActivatedRoute, Router } from "@angular/router";
import { Track, WatchItem } from "../../models/watch-item";
declare var SubtitleManager: any;

View File

@ -22,35 +22,38 @@ namespace Kyoo.Controllers
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
if (episode != null && System.IO.File.Exists(episode.Path))
return PhysicalFile(episode.Path, "video/x-matroska", true);
else
return NotFound();
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}/stream")]
public IActionResult Stream(string showSlug, long seasonNumber, long episodeNumber)
[HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult Transmux(string showSlug, long seasonNumber, long episodeNumber)
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
if (episode != null && System.IO.File.Exists(episode.Path))
{
string path = transcoder.Stream(episode.Path);
string path = transcoder.Transmux(episode);
if (path != null)
return PhysicalFile(path, "video/mp4", true);
else
return StatusCode(500);
}
else
return NotFound();
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}/transcode")]
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult Transcode(string showSlug, long seasonNumber, long episodeNumber)
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
if (episode != null && System.IO.File.Exists(episode.Path))
{
string path = transcoder.Transcode(episode.Path);
return PhysicalFile(path, "video/mp4", true);
return PhysicalFile(path, "video/mp4", true); //Should use mpeg dash
}
else
return NotFound();

View File

@ -6,7 +6,7 @@ namespace Kyoo.InternalAPI
public interface ITranscoder
{
//Should transcode to a mp4 container (same video/audio format if possible, no subtitles).
string Stream(string path);
string Transmux(WatchItem episode);
//Should transcode to a mp4 container with a h264 video format and a AAC audio format, no subtitles.
string Transcode(string path);

View File

@ -10,8 +10,12 @@ namespace Kyoo.InternalAPI
{
public class Transcoder : ITranscoder
{
private readonly string tempPath;
public Transcoder(IConfiguration config)
{
tempPath = config.GetValue<string>("tempPath");
Debug.WriteLine("&Api INIT: " + TranscoderAPI.Init());
}
@ -29,9 +33,14 @@ namespace Kyoo.InternalAPI
Debug.WriteLine("&Getting video...");
}
public string Stream(string path)
public string Transmux(WatchItem episode)
{
return @"D:\Videos\Anohana\AnoHana S01E01.mp4";
string temp = Path.Combine(tempPath, episode.Link + ".mp4");
Debug.WriteLine("&Transmuxing " + episode.Link + " at " + episode.Path + ", outputPath: " + temp);
if (File.Exists(temp) || TranscoderAPI.Transmux(episode.Path, temp) == 0)
return temp;
else
return null;
}
public string Transcode(string path)

View File

@ -12,6 +12,9 @@ namespace Kyoo.InternalAPI.TranscoderLink
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public extern static int Init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public extern static int Transmux(string path, string outPath);
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private extern static IntPtr ExtractSubtitles(string path, string outPath, out int streams);

View File

@ -1,5 +1,6 @@
{
"databasePath": "C://Projects/database.db",
"tempPath": "C:\\Projects",
"peoplePath": "D://Videos/People",
"plugins": "C:\\Projects\\Kyoo\\Debug",
"providerPlugins": "C://Projects/Plugins/Providers",