mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 05:34:23 -04:00
Reworking the transcoder to work with a CMake in C (removing unnecesarry C++).
This commit is contained in:
parent
fe9e6e7e50
commit
408352cc3c
20
Kyoo.Transcoder/CMakeLists.txt
Normal file
20
Kyoo.Transcoder/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
set (PROJECT_VERSION "1.0")
|
||||
project(Kyoo.Transcoder VERSION ${PROJECT_VERSION})
|
||||
|
||||
include_directories(ffmpeg)
|
||||
include_directories(ffmpeg/include)
|
||||
include_directories(ffmpeg/include/libavcodec)
|
||||
include_directories(ffmpeg/include/libavdevice)
|
||||
include_directories(ffmpeg/include/libavfilter)
|
||||
include_directories(ffmpeg/include/libavformat)
|
||||
include_directories(ffmpeg/include/libavutil)
|
||||
include_directories(ffmpeg/include/libpostproc)
|
||||
include_directories(ffmpeg/include/libswresample)
|
||||
include_directories(ffmpeg/include/libswscale)
|
||||
include_directories(include)
|
||||
|
||||
add_library(Kyoo.Transcoder SHARED
|
||||
src/helper.c
|
||||
src/transcoder.c src/subtitles.c)
|
@ -1,195 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{E5EFA4B2-F09D-4C4F-83DD-A436CD60BB77}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>KyooTranscoder</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(ProjectDir)\ffmpeg\include;./include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(ProjectDir)\ffmpeg\lib;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>$(SolutionDir)Kyoo\Transcoder</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)Kyoo\Transcoder</OutDir>
|
||||
<IncludePath>$(ProjectDir)\ffmpeg\include;./include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(ProjectDir)\ffmpeg\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(ProjectDir)\ffmpeg\include;./include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(ProjectDir)\ffmpeg\lib;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>$(SolutionDir)Kyoo\Transcoder</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)Kyoo\Transcoder</OutDir>
|
||||
<IncludePath>$(ProjectDir)\ffmpeg\include;./include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(ProjectDir)\ffmpeg\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\ffmpeg\lib</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(ProjectDir)ffmpeg\lib\*.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\ffmpeg\lib</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(ProjectDir)ffmpeg\lib\*.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\ffmpeg\lib</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(ProjectDir)ffmpeg\lib\*.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;KYOOTRANSCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);TRANSCODER_EXPORTS=1;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>$(ProjectDir)\ffmpeg\lib</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>$(ProjectDir)ffmpeg\lib\*.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include/helper.h" />
|
||||
<ClInclude Include="include/Stream.h" />
|
||||
<ClInclude Include="include/Transcoder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src/helper.cpp" />
|
||||
<ClCompile Include="src/Transcoder.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Models">
|
||||
<UniqueIdentifier>{a553acdb-cb65-47bc-8809-c5374fe91cae}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Compiler Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Headers">
|
||||
<UniqueIdentifier>{851d2dff-3186-4d12-9f4a-4144143be6fb}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include/helper.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include/Stream.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include/Transcoder.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src/Transcoder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src/helper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
15
Kyoo.Transcoder/include/compatibility.h
Normal file
15
Kyoo.Transcoder/include/compatibility.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Created by Anonymus Raccoon on 16/12/2019.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __WIN32__
|
||||
#define mkdir(c, m) mkdir(c)
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define asprintf __mingw_asprintf
|
||||
#endif
|
11
Kyoo.Transcoder/include/export.h
Normal file
11
Kyoo.Transcoder/include/export.h
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// Created by Anonymus Raccoon on 15/12/2019.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#define API __declspec(dllexport)
|
||||
#else
|
||||
#define API
|
||||
#endif
|
@ -1,11 +1,14 @@
|
||||
//
|
||||
// Created by Anonymus Raccoon on 15/12/2019.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
extern "C"
|
||||
{
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/dict.h>
|
||||
#include <libavutil/timestamp.h>
|
||||
}
|
||||
int open_input_context(AVFormatContext** inputContext, const char* path);
|
||||
AVStream* copy_stream_to_output(AVFormatContext* out_ctx, AVStream* in_stream);
|
||||
int open_output_file_for_write(AVFormatContext* out_ctx, const char* out_path, AVDictionary **options);
|
||||
void process_packet(AVPacket& pkt, AVStream* in_stream, AVStream* out_stream);
|
||||
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libavutil/dict.h"
|
||||
#include "libavutil/timestamp.h"
|
||||
|
||||
int open_input_context(AVFormatContext **inputContext, const char *path);
|
||||
AVStream *copy_stream_to_output(AVFormatContext *out_ctx, AVStream *in_stream);
|
||||
int open_output_file_for_write(AVFormatContext *out_ctx, const char *out_path, AVDictionary **options);
|
||||
void process_packet(AVPacket *pkt, AVStream *in_stream, AVStream *out_stream);
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" struct Stream
|
||||
typedef struct stream
|
||||
{
|
||||
char *title;
|
||||
char *language;
|
||||
@ -10,40 +10,13 @@ extern "C" struct Stream
|
||||
bool is_default;
|
||||
bool is_forced;
|
||||
char *path;
|
||||
} stream;
|
||||
|
||||
Stream()
|
||||
: title(nullptr), language(nullptr), codec(nullptr), is_default(nullptr), is_forced(nullptr), path(nullptr) {}
|
||||
|
||||
Stream(const char* title, const char* languageCode, const char* codec, bool isDefault, bool isForced)
|
||||
: title(nullptr), language(nullptr), codec(nullptr), is_default(isDefault), is_forced(isForced), path(nullptr)
|
||||
{
|
||||
if(title != nullptr)
|
||||
this->title= strdup(title);
|
||||
|
||||
if (languageCode != nullptr)
|
||||
language = strdup(languageCode);
|
||||
else
|
||||
language = strdup("und");
|
||||
|
||||
if (codec != nullptr)
|
||||
this->codec = strdup(codec);
|
||||
}
|
||||
|
||||
Stream(const char *title, const char *languageCode, const char *codec, bool isDefault, bool isForced, const char *path)
|
||||
: title(nullptr), language(nullptr), codec(nullptr), is_default(isDefault), is_forced(isForced), path(nullptr)
|
||||
{
|
||||
if (title != nullptr)
|
||||
this->title = strdup(title);
|
||||
|
||||
if (languageCode != nullptr)
|
||||
language = strdup(languageCode);
|
||||
else
|
||||
language = strdup("und");
|
||||
|
||||
if (codec != nullptr)
|
||||
this->codec = strdup(codec);
|
||||
|
||||
if (path != nullptr)
|
||||
this->path = strdup(path);
|
||||
}
|
||||
};
|
||||
#define NULLSTREAM (struct stream) { \
|
||||
NULL, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
false, \
|
||||
false, \
|
||||
NULL \
|
||||
}
|
@ -1,21 +1,17 @@
|
||||
#pragma once
|
||||
#ifdef TRANSCODER_EXPORTS
|
||||
#define API __declspec(dllexport)
|
||||
#else
|
||||
#define API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include "Stream.h"
|
||||
#include "export.h"
|
||||
#include "stream.h"
|
||||
|
||||
|
||||
extern "C" API int Init();
|
||||
API int init();
|
||||
|
||||
extern "C" API int transmux(const char *path, const char *out_path, float *playable_duration);
|
||||
API int transmux(const char *path, const char *out_path, float *playable_duration);
|
||||
|
||||
extern "C" API Stream *get_track_info(const char *path, int *stream_count, int *track_count);
|
||||
API int transcode(const char *path, const char *out_path, float *playable_duration);
|
||||
|
||||
API stream *get_track_info(const char *path, int *stream_count, int *track_count);
|
||||
|
||||
//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* extract_subtitles(const char *path, const char *outPath, int *streamCount, int *subtitleCount);
|
||||
API stream* extract_subtitles(char *path, const char *outPath, int *streamCount, int *subtitleCount);
|
||||
|
||||
extern "C" API void free_memory(Stream *streamsPtr);
|
||||
API void free_memory(stream *streamsPtr);
|
@ -1,293 +0,0 @@
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
#include "transcoder.h"
|
||||
#include "helper.h"
|
||||
|
||||
int Init()
|
||||
{
|
||||
return sizeof(Stream);
|
||||
}
|
||||
|
||||
int transmux(const char *path, const char *out_path, float *playable_duration)
|
||||
{
|
||||
AVFormatContext *in_ctx = NULL;
|
||||
AVFormatContext *out_ctx = NULL;
|
||||
AVStream *stream;
|
||||
AVPacket pkt;
|
||||
AVDictionary *options = NULL;
|
||||
int *stream_map;
|
||||
int stream_count;
|
||||
int ret = 0;
|
||||
std::string seg_path = ((std::string)out_path).substr(0, strrchr(out_path, '/') - out_path).append("/segments/");
|
||||
|
||||
*playable_duration = 0;
|
||||
if (open_input_context(&in_ctx, path) != 0)
|
||||
return 1;
|
||||
|
||||
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, out_path) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't create an output file." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
stream_map = new int[in_ctx->nb_streams];
|
||||
stream_count = 0;
|
||||
|
||||
for (unsigned int i = 0; i < in_ctx->nb_streams; i++)
|
||||
{
|
||||
stream = in_ctx->streams[i];
|
||||
if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
stream_map[i] = stream_count;
|
||||
stream_count++;
|
||||
if (copy_stream_to_output(out_ctx, stream) == NULL)
|
||||
return 1;
|
||||
}
|
||||
else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) //Should support multi-audio on a good format.
|
||||
{
|
||||
stream_map[i] = stream_count;
|
||||
stream_count++;
|
||||
if (copy_stream_to_output(out_ctx, stream) == NULL)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
stream_map[i] = -1;
|
||||
}
|
||||
|
||||
av_dump_format(out_ctx, 0, out_path, true);
|
||||
std::filesystem::create_directory(seg_path);
|
||||
av_dict_set(&options, "hls_segment_filename", seg_path.append("%v-%03d.ts").c_str(), 0);
|
||||
av_dict_set(&options, "hls_base_url", "segment/", 0);
|
||||
av_dict_set(&options, "hls_list_size", "0", 0);
|
||||
av_dict_set(&options, "streaming", "1", 0);
|
||||
|
||||
if (open_output_file_for_write(out_ctx, out_path, &options) != 0)
|
||||
return 1;
|
||||
|
||||
while (av_read_frame(in_ctx, &pkt) == 0)
|
||||
{
|
||||
if ((unsigned int)pkt.stream_index >= in_ctx->nb_streams || stream_map[pkt.stream_index] < 0)
|
||||
{
|
||||
av_packet_unref(&pkt);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream = in_ctx->streams[pkt.stream_index];
|
||||
pkt.stream_index = stream_map[pkt.stream_index];
|
||||
process_packet(pkt, stream, out_ctx->streams[pkt.stream_index]);
|
||||
if (pkt.stream_index == 0)
|
||||
*playable_duration += pkt.duration * (float)out_ctx->streams[pkt.stream_index]->time_base.num / out_ctx->streams[pkt.stream_index]->time_base.den;
|
||||
|
||||
if (av_interleaved_write_frame(out_ctx, &pkt) < 0)
|
||||
std::cout << "Error while writing a packet to the output file." << std::endl;
|
||||
|
||||
av_packet_unref(&pkt);
|
||||
}
|
||||
|
||||
av_dict_free(&options);
|
||||
av_write_trailer(out_ctx);
|
||||
avformat_close_input(&in_ctx);
|
||||
|
||||
if (out_ctx && !(out_ctx->oformat->flags & AVFMT_NOFILE))
|
||||
avio_close(out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
delete[] stream_map;
|
||||
|
||||
if (ret < 0 && ret != AVERROR_EOF)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Stream *get_track_info(const char *path, int *stream_count, int *track_count)
|
||||
{
|
||||
AVFormatContext *ctx = NULL;
|
||||
Stream *streams;
|
||||
|
||||
if (open_input_context(&ctx, path) != 0)
|
||||
return nullptr;
|
||||
|
||||
*stream_count = ctx->nb_streams;
|
||||
*track_count = 0;
|
||||
streams = new Stream[*stream_count];
|
||||
|
||||
//Initialize output and set headers.
|
||||
for (int i = 0; i < *stream_count; i++)
|
||||
{
|
||||
AVStream *stream = ctx->streams[i];
|
||||
const AVCodecParameters *codecpar = stream->codecpar;
|
||||
|
||||
if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO || codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
{
|
||||
AVDictionaryEntry *languageptr = av_dict_get(stream->metadata, "language", NULL, 0);
|
||||
|
||||
*track_count += 1;
|
||||
|
||||
streams[i] = Stream(codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? "VIDEO" : NULL, // title
|
||||
languageptr ? languageptr->value : NULL, // language
|
||||
avcodec_get_name(codecpar->codec_id), // format
|
||||
stream->disposition & AV_DISPOSITION_DEFAULT, // isDefault
|
||||
stream->disposition & AV_DISPOSITION_FORCED, // isForced
|
||||
path); // path
|
||||
}
|
||||
else
|
||||
streams[i] = Stream();
|
||||
}
|
||||
avformat_close_input(&ctx);
|
||||
return streams;
|
||||
}
|
||||
|
||||
Stream *extract_subtitles(const char *path, const char *out_path, int *stream_count, int *subtitle_count)
|
||||
{
|
||||
AVFormatContext *int_ctx = NULL;
|
||||
AVFormatContext **output_list;
|
||||
Stream *streams;
|
||||
AVPacket pkt;
|
||||
unsigned int out_count;
|
||||
|
||||
if (open_input_context(&int_ctx, path) != 0)
|
||||
return nullptr;
|
||||
|
||||
*stream_count = int_ctx->nb_streams;
|
||||
*subtitle_count = 0;
|
||||
streams = new Stream[*stream_count];
|
||||
|
||||
out_count = int_ctx->nb_streams;
|
||||
output_list = new AVFormatContext *[out_count];
|
||||
|
||||
//Initialize output and set headers.
|
||||
for (unsigned int i = 0; i < int_ctx->nb_streams; i++)
|
||||
{
|
||||
AVStream *in_stream = int_ctx->streams[i];
|
||||
const AVCodecParameters *in_codecpar = in_stream->codecpar;
|
||||
|
||||
if (in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
|
||||
{
|
||||
output_list[i] = NULL;
|
||||
streams[i] = Stream();
|
||||
}
|
||||
else
|
||||
{
|
||||
*subtitle_count += 1;
|
||||
|
||||
AVDictionaryEntry *languageptr = av_dict_get(in_stream->metadata, "language", NULL, 0);
|
||||
|
||||
//Get metadata for file name
|
||||
streams[i] = Stream(NULL, //title
|
||||
languageptr ? languageptr->value : NULL, //language
|
||||
avcodec_get_name(in_codecpar->codec_id), //format
|
||||
in_stream->disposition & AV_DISPOSITION_DEFAULT, //isDefault
|
||||
in_stream->disposition & AV_DISPOSITION_FORCED); //isForced
|
||||
|
||||
//Create the language subfolder
|
||||
std::stringstream out_strstream;
|
||||
out_strstream << out_path << (char)std::filesystem::path::preferred_separator << streams[i].language;
|
||||
std::filesystem::create_directory(out_strstream.str());
|
||||
|
||||
//Get file name
|
||||
std::string file_name(path);
|
||||
size_t last_separator = file_name.find_last_of((char)std::filesystem::path::preferred_separator);
|
||||
file_name = file_name.substr(last_separator, file_name.find_last_of('.') - last_separator);
|
||||
|
||||
//Construct output file name
|
||||
out_strstream << file_name << "." << streams[i].language;
|
||||
|
||||
if (streams[i].is_default)
|
||||
out_strstream << ".default";
|
||||
if (streams[i].is_forced)
|
||||
out_strstream << ".forced";
|
||||
|
||||
if (strcmp(streams[i].codec, "subrip") == 0)
|
||||
out_strstream << ".srt";
|
||||
else if (strcmp(streams[i].codec, "ass") == 0)
|
||||
out_strstream << ".ass";
|
||||
else
|
||||
{
|
||||
std::cout << "Unsupported subtitle codec: " << streams[i].codec << std::endl;
|
||||
output_list[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
streams[i].path = strdup(out_strstream.str().c_str());
|
||||
|
||||
std::cout << "Stream #" << i << "(" << streams[i].language << "), stream type: " << in_codecpar->codec_type << " codec: " << streams[i].codec << std::endl;
|
||||
|
||||
AVFormatContext *out_ctx = NULL;
|
||||
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, streams[i].path) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't create an output file." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
av_dict_copy(&out_ctx->metadata, int_ctx->metadata, NULL);
|
||||
|
||||
AVStream *out_stream = copy_stream_to_output(out_ctx, in_stream);
|
||||
if (out_stream == NULL)
|
||||
goto end;
|
||||
|
||||
av_dump_format(out_ctx, 0, streams[i].path, true);
|
||||
|
||||
if (open_output_file_for_write(out_ctx, streams[i].path, NULL) != 0)
|
||||
goto end;
|
||||
|
||||
output_list[i] = out_ctx;
|
||||
|
||||
if (false)
|
||||
{
|
||||
end:
|
||||
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
|
||||
avio_closep(&out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
|
||||
output_list[i] = nullptr;
|
||||
std::cout << "An error occured, cleaning up th output context for the stream #" << i << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Write subtitle data to files.
|
||||
while (av_read_frame(int_ctx, &pkt) == 0)
|
||||
{
|
||||
if ((unsigned int)pkt.stream_index >= out_count)
|
||||
continue;
|
||||
|
||||
AVFormatContext *out_ctx = output_list[pkt.stream_index];
|
||||
if (out_ctx == nullptr)
|
||||
{
|
||||
av_packet_unref(&pkt);
|
||||
continue;
|
||||
}
|
||||
|
||||
process_packet(pkt, int_ctx->streams[pkt.stream_index], out_ctx->streams[0]);
|
||||
pkt.stream_index = 0;
|
||||
|
||||
if (av_interleaved_write_frame(out_ctx, &pkt) < 0)
|
||||
std::cout << "Error while writing a packet to the output file." << std::endl;
|
||||
|
||||
av_packet_unref(&pkt);
|
||||
}
|
||||
|
||||
avformat_close_input(&int_ctx);
|
||||
|
||||
for (unsigned int i = 0; i < out_count; i++)
|
||||
{
|
||||
AVFormatContext *out_ctx = output_list[i];
|
||||
|
||||
if (out_ctx == NULL)
|
||||
continue;
|
||||
|
||||
av_write_trailer(out_ctx);
|
||||
|
||||
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
|
||||
avio_closep(&out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
}
|
||||
|
||||
delete[] output_list;
|
||||
return streams;
|
||||
}
|
||||
|
||||
void free_memory(Stream *stream_ptr)
|
||||
{
|
||||
delete[] stream_ptr;
|
||||
}
|
64
Kyoo.Transcoder/src/helper.c
Normal file
64
Kyoo.Transcoder/src/helper.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "helper.h"
|
||||
|
||||
int open_input_context(AVFormatContext **in_ctx, const char *path)
|
||||
{
|
||||
if (avformat_open_input(in_ctx, path, NULL, NULL)) {
|
||||
printf("Error: Can't open the file at %s.\n", path);
|
||||
return (1);
|
||||
}
|
||||
if (avformat_find_stream_info(*in_ctx, NULL) < 0) {
|
||||
printf("Error: Could't find streams informations for the file at %s.\n", path);
|
||||
return (1);
|
||||
}
|
||||
av_dump_format(*in_ctx, 0, path, false);
|
||||
return (0);
|
||||
}
|
||||
|
||||
AVStream *copy_stream_to_output(AVFormatContext *out_ctx, AVStream *in_stream)
|
||||
{
|
||||
AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
|
||||
|
||||
if (out_stream == NULL) {
|
||||
printf("Error: Couldn't create stream.\n");
|
||||
return (NULL);
|
||||
}
|
||||
if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
|
||||
printf("Error: Couldn't copy parameters to the output file.\n");
|
||||
return (NULL);
|
||||
}
|
||||
out_stream->codecpar->codec_tag = 0;
|
||||
avformat_transfer_internal_stream_timing_info(out_ctx->oformat, out_stream, in_stream, AVFMT_TBCF_AUTO);
|
||||
out_stream->time_base = av_add_q(av_stream_get_codec_timebase(out_stream), (AVRational){0, 1});
|
||||
out_stream->duration = av_rescale_q(in_stream->duration, in_stream->time_base, out_stream->time_base);
|
||||
out_stream->disposition = in_stream->disposition;
|
||||
out_stream->avg_frame_rate = in_stream->avg_frame_rate;
|
||||
out_stream->r_frame_rate = in_stream->r_frame_rate;
|
||||
return (out_stream);
|
||||
}
|
||||
|
||||
int open_output_file_for_write(AVFormatContext *out_ctx, const char *out_path, AVDictionary **options)
|
||||
{
|
||||
if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) {
|
||||
if (avio_open(&out_ctx->pb, out_path, AVIO_FLAG_WRITE) < 0) {
|
||||
printf("Error: Couldn't open file at %s.\n", out_path);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("Output flag set to AVFMT_NOFILE.\n");
|
||||
if (avformat_write_header(out_ctx, options) < 0) {
|
||||
printf("Error: Couldn't write headers to file at %s.\n", out_path);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void process_packet(AVPacket *pkt, AVStream *in_stream, AVStream *out_stream)
|
||||
{
|
||||
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
|
||||
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
|
||||
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
|
||||
pkt->pos = -1;
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#include "helper.h"
|
||||
#include <iostream>
|
||||
|
||||
int open_input_context(AVFormatContext **in_ctx, const char *path)
|
||||
{
|
||||
if (avformat_open_input(in_ctx, path, NULL, NULL))
|
||||
{
|
||||
std::cout << "Error: Can't open the file at " << path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (avformat_find_stream_info(*in_ctx, NULL) < 0)
|
||||
{
|
||||
std::cout << "Error: Could't find streams informations for the file at " << path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
av_dump_format(*in_ctx, 0, path, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVStream *copy_stream_to_output(AVFormatContext *out_ctx, AVStream *in_stream)
|
||||
{
|
||||
AVStream *out_stream = avformat_new_stream(out_ctx, NULL);
|
||||
|
||||
if (out_stream == NULL)
|
||||
{
|
||||
std::cout << "Error: Couldn't create stream." << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't copy parameters to the output file." << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
out_stream->codecpar->codec_tag = 0;
|
||||
avformat_transfer_internal_stream_timing_info(out_ctx->oformat, out_stream, in_stream, AVTimebaseSource::AVFMT_TBCF_AUTO);
|
||||
out_stream->time_base = av_add_q(av_stream_get_codec_timebase(out_stream), AVRational {0, 1});
|
||||
out_stream->duration = av_rescale_q(in_stream->duration, in_stream->time_base, out_stream->time_base);
|
||||
out_stream->disposition = in_stream->disposition;
|
||||
out_stream->avg_frame_rate = in_stream->avg_frame_rate;
|
||||
out_stream->r_frame_rate = in_stream->r_frame_rate;
|
||||
return out_stream;
|
||||
}
|
||||
|
||||
constexpr enum AVRounding operator |(const enum AVRounding a, const enum AVRounding b)
|
||||
{
|
||||
return (enum AVRounding)(uint32_t(a) | uint32_t(b));
|
||||
}
|
||||
|
||||
int open_output_file_for_write(AVFormatContext *out_ctx, const char *out_path, AVDictionary **options)
|
||||
{
|
||||
if (!(out_ctx->oformat->flags & AVFMT_NOFILE))
|
||||
{
|
||||
if (avio_open(&out_ctx->pb, out_path, AVIO_FLAG_WRITE) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't open file at " << out_path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
std::cout << "Output flag set to AVFMT_NOFILE." << std::endl;
|
||||
if (avformat_write_header(out_ctx, options) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't write headers to file at " << out_path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_packet(AVPacket &pkt, AVStream *in_stream, AVStream *out_stream)
|
||||
{
|
||||
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
|
||||
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
|
||||
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
|
||||
pkt.pos = -1;
|
||||
}
|
164
Kyoo.Transcoder/src/subtitles.c
Normal file
164
Kyoo.Transcoder/src/subtitles.c
Normal file
@ -0,0 +1,164 @@
|
||||
//
|
||||
// Created by Anonymus Raccoon on 16/12/2019.
|
||||
//
|
||||
|
||||
#include "transcoder.h"
|
||||
#include "stream.h"
|
||||
#include "helper.h"
|
||||
#include "compatibility.h"
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int get_subtitle_data(stream *substream, AVStream *in_stream, const char *folder_name, const char *out_path)
|
||||
{
|
||||
AVDictionaryEntry *languageptr = av_dict_get(in_stream->metadata, "language", NULL, 0);
|
||||
char *extension;
|
||||
char *codec;
|
||||
|
||||
codec = strdup(avcodec_get_name(in_stream->codecpar->codec_id));
|
||||
if (!strcmp(codec, "subrip"))
|
||||
extension = ".srt";
|
||||
else if (!strcmp(codec, "ass"))
|
||||
extension = ".ass";
|
||||
else {
|
||||
printf("Unsupported subtitle codec: %s.\n", codec);
|
||||
free(codec);
|
||||
return (-1);
|
||||
}
|
||||
*substream = (stream) {
|
||||
NULL,
|
||||
languageptr ? languageptr->value : NULL,
|
||||
codec,
|
||||
in_stream->disposition & AV_DISPOSITION_DEFAULT,
|
||||
in_stream->disposition & AV_DISPOSITION_FORCED,
|
||||
NULL
|
||||
};
|
||||
asprintf(&substream->path, "%s/%s", out_path, substream->language);
|
||||
if (mkdir(substream->path, 0733) < 0) {
|
||||
stream_free(substream);
|
||||
return (-1);
|
||||
}
|
||||
asprintf(&substream->path, "%s/%s.%s%s%s%s", substream->path,
|
||||
folder_name,
|
||||
substream->language,
|
||||
substream->is_default ? ".default" : "",
|
||||
substream->is_forced ? ".forced" : "",
|
||||
extension);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int copy_subtitle_stream(AVFormatContext **out, stream *s, AVFormatContext *int_ctx, AVStream *in_stream)
|
||||
{
|
||||
AVFormatContext *out_ctx = NULL;
|
||||
AVStream *out_stream = NULL;
|
||||
|
||||
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, s->path) < 0) {
|
||||
printf("Error: Couldn't create an output file.\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
av_dict_copy(&out_ctx->metadata, int_ctx->metadata, 0);
|
||||
out_stream = copy_stream_to_output(out_ctx, in_stream);
|
||||
if (out_stream) {
|
||||
av_dump_format(out_ctx, 0, s->path, true);
|
||||
if (open_output_file_for_write(out_ctx, s->path, NULL) == 0) {
|
||||
*out = out_ctx;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
|
||||
avio_closep(&out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
|
||||
*out = NULL;
|
||||
printf("An error occured, cleaning up th output context for the %s stream.\n", s->language);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
stream *extract_subtitles(char *path, const char *out_path, int *stream_count, int *subtitle_count)
|
||||
{
|
||||
AVFormatContext *int_ctx = NULL;
|
||||
AVFormatContext **output_list;
|
||||
stream *streams;
|
||||
AVPacket pkt;
|
||||
unsigned int out_count;
|
||||
char *folder_name = strchr(path, '/');
|
||||
char *p;
|
||||
|
||||
if (!folder_name)
|
||||
return (NULL);
|
||||
p = strchr(folder_name, '.');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
if (open_input_context(&int_ctx, path) != 0)
|
||||
return (NULL);
|
||||
*stream_count = int_ctx->nb_streams;
|
||||
*subtitle_count = 0;
|
||||
streams = malloc(sizeof(stream) * *stream_count);
|
||||
out_count = int_ctx->nb_streams;
|
||||
output_list = malloc(sizeof(AVFormatContext *) * out_count);
|
||||
|
||||
for (unsigned int i = 0; i < int_ctx->nb_streams; i++) {
|
||||
AVStream *in_stream = int_ctx->streams[i];
|
||||
|
||||
if (in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
|
||||
output_list[i] = NULL;
|
||||
streams[i] = NULLSTREAM;
|
||||
} else {
|
||||
if (get_subtitle_data(&streams[i], in_stream, folder_name, out_path) == 0) {
|
||||
if (copy_subtitle_stream(&output_list[i], &streams[i], int_ctx, in_stream) == 0) {
|
||||
*subtitle_count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
streams[i] = NULLSTREAM;
|
||||
output_list[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//Write subtitle data to files.
|
||||
while (av_read_frame(int_ctx, &pkt) == 0)
|
||||
{
|
||||
if ((unsigned int)pkt.stream_index >= out_count)
|
||||
continue;
|
||||
|
||||
AVFormatContext *out_ctx = output_list[pkt.stream_index];
|
||||
if (out_ctx == nullptr)
|
||||
{
|
||||
av_packet_unref(&pkt);
|
||||
continue;
|
||||
}
|
||||
|
||||
process_packet(pkt, int_ctx->streams[pkt.stream_index], out_ctx->streams[0]);
|
||||
pkt.stream_index = 0;
|
||||
|
||||
if (av_interleaved_write_frame(out_ctx, &pkt) < 0)
|
||||
std::cout << "Error while writing a packet to the output file." << std::endl;
|
||||
|
||||
av_packet_unref(&pkt);
|
||||
}
|
||||
|
||||
avformat_close_input(&int_ctx);
|
||||
|
||||
for (unsigned int i = 0; i < out_count; i++)
|
||||
{
|
||||
AVFormatContext *out_ctx = output_list[i];
|
||||
|
||||
if (out_ctx == NULL)
|
||||
continue;
|
||||
|
||||
av_write_trailer(out_ctx);
|
||||
|
||||
if (out_ctx && !(out_ctx->flags & AVFMT_NOFILE))
|
||||
avio_closep(&out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
}
|
||||
|
||||
delete[] output_list;
|
||||
return streams;
|
||||
}
|
139
Kyoo.Transcoder/src/transcoder.c
Normal file
139
Kyoo.Transcoder/src/transcoder.c
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "hicpp-signed-bitwise"
|
||||
#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
|
||||
#include "transcoder.h"
|
||||
#include "helper.h"
|
||||
#include "compatibility.h"
|
||||
|
||||
|
||||
stream *get_track_info(const char *path, int *stream_count, int *track_count)
|
||||
{
|
||||
AVFormatContext *ctx = NULL;
|
||||
stream *streams;
|
||||
|
||||
if (open_input_context(&ctx, path) != 0)
|
||||
return (NULL);
|
||||
*stream_count = ctx->nb_streams;
|
||||
*track_count = 0;
|
||||
streams = malloc(sizeof(stream) * *stream_count);
|
||||
|
||||
for (int i = 0; i < *stream_count; i++) {
|
||||
AVStream *stream = ctx->streams[i];
|
||||
const AVCodecParameters *codecpar = stream->codecpar;
|
||||
|
||||
if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO || codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
AVDictionaryEntry *languageptr = av_dict_get(stream->metadata, "language", NULL, 0);
|
||||
|
||||
*track_count += 1;
|
||||
streams[i] = (struct stream){
|
||||
codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? "VIDEO" : NULL,
|
||||
languageptr ? strdup(languageptr->value) : NULL,
|
||||
strdup(avcodec_get_name(codecpar->codec_id)),
|
||||
stream->disposition & AV_DISPOSITION_DEFAULT,
|
||||
stream->disposition & AV_DISPOSITION_FORCED,
|
||||
strdup(path)
|
||||
};
|
||||
}
|
||||
else
|
||||
streams[i] = NULLSTREAM;
|
||||
}
|
||||
avformat_close_input(&ctx);
|
||||
return (streams);
|
||||
}
|
||||
|
||||
int transmux(const char *path, const char *out_path, float *playable_duration)
|
||||
{
|
||||
AVFormatContext *in_ctx = NULL;
|
||||
AVFormatContext *out_ctx = NULL;
|
||||
AVStream *stream;
|
||||
AVPacket pkt;
|
||||
AVDictionary *options = NULL;
|
||||
int *stream_map;
|
||||
int stream_count;
|
||||
int ret = 0;
|
||||
std::string seg_path = ((std::string)out_path).substr(0, strrchr(out_path, '/') - out_path).append("/segments/");
|
||||
|
||||
*playable_duration = 0;
|
||||
if (open_input_context(&in_ctx, path) != 0)
|
||||
return 1;
|
||||
|
||||
if (avformat_alloc_output_context2(&out_ctx, NULL, NULL, out_path) < 0)
|
||||
{
|
||||
std::cout << "Error: Couldn't create an output file." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
stream_map = new int[in_ctx->nb_streams];
|
||||
stream_count = 0;
|
||||
|
||||
for (unsigned int i = 0; i < in_ctx->nb_streams; i++)
|
||||
{
|
||||
stream = in_ctx->streams[i];
|
||||
if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
|
||||
{
|
||||
stream_map[i] = stream_count;
|
||||
stream_count++;
|
||||
if (copy_stream_to_output(out_ctx, stream) == NULL)
|
||||
return 1;
|
||||
}
|
||||
else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) //Should support multi-audio on a good format.
|
||||
{
|
||||
stream_map[i] = stream_count;
|
||||
stream_count++;
|
||||
if (copy_stream_to_output(out_ctx, stream) == NULL)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
stream_map[i] = -1;
|
||||
}
|
||||
|
||||
av_dump_format(out_ctx, 0, out_path, true);
|
||||
std::filesystem::create_directory(seg_path);
|
||||
av_dict_set(&options, "hls_segment_filename", seg_path.append("%v-%03d.ts").c_str(), 0);
|
||||
av_dict_set(&options, "hls_base_url", "segment/", 0);
|
||||
av_dict_set(&options, "hls_list_size", "0", 0);
|
||||
av_dict_set(&options, "streaming", "1", 0);
|
||||
|
||||
if (open_output_file_for_write(out_ctx, out_path, &options) != 0)
|
||||
return 1;
|
||||
|
||||
while (av_read_frame(in_ctx, &pkt) == 0)
|
||||
{
|
||||
if ((unsigned int)pkt.stream_index >= in_ctx->nb_streams || stream_map[pkt.stream_index] < 0)
|
||||
{
|
||||
av_packet_unref(&pkt);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream = in_ctx->streams[pkt.stream_index];
|
||||
pkt.stream_index = stream_map[pkt.stream_index];
|
||||
process_packet(pkt, stream, out_ctx->streams[pkt.stream_index]);
|
||||
if (pkt.stream_index == 0)
|
||||
*playable_duration += pkt.duration * (float)out_ctx->streams[pkt.stream_index]->time_base.num / out_ctx->streams[pkt.stream_index]->time_base.den;
|
||||
|
||||
if (av_interleaved_write_frame(out_ctx, &pkt) < 0)
|
||||
std::cout << "Error while writing a packet to the output file." << std::endl;
|
||||
|
||||
av_packet_unref(&pkt);
|
||||
}
|
||||
|
||||
av_dict_free(&options);
|
||||
av_write_trailer(out_ctx);
|
||||
avformat_close_input(&in_ctx);
|
||||
|
||||
if (out_ctx && !(out_ctx->oformat->flags & AVFMT_NOFILE))
|
||||
avio_close(out_ctx->pb);
|
||||
avformat_free_context(out_ctx);
|
||||
delete[] stream_map;
|
||||
|
||||
if (ret < 0 && ret != AVERROR_EOF)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_memory(Stream *stream_ptr)
|
||||
{
|
||||
delete[] stream_ptr;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
@ -60,18 +60,20 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
public IActionResult Transcode(string showSlug, long seasonNumber, long episodeNumber)
|
||||
public async Task<IActionResult> Transcode(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
return null;
|
||||
//WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
|
||||
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
|
||||
|
||||
//if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
//{
|
||||
// string path = transcoder.Transcode(episode.Path);
|
||||
// return PhysicalFile(path, "video/mp4", true); //Should use mpeg dash
|
||||
//}
|
||||
//else
|
||||
// return NotFound();
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
{
|
||||
string path = await transcoder.Transcode(episode);
|
||||
if (path != null)
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
else
|
||||
return StatusCode(500);
|
||||
}
|
||||
else
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
@ -12,10 +12,12 @@ namespace Kyoo.InternalAPI
|
||||
public class Transcoder : ITranscoder
|
||||
{
|
||||
private readonly string transmuxPath;
|
||||
private readonly string transcodePath;
|
||||
|
||||
public Transcoder(IConfiguration config)
|
||||
{
|
||||
transmuxPath = config.GetValue<string>("transmuxTempPath");
|
||||
transcodePath = config.GetValue<string>("transcodeTempPath");
|
||||
|
||||
Debug.WriteLine("&Api INIT (unmanaged stream size): " + TranscoderAPI.Init() + ", Stream size: " + Marshal.SizeOf<Models.Watch.Stream>());
|
||||
}
|
||||
@ -61,10 +63,25 @@ namespace Kyoo.InternalAPI
|
||||
return transmuxFailed ? null : manifest;
|
||||
}
|
||||
|
||||
public Task<string> Transcode(WatchItem episode)
|
||||
public async Task<string> Transcode(WatchItem episode)
|
||||
{
|
||||
//NOT IMPLEMENTED YET
|
||||
return null;
|
||||
string folder = Path.Combine(transcodePath, episode.Link);
|
||||
string manifest = Path.Combine(folder, episode.Link + ".m3u8");
|
||||
float playableDuration = 0;
|
||||
bool transmuxFailed = false;
|
||||
|
||||
Directory.CreateDirectory(folder);
|
||||
Debug.WriteLine("&Transcoding " + episode.Link + " at " + episode.Path + ", outputPath: " + folder);
|
||||
|
||||
if (File.Exists(manifest))
|
||||
return manifest;
|
||||
Task.Run(() =>
|
||||
{
|
||||
transmuxFailed = TranscoderAPI.transcode(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0;
|
||||
});
|
||||
while (playableDuration < 10 || (!File.Exists(manifest) && !transmuxFailed))
|
||||
await Task.Delay(10);
|
||||
return transmuxFailed ? null : manifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ namespace Kyoo.InternalAPI.TranscoderLink
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static int transmux(string path, string out_path, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static int transcode(string path, string out_path, out float playableDuration);
|
||||
|
||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||
private extern static IntPtr get_track_info(string path, out int array_length, out int track_count);
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
"databasePath": "C://Projects/database.db",
|
||||
"transmuxTempPath": "C:\\\\Projects\\temp\\transmux",
|
||||
"transcodeTempPath": "C:\\\\Projects\\temp\\transcode",
|
||||
"peoplePath": "D:\\\\Videos\\People",
|
||||
"plugins": "C:\\Projects\\Kyoo\\Debug",
|
||||
"providerPlugins": "C://Projects/Plugins/Providers",
|
||||
|
Loading…
x
Reference in New Issue
Block a user