Reworked existing subtitle support (sideloaded, MP4 in and srt out)

This commit is contained in:
Ramoe 2017-11-23 09:13:43 +01:00 committed by Thulinma
parent 741c4755cc
commit b9e261e1ef
7 changed files with 159 additions and 33 deletions

38
lib/subtitles.cpp Normal file
View file

@ -0,0 +1,38 @@
#include "subtitles.h"
#include "bitfields.h"
#include "defines.h"
namespace Subtitle {
Packet getSubtitle(DTSC::Packet packet, DTSC::Meta meta) {
char * tmp = 0;
uint16_t length = 0;
unsigned int len;
Packet output;
long int trackId= packet.getTrackId();
if(meta.tracks[trackId].codec != "TTXT" && meta.tracks[trackId].codec != "SRT") {
//no subtitle track
return output;
}
if(packet.hasMember("duration")) {
output.duration = packet.getInt("duration");
} else {
//get parts from meta
//calculate duration
}
packet.getString("data", output.subtitle);
if(meta.tracks[trackId].codec == "TTXT") {
unsigned short size = Bit::btohs(output.subtitle.c_str());
output.subtitle = output.subtitle.substr(2,size);
}
return output;
}
}

15
lib/subtitles.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include <string>
#include "dtsc.h"
namespace Subtitle {
struct Packet {
std::string subtitle;
uint64_t duration;
};
Packet getSubtitle(DTSC::Packet packet, DTSC::Meta meta);
}

View file

@ -131,8 +131,8 @@ namespace Mist {
srtTrack = myMeta.tracks.rbegin()->first + 1;
myMeta.tracks[srtTrack].trackID = srtTrack;
myMeta.tracks[srtTrack].type = "subtitle";
myMeta.tracks[srtTrack].codec = "srt";
myMeta.tracks[srtTrack].type = "meta";
myMeta.tracks[srtTrack].codec = "subtitle";
getNextSrt();
while (srtPack){

View file

@ -14,7 +14,7 @@
#include "input_mp4.h"
namespace Mist {
namespace Mist{
mp4TrackHeader::mp4TrackHeader(){
initialised = false;
@ -136,7 +136,7 @@ namespace Mist {
size = stszBox.getEntrySize(index);
}
inputMP4::inputMP4(Util::Config * cfg) : Input(cfg) {
inputMP4::inputMP4(Util::Config * cfg) : Input(cfg){
malSize = 4;//initialise data read buffer to 0;
data = (char*)malloc(malSize);
capa["name"] = "MP4";
@ -156,18 +156,18 @@ namespace Mist {
free(data);
}
bool inputMP4::checkArguments() {
if (config->getString("input") == "-") {
bool inputMP4::checkArguments(){
if (config->getString("input") == "-"){
std::cerr << "Input from stdin not yet supported" << std::endl;
return false;
}
if (!config->getString("streamname").size()){
if (config->getString("output") == "-") {
if (config->getString("output") == "-"){
std::cerr << "Output to stdout not yet supported" << std::endl;
return false;
}
}else{
if (config->getString("output") != "-") {
if (config->getString("output") != "-"){
std::cerr << "File output in player mode not supported" << std::endl;
return false;
}
@ -176,18 +176,18 @@ namespace Mist {
return true;
}
bool inputMP4::preRun() {
bool inputMP4::preRun(){
//open File
inFile = fopen(config->getString("input").c_str(), "r");
if (!inFile) {
if (!inFile){
return false;
}
return true;
}
bool inputMP4::readHeader() {
if (!inFile) {
bool inputMP4::readHeader(){
if (!inFile){
INFO_MSG("inFile failed!");
return false;
}
@ -325,8 +325,8 @@ namespace Mist {
}
if (sType == "tx3g"){//plain text subtitles
myMeta.tracks[trackNo].type = "subtitle";
myMeta.tracks[trackNo].codec = "TTXT";
myMeta.tracks[trackNo].type = "meta";
myMeta.tracks[trackNo].codec = "subtitle";
}
MP4::STSS stssBox = stblBox.getChild<MP4::STSS>();
@ -407,7 +407,21 @@ namespace Mist {
}else{
BsetPart.timeOffset = 0;
}
myMeta.update(BsetPart.time, BsetPart.timeOffset, trackNo, stszBox.getEntrySize(stszIndex), BsetPart.bpos, BsetPart.keyframe);
if(sType == "tx3g"){
if(stszBox.getEntrySize(stszIndex) <=2 && false){
FAIL_MSG("size <=2");
}else{
long long packSendSize = 0;
packSendSize = 24 + (BsetPart.timeOffset ? 17 : 0) + (BsetPart.bpos ? 15 : 0) + 19 +
stszBox.getEntrySize(stszIndex) + 11-2 + 19;
myMeta.update(BsetPart.time, BsetPart.timeOffset, trackNo,
stszBox.getEntrySize(stszIndex) -2 , BsetPart.bpos, true,
packSendSize);
}
}else{
myMeta.update(BsetPart.time, BsetPart.timeOffset, trackNo, stszBox.getEntrySize(stszIndex), BsetPart.bpos, BsetPart.keyframe);
}
}
}
continue;
@ -424,7 +438,7 @@ namespace Mist {
return true;
}
void inputMP4::getNext(bool smart) {//get next part from track in stream
void inputMP4::getNext(bool smart){//get next part from track in stream
if (curPositions.empty()){
thisPacket.null();
return;
@ -459,13 +473,31 @@ namespace Mist {
return;
}
if (myMeta.tracks[curPart.trackID].codec == "TTXT"){
if (myMeta.tracks[curPart.trackID].codec == "subtitle"){
unsigned int txtLen = Bit::btohs(data);
if (!txtLen){
thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, " ", 1, 0/*Note: no bpos*/, isKeyframe);
if (!txtLen && false ){
curPart.index ++;
return getNext(smart);
//thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, " ", 1, 0/*Note: no bpos*/, isKeyframe);
}else{
thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data+2, txtLen, 0/*Note: no bpos*/, isKeyframe);
static JSON::Value thisPack;
thisPack.null();
thisPack["trackid"] = (long long)curPart.trackID;
thisPack["bpos"] = (long long)curPart.bpos; //(long long)fileSource.tellg();
thisPack["data"] = std::string(data+2,txtLen);
// thisPack["index"] = index;
thisPack["time"] = (long long)curPart.time;
thisPack["duration"] = 1000;
// thisPack["time"] = (long long)timestamp;
thisPack["keyframe"] = true;
// Write the json value to lastpack
std::string tmpStr = thisPack.toNetPacked();
thisPacket.reInit(tmpStr.data(), tmpStr.size());
//return;
//thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data+2, txtLen, 0/*Note: no bpos*/, isKeyframe);
}
}else{
thisPacket.genericFill(curPart.time, curPart.offset, curPart.trackID, data, curPart.size, 0/*Note: no bpos*/, isKeyframe);
@ -479,7 +511,7 @@ namespace Mist {
}
}
void inputMP4::seek(int seekTime) {//seek to a point
void inputMP4::seek(int seekTime){//seek to a point
nextKeyframe.clear();
//for all tracks
curPositions.clear();
@ -509,16 +541,16 @@ namespace Mist {
}//rof all tracks
}
void inputMP4::trackSelect(std::string trackSpec) {
void inputMP4::trackSelect(std::string trackSpec){
selectedTracks.clear();
long long int index;
while (trackSpec != "") {
while (trackSpec != ""){
index = trackSpec.find(' ');
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
VERYHIGH_MSG("Added track %d, index = %lld, (index == npos) = %d", atoi(trackSpec.substr(0, index).c_str()), index, index == std::string::npos);
if (index != std::string::npos) {
if (index != std::string::npos){
trackSpec.erase(0, index + 1);
} else {
}else{
trackSpec = "";
}
}

View file

@ -35,12 +35,19 @@ namespace Mist {
if (audioId != -1) {
bWidth += myMeta.tracks[audioId].bps;
}
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << (bWidth * 8) << "\r\n";
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,SUBTITLES=\"sub1\",BANDWIDTH=" << (bWidth * 8) << "\r\n";
result << it->first;
if (audioId != -1) {
result << "_" << audioId;
}
result << "/index.m3u8?sessId=" << getpid() << "\r\n";
}else if(it->second.codec == "subtitle"){
if(it->second.lang.empty()){
it->second.lang = "und";
}
result << "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"sub1\",LANGUAGE=\"" << it->second.lang << "\",NAME=\"" << Encodings::ISO639::decode(it->second.lang) << "\",AUTOSELECT=NO,DEFAULT=NO,FORCED=NO,URI=\"" << it->first << "/index.m3u8\"" << "\r\n";
}
}
if (!vidTracks && audioId) {
@ -145,10 +152,15 @@ namespace Mist {
duration = myMeta.tracks[tid].lastms - starttime;
}
char lineBuf[400];
if (sessId.size()){
snprintf(lineBuf, 400, "#EXTINF:%f,\r\n%lld_%lld.ts?sessId=%s\r\n", (double)duration/1000, starttime, starttime + duration, sessId.c_str());
if(myMeta.tracks[tid].codec == "subtitle"){
snprintf(lineBuf, 400, "#EXTINF:%f,\r\n../../../%s.vtt?track=%d&from=%lld&to=%lld\r\n", streamName.c_str(),(double)duration/1000,tid, starttime, starttime + duration);
}else{
snprintf(lineBuf, 400, "#EXTINF:%f,\r\n%lld_%lld.ts\r\n", (double)duration/1000, starttime, starttime + duration);
if (sessId.size()){
snprintf(lineBuf, 400, "#EXTINF:%f,\r\n%lld_%lld.ts?sessId=%s\r\n", (double)duration/1000, starttime, starttime + duration, sessId.c_str());
}else{
snprintf(lineBuf, 400, "#EXTINF:%f,\r\n%lld_%lld.ts\r\n", (double)duration/1000, starttime, starttime + duration);
}
}
durs.push_back(duration);
total_dur += duration;

View file

@ -14,8 +14,7 @@ namespace Mist {
capa["desc"] = "Enables HTTP protocol subtitle streaming in subrip and WebVTT formats.";
capa["url_match"].append("/$.srt");
capa["url_match"].append("/$.vtt");
capa["codecs"][0u][0u].append("srt");
capa["codecs"][0u][0u].append("TTXT");
capa["codecs"][0u][0u].append("subtitle");
capa["methods"][0u]["handler"] = "http";
capa["methods"][0u]["type"] = "html5/text/plain";
capa["methods"][0u]["priority"] = 8ll;
@ -30,6 +29,7 @@ namespace Mist {
char * dataPointer = 0;
unsigned int len = 0;
thisPacket.getString("data", dataPointer, len);
// INFO_MSG("getting sub: %s", dataPointer);
//ignore empty subs
if (len == 0 || (len == 1 && dataPointer[0] == ' ')){
return;
@ -39,6 +39,20 @@ namespace Mist {
tmp << lastNum++ << std::endl;
}
long long unsigned int time = thisPacket.getTime();
//filter subtitle in specific timespan
if(filter_from > 0 && time < filter_from){
index++; //when using seek, the index is lost.
seek(filter_from);
return;
}
if(filter_to > 0 && time > filter_to && filter_to > filter_from){
config->is_active = false;
return;
}
char tmpBuf[50];
int tmpLen = sprintf(tmpBuf, "%.2llu:%.2llu:%.2llu.%.3llu", (time / 3600000), ((time % 3600000) / 60000), (((time % 3600000) % 60000) / 1000), time % 1000);
tmp.write(tmpBuf, tmpLen);
@ -79,6 +93,18 @@ namespace Mist {
selectedTracks.clear();
selectedTracks.insert(JSON::Value(H.GetVar("track")).asInt());
}
filter_from = 0;
filter_to = 0;
index = 0;
if (H.GetVar("from") != ""){
filter_from = JSON::Value(H.GetVar("from")).asInt();
}
if (H.GetVar("to") != ""){
filter_to = JSON::Value(H.GetVar("to")).asInt();
}
H.Clean();
H.setCORSHeaders();
if(method == "OPTIONS" || method == "HEAD"){

View file

@ -13,6 +13,9 @@ namespace Mist {
protected:
bool webVTT;
int lastNum;
uint32_t filter_from;
uint32_t filter_to;
uint32_t index;
};
}