181 lines
5.8 KiB
C++
181 lines
5.8 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <sys/types.h>//for stat
|
|
#include <sys/stat.h>//for stat
|
|
#include <unistd.h>//for stat
|
|
#include <mist/util.h>
|
|
#include <mist/stream.h>
|
|
#include <mist/defines.h>
|
|
|
|
#include "input_flv.h"
|
|
|
|
namespace Mist {
|
|
inputFLV::inputFLV(Util::Config * cfg) : Input(cfg) {
|
|
capa["name"] = "FLV";
|
|
capa["desc"] = "Allows loading FLV files for Video on Demand.";
|
|
capa["source_match"] = "/*.flv";
|
|
capa["priority"] = 9ll;
|
|
capa["codecs"][0u][0u].append("H264");
|
|
capa["codecs"][0u][0u].append("H263");
|
|
capa["codecs"][0u][0u].append("VP6");
|
|
capa["codecs"][0u][1u].append("AAC");
|
|
capa["codecs"][0u][1u].append("MP3");
|
|
}
|
|
|
|
bool inputFLV::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") == "-") {
|
|
std::cerr << "Output to stdout not yet supported" << std::endl;
|
|
return false;
|
|
}
|
|
}else{
|
|
if (config->getString("output") != "-") {
|
|
std::cerr << "File output in player mode not supported" << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool inputFLV::preRun() {
|
|
//open File
|
|
inFile = fopen(config->getString("input").c_str(), "r");
|
|
if (!inFile) {
|
|
return false;
|
|
}
|
|
struct stat statData;
|
|
lastModTime = 0;
|
|
if (stat(config->getString("input").c_str(), &statData) != -1){
|
|
lastModTime = statData.st_mtime;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Overrides the default keepRunning function to shut down
|
|
/// if the file disappears or changes, by polling the file's mtime.
|
|
/// If neither applies, calls the original function.
|
|
bool inputFLV::keepRunning(){
|
|
struct stat statData;
|
|
if (stat(config->getString("input").c_str(), &statData) == -1){
|
|
INFO_MSG("Shutting down because input file disappeared");
|
|
return false;
|
|
}
|
|
if (lastModTime != statData.st_mtime){
|
|
INFO_MSG("Shutting down because input file changed");
|
|
return false;
|
|
}
|
|
return Input::keepRunning();
|
|
}
|
|
|
|
bool inputFLV::readHeader() {
|
|
if (!inFile){return false;}
|
|
//Create header file from FLV data
|
|
Util::fseek(inFile, 13, SEEK_SET);
|
|
AMF::Object amf_storage;
|
|
long long int lastBytePos = 13;
|
|
uint64_t bench = Util::getMicros();
|
|
while (!feof(inFile) && !FLV::Parse_Error){
|
|
if (tmpTag.FileLoader(inFile)){
|
|
tmpTag.toMeta(myMeta, amf_storage);
|
|
if (!tmpTag.getDataLen()){continue;}
|
|
if (tmpTag.needsInitData() && tmpTag.isInitData()){continue;}
|
|
myMeta.update(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe);
|
|
lastBytePos = Util::ftell(inFile);
|
|
}
|
|
}
|
|
bench = Util::getMicros(bench);
|
|
INFO_MSG("Header generated in %llu ms: @%lld, %s, %s", bench/1000, lastBytePos, myMeta.vod?"VoD":"NOVoD", myMeta.live?"Live":"NOLive");
|
|
if (FLV::Parse_Error){
|
|
tmpTag = FLV::Tag();
|
|
FLV::Parse_Error = false;
|
|
ERROR_MSG("Stopping at FLV parse error @%lld: %s", lastBytePos, FLV::Error_Str.c_str());
|
|
}
|
|
myMeta.toFile(config->getString("input") + ".dtsh");
|
|
Util::fseek(inFile, 13, SEEK_SET);
|
|
return true;
|
|
}
|
|
|
|
void inputFLV::getNext(bool smart) {
|
|
long long int lastBytePos = Util::ftell(inFile);
|
|
if (selectedTracks.size() == 1){
|
|
uint8_t targetTag = 0x08;
|
|
if (selectedTracks.count(1)){targetTag = 0x09;}
|
|
if (selectedTracks.count(3)){targetTag = 0x12;}
|
|
FLV::seekToTagType(inFile, targetTag);
|
|
}
|
|
while (!feof(inFile) && !FLV::Parse_Error){
|
|
if (tmpTag.FileLoader(inFile)){
|
|
if ( !selectedTracks.count(tmpTag.getTrackID())){
|
|
lastBytePos = Util::ftell(inFile);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (feof(inFile)){
|
|
thisPacket.null();
|
|
return;
|
|
}
|
|
if (FLV::Parse_Error){
|
|
FLV::Parse_Error = false;
|
|
tmpTag = FLV::Tag();
|
|
FAIL_MSG("FLV error @ %lld: %s", lastBytePos, FLV::Error_Str.c_str());
|
|
thisPacket.null();
|
|
return;
|
|
}
|
|
if (!tmpTag.getDataLen() || (tmpTag.needsInitData() && tmpTag.isInitData())){
|
|
return getNext();
|
|
}
|
|
thisPacket.genericFill(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getData(), tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe); //init packet from tmpTags data
|
|
|
|
DTSC::Track & trk = myMeta.tracks[tmpTag.getTrackID()];
|
|
if (trk.codec == "PCM" && trk.size == 16){
|
|
char * ptr = 0;
|
|
uint32_t ptrSize = 0;
|
|
thisPacket.getString("data", ptr, ptrSize);
|
|
for (uint32_t i = 0; i < ptrSize; i+=2){
|
|
char tmpchar = ptr[i];
|
|
ptr[i] = ptr[i+1];
|
|
ptr[i+1] = tmpchar;
|
|
}
|
|
}
|
|
}
|
|
|
|
void inputFLV::seek(int seekTime) {
|
|
//We will seek to the corresponding keyframe of the video track if selected, otherwise audio keyframe.
|
|
//Flv files are never multi-track, so track 1 is video, track 2 is audio.
|
|
int trackSeek = (selectedTracks.count(1) ? 1 : 2);
|
|
uint64_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos();
|
|
for (unsigned int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){
|
|
if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){
|
|
break;
|
|
}
|
|
seekPos = myMeta.tracks[trackSeek].keys[i].getBpos();
|
|
}
|
|
Util::fseek(inFile, seekPos, SEEK_SET);
|
|
}
|
|
|
|
void inputFLV::trackSelect(std::string trackSpec) {
|
|
selectedTracks.clear();
|
|
size_t index;
|
|
while (trackSpec != "") {
|
|
index = trackSpec.find(' ');
|
|
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
|
|
if (index != std::string::npos) {
|
|
trackSpec.erase(0, index + 1);
|
|
} else {
|
|
trackSpec = "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|