mirror of
https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
synced 2023-10-10 19:16:51 +02:00
008e7c8510
- increased WRITERBUFSIZE - buffer was too small for high bandwidth content - removed cStreamdevStreamer::m_Running - eliminated potential busy waits in remuxers - updated cTSRemux static helpers to code of their VDR 1.6.0 counterparts - re-enabled PES vor VDR 1.7.3+. Streamdev now uses a copy of VDR 1.6.0's cRemux for TS to PES remuxing. - make sure that only complete TS packets are written to ringbuffers - use signaling instead of sleeps when writing to ringbuffers - optimized cStreamdevPatFilter PAT packet initialization - fixed cStreamdevPatFilter not processing PATs with length > TS_SIZE - 5 - use a small ringbuffer for cStreamdevPatFilter instead of writing to cStreamdevStreamers SendBuffer as two threads mustn't write to the same ringbuffer Modified Files: CONTRIBUTORS HISTORY Makefile common.c common.h streamdev-server.c libdvbmpeg/transform.h remux/extern.c remux/extern.h remux/ts2es.c remux/ts2es.h remux/ts2ps.c remux/ts2ps.h remux/tsremux.c remux/tsremux.h server/connectionHTTP.c server/connectionVTP.c server/livestreamer.c server/livestreamer.h server/menuHTTP.c server/streamer.c server/streamer.h Added Files: remux/ts2pes.c remux/ts2pes.h
219 lines
5.0 KiB
C
219 lines
5.0 KiB
C
#include "remux/ts2ps.h"
|
|
#include "server/streamer.h"
|
|
#include <vdr/channels.h>
|
|
#include <vdr/device.h>
|
|
|
|
namespace Streamdev {
|
|
|
|
class cTS2PS {
|
|
friend void PutPES(uint8_t *Buffer, int Size, void *Data);
|
|
|
|
private:
|
|
ipack m_Ipack;
|
|
int m_Pid;
|
|
cRingBufferLinear *m_ResultBuffer;
|
|
|
|
public:
|
|
cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid = 0x00);
|
|
~cTS2PS();
|
|
|
|
void PutTSPacket(const uint8_t *Buffer);
|
|
|
|
int Pid(void) const { return m_Pid; }
|
|
};
|
|
|
|
void PutPES(uint8_t *Buffer, int Size, void *Data)
|
|
{
|
|
cTS2PS *This = (cTS2PS*)Data;
|
|
int n = This->m_ResultBuffer->Put(Buffer, Size);
|
|
if (n != Size)
|
|
esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Size - n, Size);
|
|
}
|
|
|
|
} // namespace Streamdev
|
|
using namespace Streamdev;
|
|
|
|
cTS2PS::cTS2PS(cRingBufferLinear *ResultBuffer, int Pid, uint8_t AudioCid)
|
|
{
|
|
m_ResultBuffer = ResultBuffer;
|
|
m_Pid = Pid;
|
|
|
|
init_ipack(&m_Ipack, IPACKS, PutPES, false);
|
|
m_Ipack.cid = AudioCid;
|
|
m_Ipack.data = (void*)this;
|
|
}
|
|
|
|
cTS2PS::~cTS2PS()
|
|
{
|
|
free_ipack(&m_Ipack);
|
|
}
|
|
|
|
void cTS2PS::PutTSPacket(const uint8_t *Buffer)
|
|
{
|
|
if (!Buffer)
|
|
return;
|
|
|
|
if (Buffer[1] & 0x80) { // ts error
|
|
// TODO
|
|
}
|
|
|
|
if (Buffer[1] & 0x40) { // payload start
|
|
if (m_Ipack.plength == MMAX_PLENGTH - 6 && m_Ipack.found > 6) {
|
|
m_Ipack.plength = m_Ipack.found - 6;
|
|
m_Ipack.found = 0;
|
|
send_ipack(&m_Ipack);
|
|
reset_ipack(&m_Ipack);
|
|
}
|
|
}
|
|
|
|
uint8_t off = 0;
|
|
|
|
if (Buffer[3] & 0x20) { // adaptation field?
|
|
off = Buffer[4] + 1;
|
|
if (off + 4 > TS_SIZE - 1)
|
|
return;
|
|
}
|
|
|
|
instant_repack((uint8_t*)(Buffer + 4 + off), TS_SIZE - 4 - off, &m_Ipack);
|
|
}
|
|
|
|
cTS2PSRemux::cTS2PSRemux(int VPid, const int *APids, const int *DPids, const int *SPids):
|
|
m_NumTracks(0),
|
|
m_ResultBuffer(new cStreamdevBuffer(WRITERBUFSIZE, IPACKS)),
|
|
m_ResultSkipped(0),
|
|
m_Skipped(0),
|
|
m_Synced(false),
|
|
m_IsRadio(VPid == 0 || VPid == 1 || VPid == 0x1FFF)
|
|
{
|
|
m_ResultBuffer->SetTimeouts(100, 100);
|
|
|
|
if (VPid)
|
|
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, VPid);
|
|
if (APids) {
|
|
int n = 0;
|
|
while (*APids && m_NumTracks < MAXTRACKS && n < MAXAPIDS)
|
|
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *APids++, 0xC0 + n++);
|
|
}
|
|
if (DPids) {
|
|
int n = 0;
|
|
while (*DPids && m_NumTracks < MAXTRACKS && n < MAXDPIDS)
|
|
m_Remux[m_NumTracks++] = new cTS2PS(m_ResultBuffer, *DPids++, 0x80 + n++);
|
|
}
|
|
}
|
|
|
|
cTS2PSRemux::~cTS2PSRemux() {
|
|
for (int i = 0; i < m_NumTracks; ++i)
|
|
delete m_Remux[i];
|
|
delete m_ResultBuffer;
|
|
}
|
|
|
|
int cTS2PSRemux::Put(const uchar *Data, int Count)
|
|
{
|
|
int used = 0;
|
|
|
|
// Make sure we are looking at a TS packet:
|
|
while (Count > TS_SIZE) {
|
|
if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
|
|
break;
|
|
Data++;
|
|
Count--;
|
|
used++;
|
|
}
|
|
if (used)
|
|
esyslog("ERROR: m_Skipped %d byte to sync on TS packet", used);
|
|
|
|
// Convert incoming TS data into multiplexed PS:
|
|
|
|
for (int i = 0; i < Count; i += TS_SIZE) {
|
|
if (Count - i < TS_SIZE)
|
|
break;
|
|
if (Data[i] != TS_SYNC_BYTE)
|
|
break;
|
|
if (m_ResultBuffer->Free() < 2 * IPACKS) {
|
|
m_ResultBuffer->WaitForPut();
|
|
break; // A cTS2PS might write one full packet and also a small rest
|
|
}
|
|
int pid = GetPid(Data + i + 1);
|
|
if (Data[i + 3] & 0x10) { // got payload
|
|
for (int t = 0; t < m_NumTracks; t++) {
|
|
if (m_Remux[t]->Pid() == pid) {
|
|
m_Remux[t]->PutTSPacket(Data + i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
used += TS_SIZE;
|
|
}
|
|
|
|
// Check if we're getting anywhere here:
|
|
if (!m_Synced && m_Skipped >= 0)
|
|
m_Skipped += used;
|
|
|
|
return used;
|
|
}
|
|
|
|
uchar *cTS2PSRemux::Get(int &Count)
|
|
{
|
|
// Remove any previously skipped data from the result buffer:
|
|
|
|
if (m_ResultSkipped > 0) {
|
|
m_ResultBuffer->Del(m_ResultSkipped);
|
|
m_ResultSkipped = 0;
|
|
}
|
|
|
|
// Special VPID case to enable recording radio channels:
|
|
if (m_IsRadio) {
|
|
// Force syncing of radio channels to avoid "no useful data" error
|
|
m_Synced = true;
|
|
return m_ResultBuffer->Get(Count);
|
|
}
|
|
|
|
// Check for frame borders:
|
|
Count = 0;
|
|
uchar *resultData = NULL;
|
|
int resultCount = 0;
|
|
uchar *data = m_ResultBuffer->Get(resultCount);
|
|
if (data) {
|
|
for (int i = 0; i < resultCount - 3; i++) {
|
|
if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
|
|
int l = 0;
|
|
uchar StreamType = data[i + 3];
|
|
if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
|
|
uchar pt = NO_PICTURE;
|
|
l = ScanVideoPacket(data, resultCount, i, pt);
|
|
if (l < 0)
|
|
return resultData;
|
|
if (pt != NO_PICTURE) {
|
|
if (pt < I_FRAME || B_FRAME < pt) {
|
|
esyslog("ERROR: unknown picture type '%d'", pt);
|
|
}
|
|
else if (!m_Synced) {
|
|
if (pt == I_FRAME) {
|
|
m_ResultSkipped = i; // will drop everything before this position
|
|
SetBrokenLink(data + i, l);
|
|
m_Synced = true;
|
|
}
|
|
}
|
|
else if (Count)
|
|
return resultData;
|
|
}
|
|
} else {
|
|
l = GetPacketLength(data, resultCount, i);
|
|
if (l < 0)
|
|
return resultData;
|
|
}
|
|
if (m_Synced) {
|
|
if (!Count)
|
|
resultData = data + i;
|
|
Count += l;
|
|
} else
|
|
m_ResultSkipped = i + l;
|
|
if (l > 0)
|
|
i += l - 1; // the loop increments, too
|
|
}
|
|
}
|
|
}
|
|
return resultData;
|
|
}
|
|
|