169 lines
5.5 KiB
C++
169 lines
5.5 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
********************************************************************************/
|
|
|
|
|
|
#include "Main.h"
|
|
#include "RTMPStuff.h"
|
|
|
|
#include "DataPacketHelpers.h"
|
|
|
|
|
|
|
|
class FLVFileStream : public VideoFileStream
|
|
{
|
|
XFileOutputSerializer fileOut;
|
|
String strFile;
|
|
|
|
UINT64 metaDataPos;
|
|
DWORD lastTimeStamp, initialTimestamp;
|
|
|
|
decltype(GetBufferedSEIPacket()) sei = GetBufferedSEIPacket();
|
|
decltype(GetBufferedAudioHeadersPacket()) audioHeaders = GetBufferedAudioHeadersPacket();
|
|
decltype(GetBufferedVideoHeadersPacket()) videoHeaders = GetBufferedVideoHeadersPacket();
|
|
|
|
bool bSentFirstPacket, bSentSEI;
|
|
|
|
void AppendFLVPacket(const BYTE *lpData, UINT size, BYTE type, DWORD timestamp)
|
|
{
|
|
if (!bSentSEI && type == 9 && lpData[0] == 0x17 && lpData[1] == 0x1) { //send SEI with first keyframe packet
|
|
UINT networkDataSize = fastHtonl(size+sei.size);
|
|
UINT networkTimestamp = fastHtonl(timestamp);
|
|
UINT streamID = 0;
|
|
fileOut.OutputByte(type);
|
|
fileOut.Serialize(((LPBYTE)(&networkDataSize))+1, 3);
|
|
fileOut.Serialize(((LPBYTE)(&networkTimestamp))+1, 3);
|
|
fileOut.Serialize(&networkTimestamp, 1);
|
|
fileOut.Serialize(&streamID, 3);
|
|
fileOut.Serialize(lpData, 5);
|
|
fileOut.Serialize(sei.lpPacket, sei.size);
|
|
fileOut.Serialize(lpData+5, size-5);
|
|
fileOut.OutputDword(fastHtonl(size+sei.size+11));
|
|
|
|
bSentSEI = true;
|
|
} else {
|
|
UINT networkDataSize = fastHtonl(size);
|
|
UINT networkTimestamp = fastHtonl(timestamp);
|
|
UINT streamID = 0;
|
|
fileOut.OutputByte(type);
|
|
fileOut.Serialize(((LPBYTE)(&networkDataSize))+1, 3);
|
|
fileOut.Serialize(((LPBYTE)(&networkTimestamp))+1, 3);
|
|
fileOut.Serialize(&networkTimestamp, 1);
|
|
fileOut.Serialize(&streamID, 3);
|
|
fileOut.Serialize(lpData, size);
|
|
fileOut.OutputDword(fastHtonl(size+11));
|
|
}
|
|
|
|
lastTimeStamp = timestamp;
|
|
}
|
|
|
|
public:
|
|
bool Init(CTSTR lpFile)
|
|
{
|
|
strFile = lpFile;
|
|
initialTimestamp = -1;
|
|
|
|
if(!fileOut.Open(lpFile, XFILE_CREATEALWAYS, 1024*1024))
|
|
return false;
|
|
|
|
fileOut.OutputByte('F');
|
|
fileOut.OutputByte('L');
|
|
fileOut.OutputByte('V');
|
|
fileOut.OutputByte(1);
|
|
fileOut.OutputByte(5); //bit 0 = (hasAudio), bit 2 = (hasAudio)
|
|
fileOut.OutputDword(DWORD_BE(9));
|
|
fileOut.OutputDword(0);
|
|
|
|
metaDataPos = fileOut.GetPos();
|
|
|
|
char metaDataBuffer[2048];
|
|
char *enc = metaDataBuffer;
|
|
char *pend = metaDataBuffer+sizeof(metaDataBuffer);
|
|
|
|
enc = AMF_EncodeString(enc, pend, &av_onMetaData);
|
|
char *endMetaData = App->EncMetaData(enc, pend, true);
|
|
UINT metaDataSize = endMetaData-metaDataBuffer;
|
|
|
|
AppendFLVPacket((LPBYTE)metaDataBuffer, metaDataSize, 18, 0);
|
|
return true;
|
|
}
|
|
|
|
~FLVFileStream()
|
|
{
|
|
UINT64 fileSize = fileOut.GetPos();
|
|
fileOut.Close();
|
|
|
|
XFile file;
|
|
if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING))
|
|
{
|
|
double doubleFileSize = double(fileSize);
|
|
double doubleDuration = double(lastTimeStamp/1000);
|
|
|
|
file.SetPos(metaDataPos+0x28, XFILE_BEGIN);
|
|
QWORD outputVal = *reinterpret_cast<QWORD*>(&doubleDuration);
|
|
outputVal = fastHtonll(outputVal);
|
|
file.Write(&outputVal, 8);
|
|
|
|
file.SetPos(metaDataPos+0x3B, XFILE_BEGIN);
|
|
outputVal = *reinterpret_cast<QWORD*>(&doubleFileSize);
|
|
outputVal = fastHtonll(outputVal);
|
|
file.Write(&outputVal, 8);
|
|
|
|
file.Close();
|
|
}
|
|
}
|
|
|
|
void InitBufferedPackets()
|
|
{
|
|
sei.InitBuffer();
|
|
audioHeaders.InitBuffer();
|
|
videoHeaders.InitBuffer();
|
|
}
|
|
|
|
virtual void AddPacket(const BYTE *data, UINT size, DWORD timestamp, DWORD /*pts*/, PacketType type) override
|
|
{
|
|
InitBufferedPackets();
|
|
|
|
if(!bSentFirstPacket)
|
|
{
|
|
bSentFirstPacket = true;
|
|
|
|
AppendFLVPacket(audioHeaders.lpPacket, audioHeaders.size, 8, 0);
|
|
AppendFLVPacket(videoHeaders.lpPacket, videoHeaders.size, 9, 0);
|
|
}
|
|
|
|
if(initialTimestamp == -1 && data[0] != 0x17)
|
|
return;
|
|
else if(initialTimestamp == -1 && data[0] == 0x17) {
|
|
initialTimestamp = timestamp;
|
|
}
|
|
|
|
AppendFLVPacket(data, size, (type == PacketType_Audio) ? 8 : 9, timestamp-initialTimestamp);
|
|
}
|
|
};
|
|
|
|
|
|
VideoFileStream* CreateFLVFileStream(CTSTR lpFile)
|
|
{
|
|
FLVFileStream *fileStream = new FLVFileStream;
|
|
if(fileStream->Init(lpFile))
|
|
return fileStream;
|
|
|
|
delete fileStream;
|
|
return NULL;
|
|
}
|