344 lines
12 KiB
C++
344 lines
12 KiB
C++
#include "singleFileMuxer.h"
|
|
|
|
#include <fs/directory.h>
|
|
#include <fs/textfile.h>
|
|
|
|
#include "abstractMuxer.h"
|
|
#include "ac3StreamReader.h"
|
|
#include "avCodecs.h"
|
|
#include "lpcmStreamReader.h"
|
|
#include "mpegAudioStreamReader.h"
|
|
#include "muxerManager.h"
|
|
#include "psgStreamReader.h"
|
|
#include "srtStreamReader.h"
|
|
#include "vodCoreException.h"
|
|
|
|
#ifndef win32
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
std::string getNewName(const std::string& oldName, int cnt)
|
|
{
|
|
if (strEndWith(oldName, ".wav"))
|
|
return oldName.substr(0, oldName.size() - 4) + "." + int32ToStr(cnt) + ".wav";
|
|
else
|
|
return oldName + ".wav" + int32ToStr(cnt);
|
|
}
|
|
|
|
SingleFileMuxer::SingleFileMuxer(MuxerManager* owner) : AbstractMuxer(owner), m_lastIndex(-1) {}
|
|
|
|
SingleFileMuxer::~SingleFileMuxer()
|
|
{
|
|
for (auto itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr) delete itr->second;
|
|
}
|
|
|
|
void SingleFileMuxer::intAddStream(const std::string& streamName, const std::string& codecName, int streamIndex,
|
|
const map<string, string>& params, AbstractStreamReader* codecReader)
|
|
{
|
|
codecReader->setDemuxMode(true);
|
|
string fileExt = "track";
|
|
|
|
// this call seemingly does nothing, but it actually makes some
|
|
// StreamReaders refresh/set their private variables, enabling proper
|
|
// codec->extension mapping in here. don't remove it.
|
|
uint8_t descrBuffer[188];
|
|
codecReader->getTSDescriptor(descrBuffer, true, true);
|
|
|
|
if (codecName == "A_AAC")
|
|
{
|
|
fileExt = ".aac";
|
|
}
|
|
else if (codecName == "A_MP3")
|
|
{
|
|
const auto mp3Reader = static_cast<MpegAudioStreamReader*>(codecReader);
|
|
if (mp3Reader->getLayer() == 3)
|
|
fileExt = ".mp3";
|
|
else
|
|
fileExt = ".mpa";
|
|
}
|
|
else if (codecName == "A_MLP")
|
|
{
|
|
fileExt = ".thd";
|
|
}
|
|
else if (codecName == "A_DTS")
|
|
{
|
|
fileExt = ".dts";
|
|
}
|
|
else if (codecName == "A_LPCM")
|
|
{
|
|
fileExt = ".wav";
|
|
}
|
|
else if (codecName == "A_AC3")
|
|
{
|
|
const auto ac3Reader = static_cast<AC3StreamReader*>(codecReader);
|
|
if (ac3Reader->isTrueHD() && !ac3Reader->getDownconvertToAC3())
|
|
fileExt = ".ac3+thd";
|
|
else if (ac3Reader->isEAC3())
|
|
fileExt = ".ec3";
|
|
else
|
|
fileExt = ".ac3";
|
|
}
|
|
else if (codecName == "S_SUP")
|
|
{
|
|
fileExt = ".sup";
|
|
}
|
|
else if (codecName == "S_HDMV/PGS")
|
|
{
|
|
fileExt = ".sup";
|
|
}
|
|
else if (codecName == "S_TEXT/UTF8")
|
|
{
|
|
fileExt = ".sup";
|
|
}
|
|
else if (codecName[0] == 'V')
|
|
{
|
|
if (codecName == "V_MS/VFW/WVC1")
|
|
{
|
|
fileExt = ".vc1";
|
|
}
|
|
else if (codecName == "V_MPEG4/ISO/AVC")
|
|
{
|
|
fileExt = ".264";
|
|
}
|
|
else if (codecName == "V_MPEG4/ISO/MVC")
|
|
{
|
|
fileExt = ".mvc";
|
|
}
|
|
else if (codecName == "V_MPEGH/ISO/HEVC")
|
|
{
|
|
fileExt = ".hevc";
|
|
}
|
|
else if (codecName == "V_MPEGI/ISO/VVC")
|
|
{
|
|
fileExt = ".vvc";
|
|
}
|
|
else
|
|
{
|
|
fileExt = ".mpv";
|
|
}
|
|
}
|
|
|
|
vector<string> fileList = extractFileList(streamName);
|
|
string fileName;
|
|
if (fileList.size() < 3)
|
|
for (int i = 0; i < (int)fileList.size(); i++)
|
|
{
|
|
if (i > 0)
|
|
fileName += '+';
|
|
fileName += extractFileName(fileList[i]);
|
|
}
|
|
else
|
|
fileName = extractFileName(fileList[0]) + "+___+" + extractFileName(fileList[fileList.size() - 1]);
|
|
|
|
auto itr = params.find("track");
|
|
if (itr != params.end())
|
|
{
|
|
if (params.find("subClip") != params.end())
|
|
fileName += ".subclip_track_";
|
|
else
|
|
fileName += ".track_";
|
|
fileName += itr->second;
|
|
}
|
|
|
|
int cnt = ++m_trackNameTmp[fileName];
|
|
if (cnt > 1)
|
|
{
|
|
fileName += "_";
|
|
fileName += int32ToStr(cnt);
|
|
}
|
|
|
|
itr = params.find("lang");
|
|
if (itr != params.end())
|
|
{
|
|
fileName += "_";
|
|
fileName += itr->second;
|
|
}
|
|
|
|
StreamInfo* streamInfo = new StreamInfo((unsigned)DEFAULT_FILE_BLOCK_SIZE);
|
|
streamInfo->m_fileName = fileName + fileExt;
|
|
if (streamInfo->m_fileName.size() > 254)
|
|
LTRACE(LT_ERROR, 2, "Error: File name too long.");
|
|
streamInfo->m_codecReader = codecReader;
|
|
m_streamInfo[streamIndex] = streamInfo;
|
|
}
|
|
|
|
void SingleFileMuxer::openDstFile()
|
|
{
|
|
string dir = closeDirPath(toNativeSeparators(m_origFileName));
|
|
// if (!createDir(dstFileName, true))
|
|
// THROW(ERR_CANT_CREATE_FILE, "Can't create output directory " << dstFileName);
|
|
int systemFlags = 0;
|
|
#ifdef _WIN32
|
|
if (m_owner->isAsyncMode())
|
|
systemFlags += FILE_FLAG_NO_BUFFERING;
|
|
#endif
|
|
for (auto itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
itr->second->m_fileName = dir + itr->second->m_fileName;
|
|
if (!itr->second->m_file.open(itr->second->m_fileName.c_str(), File::ofWrite, systemFlags))
|
|
THROW(ERR_CANT_CREATE_FILE, "Can't create output file " << itr->second->m_fileName);
|
|
}
|
|
}
|
|
|
|
void SingleFileMuxer::writeOutBuffer(StreamInfo* streamInfo)
|
|
{
|
|
const uint32_t blockSize = DEFAULT_FILE_BLOCK_SIZE;
|
|
if (streamInfo->m_bufLen >= blockSize)
|
|
{
|
|
int toFileLen = blockSize & 0xffff0000;
|
|
if (m_owner->isAsyncMode())
|
|
{
|
|
uint8_t* newBuf = new uint8_t[blockSize + MAX_AV_PACKET_SIZE];
|
|
memcpy(newBuf, streamInfo->m_buffer + toFileLen, streamInfo->m_bufLen - toFileLen);
|
|
m_owner->asyncWriteBuffer(this, streamInfo->m_buffer, toFileLen, &streamInfo->m_file);
|
|
streamInfo->m_buffer = newBuf;
|
|
}
|
|
else
|
|
{
|
|
m_owner->syncWriteBuffer(this, streamInfo->m_buffer, toFileLen, &streamInfo->m_file);
|
|
memmove(streamInfo->m_buffer, streamInfo->m_buffer + toFileLen, streamInfo->m_bufLen - toFileLen);
|
|
}
|
|
streamInfo->m_totalWrited += toFileLen;
|
|
streamInfo->m_bufLen -= toFileLen;
|
|
}
|
|
|
|
auto lpcmReader = dynamic_cast<LPCMStreamReader*>(streamInfo->m_codecReader);
|
|
if (lpcmReader && streamInfo->m_totalWrited >= 0xffff0000ul - blockSize)
|
|
// if (lpcmReader && streamInfo->m_totalWrited >= 0x0ffffffful)
|
|
{
|
|
if (m_owner->isAsyncMode())
|
|
m_owner->waitForWriting();
|
|
streamInfo->m_file.close();
|
|
streamInfo->m_file.open(streamInfo->m_fileName.c_str(), File::ofWrite + File::ofAppend);
|
|
streamInfo->m_file.write(streamInfo->m_buffer, streamInfo->m_bufLen);
|
|
streamInfo->m_file.close();
|
|
streamInfo->m_file.open(streamInfo->m_fileName.c_str(), File::ofWrite + File::ofNoTruncate);
|
|
lpcmReader->beforeFileCloseEvent(streamInfo->m_file);
|
|
streamInfo->m_file.close();
|
|
std::string newName = getNewName(streamInfo->m_fileName.c_str(), streamInfo->m_part);
|
|
deleteFile(newName.c_str());
|
|
if (rename(streamInfo->m_fileName.c_str(), newName.c_str()) != 0)
|
|
THROW(ERR_COMMON, "Can't rename file " << streamInfo->m_fileName << " to " << newName);
|
|
streamInfo->m_part++;
|
|
int systemFlags = 0;
|
|
streamInfo->m_bufLen = 0;
|
|
#ifdef _WIN32
|
|
if (m_owner->isAsyncMode())
|
|
systemFlags += FILE_FLAG_NO_BUFFERING;
|
|
#endif
|
|
if (!streamInfo->m_file.open(streamInfo->m_fileName.c_str(), File::ofWrite + systemFlags))
|
|
THROW(ERR_COMMON, "Can't open file " << streamInfo->m_fileName);
|
|
lpcmReader->setFirstFrame(true);
|
|
streamInfo->m_totalWrited = 0;
|
|
}
|
|
}
|
|
|
|
bool SingleFileMuxer::muxPacket(AVPacket& avPacket)
|
|
{
|
|
if (avPacket.data == nullptr || avPacket.size == 0)
|
|
return true;
|
|
StreamInfo* streamInfo = m_streamInfo[avPacket.stream_index];
|
|
if (avPacket.dts != streamInfo->m_dts || avPacket.pts != streamInfo->m_pts ||
|
|
m_lastIndex != avPacket.stream_index || avPacket.flags & AVPacket::FORCE_NEW_FRAME)
|
|
{
|
|
const uint32_t blockSize = DEFAULT_FILE_BLOCK_SIZE;
|
|
streamInfo->m_bufLen += avPacket.codec->writeAdditionData(
|
|
streamInfo->m_buffer + streamInfo->m_bufLen,
|
|
streamInfo->m_buffer + blockSize + MAX_AV_PACKET_SIZE + ADD_DATA_SIZE, avPacket, 0);
|
|
writeOutBuffer(streamInfo);
|
|
}
|
|
m_lastIndex = avPacket.stream_index;
|
|
streamInfo->m_dts = avPacket.dts;
|
|
streamInfo->m_pts = avPacket.pts;
|
|
memcpy(streamInfo->m_buffer + streamInfo->m_bufLen, avPacket.data, avPacket.size);
|
|
streamInfo->m_bufLen += avPacket.size;
|
|
writeOutBuffer(streamInfo);
|
|
return true;
|
|
}
|
|
|
|
bool SingleFileMuxer::doFlush()
|
|
{
|
|
for (auto itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
StreamInfo* streamInfo = itr->second;
|
|
unsigned lastBlockSize = streamInfo->m_bufLen & 0xffff; // last 64K of data
|
|
unsigned roundBufLen = streamInfo->m_bufLen & 0xffff0000;
|
|
if (m_owner->isAsyncMode())
|
|
{
|
|
if (lastBlockSize > 0)
|
|
{
|
|
auto newBuff = new uint8_t[lastBlockSize];
|
|
memcpy(newBuff, streamInfo->m_buffer + roundBufLen, lastBlockSize);
|
|
m_owner->asyncWriteBuffer(this, streamInfo->m_buffer, roundBufLen, &streamInfo->m_file);
|
|
streamInfo->m_buffer = newBuff;
|
|
}
|
|
else
|
|
{
|
|
m_owner->asyncWriteBuffer(this, streamInfo->m_buffer, roundBufLen, &streamInfo->m_file);
|
|
streamInfo->m_buffer = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_owner->syncWriteBuffer(this, streamInfo->m_buffer, roundBufLen, &streamInfo->m_file);
|
|
memmove(streamInfo->m_buffer, streamInfo->m_buffer + roundBufLen, lastBlockSize);
|
|
}
|
|
streamInfo->m_bufLen = lastBlockSize;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SingleFileMuxer::close()
|
|
{
|
|
for (auto itr = m_streamInfo.begin(); itr != m_streamInfo.end(); ++itr)
|
|
{
|
|
StreamInfo* streamInfo = itr->second;
|
|
if (!streamInfo->m_file.close())
|
|
return false;
|
|
if (streamInfo->m_bufLen > 0)
|
|
{
|
|
if (!streamInfo->m_file.open(streamInfo->m_fileName.c_str(), File::ofWrite + File::ofAppend))
|
|
return false;
|
|
if (!streamInfo->m_file.write(streamInfo->m_buffer, streamInfo->m_bufLen))
|
|
return false;
|
|
if (streamInfo->m_codecReader)
|
|
if (!streamInfo->m_codecReader->beforeFileCloseEvent(streamInfo->m_file))
|
|
return false;
|
|
if (!streamInfo->m_file.close())
|
|
return false;
|
|
|
|
if (streamInfo->m_part > 1)
|
|
{
|
|
std::string newName = getNewName(streamInfo->m_fileName.c_str(), streamInfo->m_part);
|
|
deleteFile(newName.c_str());
|
|
if (rename(streamInfo->m_fileName.c_str(), newName.c_str()) != 0)
|
|
THROW(ERR_COMMON, "Can't rename file " << streamInfo->m_fileName << " to " << newName);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SingleFileMuxer::parseMuxOpt(const std::string& opts)
|
|
{
|
|
vector<string> params = splitStr(opts.c_str(), ' ');
|
|
for (auto& i : params)
|
|
{
|
|
vector<string> paramPair = splitStr(trimStr(i).c_str(), '=');
|
|
if (paramPair.size() == 0)
|
|
continue;
|
|
if (paramPair[0] == "--split-duration")
|
|
{
|
|
LTRACE(LT_WARN, 2,
|
|
"Warning! Splitting does not implemented for demux mode. Parameter " << paramPair[0] << " ignored.");
|
|
}
|
|
if (paramPair[0] == "--split-size")
|
|
{
|
|
LTRACE(LT_WARN, 2,
|
|
"Warning! Splitting does not implemented for demux mode. Parameter " << paramPair[0] << " ignored.");
|
|
}
|
|
}
|
|
}
|