mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Redesigned the ring buffer; prepared for remultiplexing
This commit is contained in:
parent
191fb910bf
commit
5c68fc100c
7
HISTORY
7
HISTORY
@ -414,7 +414,7 @@ Video Disk Recorder Revision History
|
|||||||
given number of seconds. This is mainly useful in combination with the new
|
given number of seconds. This is mainly useful in combination with the new
|
||||||
'runvdr' script that restarts VDR in case is has exited.
|
'runvdr' script that restarts VDR in case is has exited.
|
||||||
|
|
||||||
2001-03-02: Version 0.72
|
2001-04-01: Version 0.72
|
||||||
|
|
||||||
- Fixed SVDRP commands LSTC and LSTT to make them return an error message if
|
- Fixed SVDRP commands LSTC and LSTT to make them return an error message if
|
||||||
no channels or timers are defined.
|
no channels or timers are defined.
|
||||||
@ -427,3 +427,8 @@ Video Disk Recorder Revision History
|
|||||||
- Fixed internationalization of some Main menu texts.
|
- Fixed internationalization of some Main menu texts.
|
||||||
- Updated 'channels.conf' after the recent changes of Premiere World (thanks
|
- Updated 'channels.conf' after the recent changes of Premiere World (thanks
|
||||||
to Axel Gruber).
|
to Axel Gruber).
|
||||||
|
- Redesigned the ring buffer to make it work with two separate threads for
|
||||||
|
input and output (also prepared for using a remultiplexer).
|
||||||
|
- Fixed setting system time from transponders.
|
||||||
|
- Fixed a segfault in the Schedule menu in case there is no EPG information.
|
||||||
|
- The 'runvdr' script now kills any leftover vdr threads before restarting it.
|
||||||
|
15
Makefile
15
Makefile
@ -4,13 +4,14 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Makefile 1.20 2001/02/24 15:52:58 kls Exp $
|
# $Id: Makefile 1.21 2001/03/18 16:47:00 kls Exp $
|
||||||
|
|
||||||
DVBDIR = ../DVB
|
DVBDIR = ../DVB
|
||||||
|
|
||||||
INCLUDES = -I$(DVBDIR)/driver
|
INCLUDES = -I$(DVBDIR)/driver
|
||||||
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
|
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
|
||||||
recording.o remote.o svdrp.o thread.o tools.o vdr.o videodir.o
|
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
|
||||||
|
videodir.o
|
||||||
|
|
||||||
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
||||||
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
||||||
@ -37,21 +38,23 @@ font: genfontfile fontfix.c fontosd.c
|
|||||||
# Implicit rules:
|
# Implicit rules:
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
g++ -g -O2 -Wall -m486 -c $(DEFINES) $(INCLUDES) $<
|
g++ -g -O2 -Wall -Woverloaded-virtual -m486 -c $(DEFINES) $(INCLUDES) $<
|
||||||
|
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
|
|
||||||
config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
|
config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
|
||||||
dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
|
dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h remux.h ringbuffer.h svdrp.h thread.h tools.h videodir.h
|
||||||
dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
|
dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
|
||||||
eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
|
eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
|
||||||
font.o : font.c font.h fontfix.c fontosd.c tools.h
|
font.o : font.c font.h fontfix.c fontosd.c tools.h
|
||||||
i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
|
i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
|
||||||
interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
|
interface.o : interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
|
||||||
menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
|
menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
|
||||||
osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
|
osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
|
||||||
recording.o: recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
|
recording.o : recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
|
||||||
remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
|
remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
|
||||||
|
remux.o : remux.c remux.h tools.h
|
||||||
|
ringbuffer.o: ringbuffer.c ringbuffer.h thread.h tools.h
|
||||||
svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
|
svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
|
||||||
thread.o : thread.c thread.h tools.h
|
thread.o : thread.c thread.h tools.h
|
||||||
tools.o : tools.c tools.h
|
tools.o : tools.c tools.h
|
||||||
|
4
config.h
4
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.h 1.42 2001/02/24 13:19:39 kls Exp $
|
* $Id: config.h 1.43 2001/03/18 16:47:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H
|
#ifndef __CONFIG_H
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
#define VDRVERSION "0.71"
|
#define VDRVERSION "0.72"
|
||||||
|
|
||||||
#define MaxBuffer 10000
|
#define MaxBuffer 10000
|
||||||
|
|
||||||
|
308
dvbapi.c
308
dvbapi.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: dvbapi.c 1.63 2001/03/14 18:39:53 kls Exp $
|
* $Id: dvbapi.c 1.64 2001/03/18 16:47:16 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
@ -22,6 +22,8 @@ extern "C" {
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "recording.h"
|
#include "recording.h"
|
||||||
|
#include "remux.h"
|
||||||
|
#include "ringbuffer.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
|
|
||||||
@ -29,29 +31,9 @@ extern "C" {
|
|||||||
#define VBIDEVICE "/dev/vbi"
|
#define VBIDEVICE "/dev/vbi"
|
||||||
|
|
||||||
// The size of the array used to buffer video data:
|
// The size of the array used to buffer video data:
|
||||||
|
// (must be larger than MINVIDEODATA - see remux.h)
|
||||||
#define VIDEOBUFSIZE (1024*1024)
|
#define VIDEOBUFSIZE (1024*1024)
|
||||||
|
|
||||||
// The minimum amount of video data necessary to identify frames
|
|
||||||
// (must be smaller than VIDEOBUFSIZE!):
|
|
||||||
#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
|
|
||||||
|
|
||||||
// The maximum time the buffer is allowed to write data to disk when recording:
|
|
||||||
#define MAXRECORDWRITETIME 50 // ms
|
|
||||||
|
|
||||||
// Picture types:
|
|
||||||
#define NO_PICTURE 0
|
|
||||||
#define I_FRAME 1
|
|
||||||
#define P_FRAME 2
|
|
||||||
#define B_FRAME 3
|
|
||||||
|
|
||||||
// Start codes:
|
|
||||||
#define SC_PICTURE 0x00 // "picture header"
|
|
||||||
#define SC_SEQU 0xB3 // "sequence header"
|
|
||||||
#define SC_PHEAD 0xBA // "pack header"
|
|
||||||
#define SC_SHEAD 0xBB // "system header"
|
|
||||||
#define SC_AUDIO 0xC0
|
|
||||||
#define SC_VIDEO 0xE0
|
|
||||||
|
|
||||||
#define FRAMESPERSEC 25
|
#define FRAMESPERSEC 25
|
||||||
|
|
||||||
// The maximum file size is limited by the range that can be covered
|
// The maximum file size is limited by the range that can be covered
|
||||||
@ -333,7 +315,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cRingBuffer -----------------------------------------------------------
|
// --- cRingBuffer_ -----------------------------------------------------------
|
||||||
|
|
||||||
/* cRingBuffer reads data from an input file, stores it in a buffer and writes
|
/* cRingBuffer reads data from an input file, stores it in a buffer and writes
|
||||||
it to an output file upon request. The Read() and Write() functions should
|
it to an output file upon request. The Read() and Write() functions should
|
||||||
@ -344,7 +326,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)
|
|||||||
will be made.
|
will be made.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class cRingBuffer {
|
class cRingBuffer_ {
|
||||||
private:
|
private:
|
||||||
uchar *buffer;
|
uchar *buffer;
|
||||||
int size, head, tail, freeLimit, availLimit;
|
int size, head, tail, freeLimit, availLimit;
|
||||||
@ -367,8 +349,8 @@ protected:
|
|||||||
int FindStartCode(uchar Code, int Offset = 0);
|
int FindStartCode(uchar Code, int Offset = 0);
|
||||||
int GetPacketLength(int Offset = 0);
|
int GetPacketLength(int Offset = 0);
|
||||||
public:
|
public:
|
||||||
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
||||||
virtual ~cRingBuffer();
|
virtual ~cRingBuffer_();
|
||||||
virtual int Read(int Max = -1);
|
virtual int Read(int Max = -1);
|
||||||
virtual int Write(int Max = -1);
|
virtual int Write(int Max = -1);
|
||||||
bool EndOfFile(void) { return eof; }
|
bool EndOfFile(void) { return eof; }
|
||||||
@ -377,7 +359,7 @@ public:
|
|||||||
void Skip(int n);
|
void Skip(int n);
|
||||||
};
|
};
|
||||||
|
|
||||||
cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
|
cRingBuffer_::cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)
|
||||||
{
|
{
|
||||||
inFile = InFile;
|
inFile = InFile;
|
||||||
outFile = OutFile;
|
outFile = OutFile;
|
||||||
@ -393,13 +375,13 @@ cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int
|
|||||||
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
|
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
cRingBuffer::~cRingBuffer()
|
cRingBuffer_::~cRingBuffer_()
|
||||||
{
|
{
|
||||||
dsyslog(LOG_INFO, "buffer stats: %d free, %d overflows, limit exceeded %d times", minFree, countOverflow, countLimit);
|
dsyslog(LOG_INFO, "buffer stats: %d free, %d overflows, limit exceeded %d times", minFree, countOverflow, countLimit);
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBuffer::Byte(int Offset)
|
int cRingBuffer_::Byte(int Offset)
|
||||||
{
|
{
|
||||||
if (buffer && Offset < Available()) {
|
if (buffer && Offset < Available()) {
|
||||||
Offset += head;
|
Offset += head;
|
||||||
@ -410,7 +392,7 @@ int cRingBuffer::Byte(int Offset)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cRingBuffer::Set(int Offset, int Length, int Value)
|
bool cRingBuffer_::Set(int Offset, int Length, int Value)
|
||||||
{
|
{
|
||||||
if (buffer && Offset + Length <= Available() ) {
|
if (buffer && Offset + Length <= Available() ) {
|
||||||
Offset += head;
|
Offset += head;
|
||||||
@ -425,7 +407,7 @@ bool cRingBuffer::Set(int Offset, int Length, int Value)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRingBuffer::Skip(int n)
|
void cRingBuffer_::Skip(int n)
|
||||||
{
|
{
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
if (head < tail) {
|
if (head < tail) {
|
||||||
@ -443,7 +425,7 @@ void cRingBuffer::Skip(int n)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBuffer::Read(int Max)
|
int cRingBuffer_::Read(int Max)
|
||||||
{
|
{
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
eof = false;
|
eof = false;
|
||||||
@ -501,7 +483,7 @@ int cRingBuffer::Read(int Max)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBuffer::Write(int Max)
|
int cRingBuffer_::Write(int Max)
|
||||||
{
|
{
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
int avail = Available();
|
int avail = Available();
|
||||||
@ -540,7 +522,7 @@ int cRingBuffer::Write(int Max)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBuffer::FindStartCode(uchar Code, int Offset)
|
int cRingBuffer_::FindStartCode(uchar Code, int Offset)
|
||||||
{
|
{
|
||||||
// Searches for a start code (beginning at Offset) and returns the number
|
// Searches for a start code (beginning at Offset) and returns the number
|
||||||
// of bytes from Offset to the start code.
|
// of bytes from Offset to the start code.
|
||||||
@ -557,7 +539,7 @@ int cRingBuffer::FindStartCode(uchar Code, int Offset)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRingBuffer::GetPacketLength(int Offset)
|
int cRingBuffer_::GetPacketLength(int Offset)
|
||||||
{
|
{
|
||||||
// Returns the entire length of the packet starting at offset.
|
// Returns the entire length of the packet starting at offset.
|
||||||
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
|
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
|
||||||
@ -671,31 +653,29 @@ int cFileName::NextFile(void)
|
|||||||
|
|
||||||
// --- cRecordBuffer ---------------------------------------------------------
|
// --- cRecordBuffer ---------------------------------------------------------
|
||||||
|
|
||||||
class cRecordBuffer : public cRingBuffer, public cThread {
|
class cRecordBuffer : public cRingBuffer {
|
||||||
private:
|
private:
|
||||||
cFileName fileName;
|
cFileName fileName;
|
||||||
cIndexFile *index;
|
cIndexFile *index;
|
||||||
|
cRemux remux;
|
||||||
uchar pictureType;
|
uchar pictureType;
|
||||||
int fileSize;
|
int fileSize;
|
||||||
int videoDev;
|
int videoDev;
|
||||||
int recordFile;
|
int recordFile;
|
||||||
bool ok, synced, stop;
|
bool recording;
|
||||||
time_t lastDiskSpaceCheck;
|
time_t lastDiskSpaceCheck;
|
||||||
bool RunningLowOnDiskSpace(void);
|
bool RunningLowOnDiskSpace(void);
|
||||||
int ScanVideoPacket(int *PictureType, int Offset);
|
|
||||||
int Synchronize(void);
|
|
||||||
bool NextFile(void);
|
bool NextFile(void);
|
||||||
virtual int Write(int Max = -1);
|
|
||||||
bool WriteWithTimeout(void);
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Input(void);
|
||||||
|
virtual void Output(void);
|
||||||
public:
|
public:
|
||||||
cRecordBuffer(int *InFile, const char *FileName);
|
cRecordBuffer(int *InFile, const char *FileName);
|
||||||
virtual ~cRecordBuffer();
|
virtual ~cRecordBuffer();
|
||||||
};
|
};
|
||||||
|
|
||||||
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
||||||
:cRingBuffer(InFile, &recordFile, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
|
:cRingBuffer(VIDEOBUFSIZE)
|
||||||
,fileName(FileName, true)
|
,fileName(FileName, true)
|
||||||
{
|
{
|
||||||
index = NULL;
|
index = NULL;
|
||||||
@ -703,7 +683,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
videoDev = *InFile;
|
videoDev = *InFile;
|
||||||
recordFile = fileName.Open();
|
recordFile = fileName.Open();
|
||||||
ok = synced = stop = false;
|
recording = false;
|
||||||
lastDiskSpaceCheck = time(NULL);
|
lastDiskSpaceCheck = time(NULL);
|
||||||
if (!fileName.Name())
|
if (!fileName.Name())
|
||||||
return;
|
return;
|
||||||
@ -712,44 +692,15 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
if (!index)
|
if (!index)
|
||||||
esyslog(LOG_ERR, "ERROR: can't allocate index");
|
esyslog(LOG_ERR, "ERROR: can't allocate index");
|
||||||
// let's continue without index, so we'll at least have the recording
|
// let's continue without index, so we'll at least have the recording
|
||||||
ok = true;
|
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
cRecordBuffer::~cRecordBuffer()
|
cRecordBuffer::~cRecordBuffer()
|
||||||
{
|
{
|
||||||
stop = true;
|
Stop();
|
||||||
Cancel(3);
|
|
||||||
delete index;
|
delete index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRecordBuffer::Action(void)
|
|
||||||
{
|
|
||||||
dsyslog(LOG_INFO, "recording thread started (pid=%d)", getpid());
|
|
||||||
|
|
||||||
time_t t = time(NULL);
|
|
||||||
for (;;) {
|
|
||||||
usleep(1); // this keeps the CPU load low
|
|
||||||
|
|
||||||
LOCK_THREAD;
|
|
||||||
|
|
||||||
int r = Read();
|
|
||||||
if (r >= 0) {
|
|
||||||
if (r > 0)
|
|
||||||
t = time(NULL);
|
|
||||||
if (!WriteWithTimeout())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (r < 0 || (r == 0 && time(NULL) - t > 5)) {
|
|
||||||
esyslog(LOG_ERR, "ERROR: video data stream broken");
|
|
||||||
t = time(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetPlayMode(videoDev, VID_PLAY_RESET);
|
|
||||||
|
|
||||||
dsyslog(LOG_INFO, "end recording thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
||||||
{
|
{
|
||||||
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||||
@ -763,88 +714,6 @@ bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRecordBuffer::ScanVideoPacket(int *PictureType, int Offset)
|
|
||||||
{
|
|
||||||
// Scans the video packet starting at Offset and returns its length.
|
|
||||||
// If the return value is -1 the packet was not completely in the buffer.
|
|
||||||
|
|
||||||
int Length = GetPacketLength(Offset);
|
|
||||||
if (Length <= Available()) {
|
|
||||||
int i = Offset + 8; // the minimum length of the video packet header
|
|
||||||
i += Byte(i) + 1; // possible additional header bytes
|
|
||||||
for (; i < Offset + Length; i++) {
|
|
||||||
if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
|
|
||||||
switch (Byte(i + 3)) {
|
|
||||||
case SC_PICTURE: *PictureType = GetPictureType(i);
|
|
||||||
return Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*PictureType = NO_PICTURE;
|
|
||||||
return Length;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cRecordBuffer::Synchronize(void)
|
|
||||||
{
|
|
||||||
// Positions to the start of a data block (skipping everything up to
|
|
||||||
// an I-frame if not synced) and returns the block length.
|
|
||||||
|
|
||||||
pictureType = NO_PICTURE;
|
|
||||||
|
|
||||||
//XXX remove this once the buffer is handled with two separate threads:
|
|
||||||
if (!synced && Free() < 100000) {
|
|
||||||
dsyslog(LOG_INFO, "unable to synchronize, dropped %d bytes", Available());
|
|
||||||
Clear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (int i = 0; Available() > MINVIDEODATA && i < MINVIDEODATA; i++) {
|
|
||||||
if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
|
|
||||||
switch (Byte(i + 3)) {
|
|
||||||
case SC_VIDEO: {
|
|
||||||
int pt = NO_PICTURE;
|
|
||||||
int l = ScanVideoPacket(&pt, i);
|
|
||||||
if (l < 0)
|
|
||||||
return 0; // no useful data found, wait for more
|
|
||||||
if (pt != NO_PICTURE) {
|
|
||||||
if (pt < I_FRAME || B_FRAME < pt) {
|
|
||||||
esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
|
|
||||||
}
|
|
||||||
else if (pictureType == NO_PICTURE) {
|
|
||||||
if (!synced) {
|
|
||||||
if (pt == I_FRAME) {
|
|
||||||
Skip(i);
|
|
||||||
synced = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Skip(i + l);
|
|
||||||
i = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (synced)
|
|
||||||
pictureType = pt;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
else if (!synced) {
|
|
||||||
Skip(i + l);
|
|
||||||
i = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i += l - 1; // -1 to compensate for i++ in the loop!
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SC_AUDIO: i += GetPacketLength(i) - 1; // -1 to compensate for i++ in the loop!
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0; // no useful data found, wait for more
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cRecordBuffer::NextFile(void)
|
bool cRecordBuffer::NextFile(void)
|
||||||
{
|
{
|
||||||
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||||
@ -856,56 +725,93 @@ bool cRecordBuffer::NextFile(void)
|
|||||||
return recordFile >= 0;
|
return recordFile >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRecordBuffer::Write(int Max)
|
void cRecordBuffer::Input(void)
|
||||||
{
|
{
|
||||||
// This function ignores the incoming 'Max'!
|
dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
|
||||||
// It tries to write out exactly *one* frame block.
|
|
||||||
if (!ok)
|
uchar b[MINVIDEODATA];
|
||||||
return -1;
|
time_t t = time(NULL);
|
||||||
int n = Synchronize();
|
recording = true;
|
||||||
if (n) {
|
for (;;) {
|
||||||
if (stop && pictureType == I_FRAME) {
|
int r = read(videoDev, b, sizeof(b));
|
||||||
ok = false;
|
if (r > 0) {
|
||||||
return -1; // finish the recording before the next 'I' frame
|
uchar *p = b;
|
||||||
|
while (r > 0) {
|
||||||
|
int w = Put(p, r);
|
||||||
|
p += w;
|
||||||
|
r -= w;
|
||||||
}
|
}
|
||||||
|
t = time(NULL);
|
||||||
|
}
|
||||||
|
else if (r < 0) {
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
LOG_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (time(NULL) - t > 5) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: video data stream broken");
|
||||||
|
t = time(NULL);
|
||||||
|
}
|
||||||
|
cFile::FileReady(videoDev, 100);
|
||||||
|
if (!recording)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SetPlayMode(videoDev, VID_PLAY_RESET);
|
||||||
|
|
||||||
|
dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRecordBuffer::Output(void)
|
||||||
|
{
|
||||||
|
dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
|
||||||
|
|
||||||
|
uchar b[MINVIDEODATA * 2];
|
||||||
|
int r = 0;
|
||||||
|
for (;;) {
|
||||||
|
usleep(1); // this keeps the CPU load low
|
||||||
|
r += Get(b + r, sizeof(b) - r);
|
||||||
|
if (r > 0) {
|
||||||
|
//XXX buffer full???
|
||||||
|
int Count = r, Result;
|
||||||
|
const uchar *p = remux.Process(b, Count, Result, pictureType);
|
||||||
|
if (p) {
|
||||||
|
if (!Busy() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
|
||||||
|
break;
|
||||||
if (NextFile()) {
|
if (NextFile()) {
|
||||||
if (index && pictureType != NO_PICTURE)
|
if (index && pictureType != NO_PICTURE)
|
||||||
index->Write(pictureType, fileName.Number(), fileSize);
|
index->Write(pictureType, fileName.Number(), fileSize);
|
||||||
int written = 0;
|
while (Result > 0) {
|
||||||
for (;;) {
|
int w = write(recordFile, p, Result);
|
||||||
int w = cRingBuffer::Write(n);
|
if (w < 0) {
|
||||||
if (w >= 0) {
|
LOG_ERROR_STR(fileName.Name());
|
||||||
|
recording = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p += w;
|
||||||
|
Result -= w;
|
||||||
fileSize += w;
|
fileSize += w;
|
||||||
written += w;
|
}
|
||||||
n -= w;
|
|
||||||
if (n == 0)
|
|
||||||
return written;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cRecordBuffer::WriteWithTimeout(void)
|
|
||||||
{
|
|
||||||
int t0 = time_ms();
|
|
||||||
do {
|
|
||||||
int w = Write();
|
|
||||||
if (w < 0)
|
|
||||||
return false;
|
|
||||||
if (w == 0)
|
|
||||||
break;
|
break;
|
||||||
} while (time_ms() - t0 < MAXRECORDWRITETIME);
|
}
|
||||||
return true;
|
if (Count > 0) {
|
||||||
|
r -= Count;
|
||||||
|
memmove(b, b + Count, r);
|
||||||
|
}
|
||||||
|
if (!recording)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recording = false;
|
||||||
|
|
||||||
|
dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cReplayBuffer ---------------------------------------------------------
|
// --- cReplayBuffer ---------------------------------------------------------
|
||||||
|
|
||||||
class cReplayBuffer : public cRingBuffer, public cThread {
|
class cReplayBuffer : public cRingBuffer_, public cThread {
|
||||||
private:
|
private:
|
||||||
enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
|
enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
|
||||||
enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
|
enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
|
||||||
@ -944,7 +850,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
||||||
:cRingBuffer(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
:cRingBuffer_(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
||||||
,fileName(FileName, false)
|
,fileName(FileName, false)
|
||||||
{
|
{
|
||||||
index = NULL;
|
index = NULL;
|
||||||
@ -1271,7 +1177,7 @@ int cReplayBuffer::Read(int Max = -1)
|
|||||||
int readin = 0;
|
int readin = 0;
|
||||||
do {
|
do {
|
||||||
// If Max is > 0 here we need to make sure we read in the entire block!
|
// If Max is > 0 here we need to make sure we read in the entire block!
|
||||||
int r = cRingBuffer::Read(Max);
|
int r = cRingBuffer_::Read(Max);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
readin += r;
|
readin += r;
|
||||||
else
|
else
|
||||||
@ -1300,7 +1206,7 @@ int cReplayBuffer::Write(int Max)
|
|||||||
if (Max) {
|
if (Max) {
|
||||||
int w;
|
int w;
|
||||||
do {
|
do {
|
||||||
w = cRingBuffer::Write(Max);
|
w = cRingBuffer_::Write(Max);
|
||||||
if (w >= 0) {
|
if (w >= 0) {
|
||||||
fileOffset += w;
|
fileOffset += w;
|
||||||
Written += w;
|
Written += w;
|
||||||
@ -1348,7 +1254,7 @@ void cTransferBuffer::Action(void)
|
|||||||
{
|
{
|
||||||
dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
|
dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid());
|
||||||
|
|
||||||
cRingBuffer Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
|
cRingBuffer_ Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);
|
||||||
active = true;
|
active = true;
|
||||||
while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start
|
while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start
|
||||||
Buffer.Read(); // initializes fromDevice for reading
|
Buffer.Read(); // initializes fromDevice for reading
|
||||||
@ -1364,7 +1270,7 @@ void cTransferBuffer::Action(void)
|
|||||||
|
|
||||||
// --- cCuttingBuffer --------------------------------------------------------
|
// --- cCuttingBuffer --------------------------------------------------------
|
||||||
|
|
||||||
class cCuttingBuffer : public cRingBuffer, public cThread {
|
class cCuttingBuffer : public cRingBuffer_, public cThread {
|
||||||
private:
|
private:
|
||||||
bool active;
|
bool active;
|
||||||
int fromFile, toFile;
|
int fromFile, toFile;
|
||||||
@ -1379,7 +1285,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
|
cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
|
||||||
:cRingBuffer(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
:cRingBuffer_(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
||||||
{
|
{
|
||||||
active = false;
|
active = false;
|
||||||
fromFile = toFile = -1;
|
fromFile = toFile = -1;
|
||||||
@ -1438,7 +1344,7 @@ void cCuttingBuffer::Action(void)
|
|||||||
CurrentFileNumber = FileNumber;
|
CurrentFileNumber = FileNumber;
|
||||||
}
|
}
|
||||||
if (fromFile >= 0)
|
if (fromFile >= 0)
|
||||||
Length = cRingBuffer::Read(Length);
|
Length = cRingBuffer_::Read(Length);
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1456,7 +1362,7 @@ void cCuttingBuffer::Action(void)
|
|||||||
}
|
}
|
||||||
LastIFrame = 0;
|
LastIFrame = 0;
|
||||||
}
|
}
|
||||||
cRingBuffer::Write(Length);
|
cRingBuffer_::Write(Length);
|
||||||
toIndex->Write(PictureType, toFileName->Number(), FileSize);
|
toIndex->Write(PictureType, toFileName->Number(), FileSize);
|
||||||
FileSize += Length;
|
FileSize += Length;
|
||||||
if (!LastIFrame)
|
if (!LastIFrame)
|
||||||
|
173
remux.c
Normal file
173
remux.c
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* remux.c: A streaming MPEG2 remultiplexer
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The calling interface of the 'cRemux::Process()' function is defined
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
'Data' points to a chunk of data that consists of 'Count' bytes.
|
||||||
|
The 'Process' function shall try to remultiplex as much of the
|
||||||
|
data as possible and return a pointer to the resulting buffer.
|
||||||
|
That buffer typically is different from the incoming 'Data',
|
||||||
|
but in the simplest case (when 'Process' does nothing) might
|
||||||
|
as well point to the original 'Data'. When returning, 'Count'
|
||||||
|
shall be set to the number of bytes that have been processed
|
||||||
|
(i.e. have been taken from 'Data'), while 'Result' indicates
|
||||||
|
how many bytes the returned buffer contains. 'PictureType' shall
|
||||||
|
be set to NO_PICTURE if the returned data does not start a new
|
||||||
|
picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture
|
||||||
|
starting point has been found. This also means that the returned
|
||||||
|
data buffer may contain at most one entire video frame, because
|
||||||
|
the next frame must be returned with its own value for 'PictureType'.
|
||||||
|
|
||||||
|
'Process' shall do it's best to keep the latency time as short
|
||||||
|
as possible in order to allow a quick start of VDR's "Transfer
|
||||||
|
mode" (displaying the signal of one DVB card on another card).
|
||||||
|
In order to do that, this function may decide to first pass
|
||||||
|
through the incoming data (almost) unprocessed, and make
|
||||||
|
actual processing kick in after a few seconds (if that is at
|
||||||
|
all possible for the algorithm). This may result in a non-
|
||||||
|
optimal stream at the beginning, which won't matter for normal
|
||||||
|
recordings but may make switching through encrypted channels
|
||||||
|
in "Transfer mode" faster.
|
||||||
|
|
||||||
|
In the resulting data stream, a new packet shall always be started
|
||||||
|
when a frame border is encountered. VDR needs this in order to
|
||||||
|
be able to detect and store the frame indexes, and to easily
|
||||||
|
display single frames in fast forward/back mode. The very first
|
||||||
|
data block returned shall be the starting point of an I_FRAME.
|
||||||
|
Everything before that shall be silently dropped.
|
||||||
|
|
||||||
|
If the incoming data is not enough to do remultiplexing, a value
|
||||||
|
of NULL shall be returned ('Result' has no meaning then). This
|
||||||
|
will tell the caller to wait for more data to be presented in
|
||||||
|
the next call. If NULL is returned and 'Count' is not 0, the
|
||||||
|
caller shall remove 'Count' bytes from the beginning of 'Data'
|
||||||
|
before the next call. This is the way 'Process' indicates that
|
||||||
|
it must skip that data.
|
||||||
|
|
||||||
|
Any data that is not used during this call will appear at the
|
||||||
|
beginning of the incoming 'Data' buffer at the next call, plus
|
||||||
|
any new data that has become available.
|
||||||
|
|
||||||
|
It is guaranteed that the caller will completely process any
|
||||||
|
returned data before the next call to 'Process'. That way, 'Process'
|
||||||
|
can dynamically allocate its return buffer and be sure the caller
|
||||||
|
doesn't keep any pointers into that buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "remux.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
#if defined(REMUX_NONE)
|
||||||
|
|
||||||
|
cRemux::cRemux(void)
|
||||||
|
{
|
||||||
|
synced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cRemux::~cRemux()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
|
||||||
|
{
|
||||||
|
// Returns the entire length of the packet starting at offset, or -1 in case of error.
|
||||||
|
return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
|
||||||
|
{
|
||||||
|
// Scans the video packet starting at Offset and returns its length.
|
||||||
|
// If the return value is -1 the packet was not completely in the buffer.
|
||||||
|
|
||||||
|
int Length = GetPacketLength(Data, Count, Offset);
|
||||||
|
if (Length > 0 && Offset + Length <= Count) {
|
||||||
|
int i = Offset + 8; // the minimum length of the video packet header
|
||||||
|
i += Data[i] + 1; // possible additional header bytes
|
||||||
|
for (; i < Offset + Length; i++) {
|
||||||
|
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
||||||
|
switch (Data[i + 3]) {
|
||||||
|
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
|
||||||
|
return Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PictureType = NO_PICTURE;
|
||||||
|
return Length;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType)
|
||||||
|
{
|
||||||
|
int Skip = 0;
|
||||||
|
|
||||||
|
PictureType = NO_PICTURE;
|
||||||
|
|
||||||
|
if (Count >= MINVIDEODATA) {
|
||||||
|
for (int i = 0; i < Count; i++) {
|
||||||
|
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
|
||||||
|
switch (Data[i + 3]) {
|
||||||
|
case SC_VIDEO:
|
||||||
|
{
|
||||||
|
uchar pt = NO_PICTURE;
|
||||||
|
int l = ScanVideoPacket(Data, Count, i, pt);
|
||||||
|
if (l < 0) {
|
||||||
|
if (Skip < Count)
|
||||||
|
Count = Skip;
|
||||||
|
return NULL; // no useful data found, wait for more
|
||||||
|
}
|
||||||
|
if (pt != NO_PICTURE) {
|
||||||
|
if (pt < I_FRAME || B_FRAME < pt) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
|
||||||
|
}
|
||||||
|
else if (PictureType == NO_PICTURE) {
|
||||||
|
if (!synced) {
|
||||||
|
if (pt == I_FRAME) {
|
||||||
|
Skip = i;
|
||||||
|
synced = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i += l;
|
||||||
|
Skip = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (synced)
|
||||||
|
PictureType = pt;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Count = i;
|
||||||
|
Result = i - Skip;
|
||||||
|
return Data + Skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!synced) {
|
||||||
|
i += l;
|
||||||
|
Skip = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += l - 1; // -1 to compensate for i++ in the loop!
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SC_AUDIO:
|
||||||
|
i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Skip < Count)
|
||||||
|
Count = Skip;
|
||||||
|
return NULL; // no useful data found, wait for more
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(REMUX_TEST)
|
||||||
|
#endif
|
||||||
|
|
51
remux.h
Normal file
51
remux.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* remux.h: A streaming MPEG2 remultiplexer
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: remux.h 1.1 2001/03/31 08:42:27 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __REMUX_H
|
||||||
|
#define __REMUX_H
|
||||||
|
|
||||||
|
// There are various experiments with different types of remultiplexers
|
||||||
|
// going on at the moment. Select the remultiplexer here:
|
||||||
|
#define REMUX_NONE 1
|
||||||
|
//#define REMUX_TEST 1
|
||||||
|
|
||||||
|
// Picture types:
|
||||||
|
#define NO_PICTURE 0
|
||||||
|
#define I_FRAME 1
|
||||||
|
#define P_FRAME 2
|
||||||
|
#define B_FRAME 3
|
||||||
|
|
||||||
|
// Start codes:
|
||||||
|
#define SC_PICTURE 0x00 // "picture header"
|
||||||
|
#define SC_SEQU 0xB3 // "sequence header"
|
||||||
|
#define SC_PHEAD 0xBA // "pack header"
|
||||||
|
#define SC_SHEAD 0xBB // "system header"
|
||||||
|
#define SC_AUDIO 0xC0
|
||||||
|
#define SC_VIDEO 0xE0
|
||||||
|
|
||||||
|
// The minimum amount of video data necessary to identify frames:
|
||||||
|
#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety)
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
class cRemux {
|
||||||
|
private:
|
||||||
|
#if defined(REMUX_NONE)
|
||||||
|
bool synced;
|
||||||
|
int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||||
|
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||||
|
#elif defined(REMUX_TEST)
|
||||||
|
#endif
|
||||||
|
public:
|
||||||
|
cRemux(void);
|
||||||
|
~cRemux();
|
||||||
|
const uchar *Process(const uchar *Data, int &Count, int &Result, uchar &PictureType);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __REMUX_H
|
170
ringbuffer.c
Normal file
170
ringbuffer.c
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* ringbuffer.c: A threaded ring buffer
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* Parts of this file were inspired by the 'ringbuffy.c' from the
|
||||||
|
* LinuxDVB driver (see linuxtv.org).
|
||||||
|
*
|
||||||
|
* $Id: ringbuffer.c 1.1 2001/03/18 16:47:00 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ringbuffer.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
// --- cRingBufferInputThread -------------------------------------------------
|
||||||
|
|
||||||
|
class cRingBufferInputThread : public cThread {
|
||||||
|
private:
|
||||||
|
cRingBuffer *ringBuffer;
|
||||||
|
protected:
|
||||||
|
virtual void Action(void) { ringBuffer->Input(); }
|
||||||
|
public:
|
||||||
|
cRingBufferInputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- cRingBufferOutputThread ------------------------------------------------
|
||||||
|
|
||||||
|
class cRingBufferOutputThread : public cThread {
|
||||||
|
private:
|
||||||
|
cRingBuffer *ringBuffer;
|
||||||
|
protected:
|
||||||
|
virtual void Action(void) { ringBuffer->Output(); }
|
||||||
|
public:
|
||||||
|
cRingBufferOutputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- cRingBuffer ------------------------------------------------------------
|
||||||
|
|
||||||
|
cRingBuffer::cRingBuffer(int Size)
|
||||||
|
{
|
||||||
|
size = Size;
|
||||||
|
buffer = NULL;
|
||||||
|
inputThread = NULL;
|
||||||
|
outputThread = NULL;
|
||||||
|
maxFill = 0;
|
||||||
|
busy = false;
|
||||||
|
if (size > 1) { // 'size - 1' must not be 0!
|
||||||
|
buffer = new uchar[size];
|
||||||
|
if (!buffer)
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
cRingBuffer::~cRingBuffer()
|
||||||
|
{
|
||||||
|
delete inputThread;
|
||||||
|
delete outputThread;
|
||||||
|
delete buffer;
|
||||||
|
dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRingBuffer::Clear(void)
|
||||||
|
{
|
||||||
|
mutex.Lock();
|
||||||
|
head = tail = 0;
|
||||||
|
mutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int cRingBuffer::Put(const uchar *Data, int Count)
|
||||||
|
{
|
||||||
|
if (Count > 0) {
|
||||||
|
mutex.Lock();
|
||||||
|
int rest = size - head;
|
||||||
|
int diff = tail - head;
|
||||||
|
mutex.Unlock();
|
||||||
|
int free = (diff > 0) ? diff - 1 : size + diff - 1;
|
||||||
|
// Statistics:
|
||||||
|
int fill = size - free - 1 + Count;
|
||||||
|
if (fill >= size)
|
||||||
|
fill = size - 1;
|
||||||
|
if (fill > maxFill) {
|
||||||
|
maxFill = fill;
|
||||||
|
int percent = maxFill * 100 / (size - 1);
|
||||||
|
if (percent > 75)
|
||||||
|
dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
if (free <= 0)
|
||||||
|
return 0;
|
||||||
|
if (free < Count)
|
||||||
|
Count = free;
|
||||||
|
if (Count > maxFill)
|
||||||
|
maxFill = Count;
|
||||||
|
if (Count >= rest) {
|
||||||
|
memcpy(buffer + head, Data, rest);
|
||||||
|
if (Count - rest)
|
||||||
|
memcpy(buffer, Data + rest, Count - rest);
|
||||||
|
head = Count - rest;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(buffer + head, Data, Count);
|
||||||
|
head += Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cRingBuffer::Get(uchar *Data, int Count)
|
||||||
|
{
|
||||||
|
if (Count > 0) {
|
||||||
|
mutex.Lock();
|
||||||
|
int rest = size - tail;
|
||||||
|
int diff = head - tail;
|
||||||
|
mutex.Unlock();
|
||||||
|
int cont = (diff >= 0) ? diff : size + diff;
|
||||||
|
if (rest <= 0)
|
||||||
|
return 0;
|
||||||
|
if (cont < Count)
|
||||||
|
Count = cont;
|
||||||
|
if (Count >= rest) {
|
||||||
|
memcpy(Data, buffer + tail, rest);
|
||||||
|
if (Count - rest)
|
||||||
|
memcpy(Data + rest, buffer, Count - rest);
|
||||||
|
tail = Count - rest;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(Data, buffer + tail, Count);
|
||||||
|
tail += Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRingBuffer::Start(void)
|
||||||
|
{
|
||||||
|
if (!busy) {
|
||||||
|
busy = true;
|
||||||
|
outputThread = new cRingBufferOutputThread(this);
|
||||||
|
if (!outputThread->Start())
|
||||||
|
DELETENULL(outputThread);
|
||||||
|
inputThread = new cRingBufferInputThread(this);
|
||||||
|
if (!inputThread->Start()) {
|
||||||
|
DELETENULL(inputThread);
|
||||||
|
DELETENULL(outputThread);
|
||||||
|
}
|
||||||
|
busy = outputThread && inputThread;
|
||||||
|
}
|
||||||
|
return busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRingBuffer::Active(void)
|
||||||
|
{
|
||||||
|
return outputThread && outputThread->Active() && inputThread && inputThread->Active();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cRingBuffer::Stop(void)
|
||||||
|
{
|
||||||
|
busy = false;
|
||||||
|
for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) {
|
||||||
|
if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active())))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DELETENULL(inputThread);
|
||||||
|
DELETENULL(outputThread);
|
||||||
|
}
|
||||||
|
|
55
ringbuffer.h
Normal file
55
ringbuffer.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* ringbuffer.h: A threaded ring buffer
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: ringbuffer.h 1.1 2001/03/18 16:47:00 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RINGBUFFER_H
|
||||||
|
#define __RINGBUFFER_H
|
||||||
|
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
|
typedef unsigned char uchar;
|
||||||
|
|
||||||
|
class cRingBufferInputThread;
|
||||||
|
class cRingBufferOutputThread;
|
||||||
|
|
||||||
|
class cRingBuffer {
|
||||||
|
friend class cRingBufferInputThread;
|
||||||
|
friend class cRingBufferOutputThread;
|
||||||
|
private:
|
||||||
|
cRingBufferInputThread *inputThread;
|
||||||
|
cRingBufferOutputThread *outputThread;
|
||||||
|
cMutex mutex;
|
||||||
|
int size, head, tail;
|
||||||
|
uchar *buffer;
|
||||||
|
int maxFill;
|
||||||
|
bool busy;
|
||||||
|
protected:
|
||||||
|
bool Busy(void) { return busy; }
|
||||||
|
void Clear(void);
|
||||||
|
// Immediately clears the ring buffer.
|
||||||
|
int Put(const uchar *Data, int Count);
|
||||||
|
// Puts at most Count bytes of Data into the ring buffer.
|
||||||
|
// Returns the number of bytes actually stored.
|
||||||
|
int Get(uchar *Data, int Count);
|
||||||
|
// Gets at most Count bytes of Data from the ring buffer.
|
||||||
|
// Returns the number of bytes actually retrieved.
|
||||||
|
virtual void Input(void) = 0;
|
||||||
|
// Runs as a separate thread and shall continuously read data from
|
||||||
|
// a source and call Put() to store the data in the ring buffer.
|
||||||
|
virtual void Output(void) = 0;
|
||||||
|
// Runs as a separate thread and shall continuously call Get() to
|
||||||
|
// retrieve data from the ring buffer and write it to a destination.
|
||||||
|
public:
|
||||||
|
cRingBuffer(int Size);
|
||||||
|
virtual ~cRingBuffer();
|
||||||
|
bool Start(void);
|
||||||
|
bool Active(void);
|
||||||
|
void Stop(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __RINGBUFFER_H
|
Loading…
Reference in New Issue
Block a user