mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Adapted frame detection to driver 0.8+
This commit is contained in:
parent
05c61fe624
commit
7c79f61dd5
3
HISTORY
3
HISTORY
@ -320,7 +320,8 @@ Video Disk Recorder Revision History
|
||||
the DVB/driver directory.
|
||||
Old recordings (in AV_PES mode) can still be replayed (as long as the driver
|
||||
still supports replaying AV_PES files). The only limitation with this is that
|
||||
in fast forward/back mode the picture may be slightly distorted.
|
||||
in fast forward/back mode the picture may be slightly distorted and there may
|
||||
be sound fragments.
|
||||
- The EPG data is now dumped into the file /video/epg.data every ten minutes.
|
||||
Use the Perl script 'epg2html.pl' to convert the raw EPG data into a simple
|
||||
HTML programme listing.
|
||||
|
170
dvbapi.c
170
dvbapi.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbapi.c 1.44 2000/12/25 15:18:02 kls Exp $
|
||||
* $Id: dvbapi.c 1.45 2001/01/07 17:00:50 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbapi.h"
|
||||
@ -45,8 +45,10 @@ extern "C" {
|
||||
#define B_FRAME 3
|
||||
|
||||
// Start codes:
|
||||
#define SC_PICTURE 0x00
|
||||
#define SC_BLOCK 0xBA
|
||||
#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
|
||||
|
||||
@ -409,12 +411,12 @@ protected:
|
||||
int Readable(void) { return (tail >= head) ? size - tail - (head ? 0 : 1) : head - tail - 1; } // keep a 1 byte gap!
|
||||
int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
|
||||
int Byte(int Offset);
|
||||
void Set(int Offset, int Length, int Value);
|
||||
bool Set(int Offset, int Length, int Value);
|
||||
protected:
|
||||
int GetStartCode(int Offset) { return (Byte(Offset) == 0x00 && Byte(Offset + 1) == 0x00 && Byte(Offset + 2) == 0x01) ? Byte(Offset + 3) : -1; }
|
||||
int GetPictureType(int Offset) { return (Byte(Offset + 5) >> 3) & 0x07; }
|
||||
int FindStartCode(uchar Code, int Offset = 0);
|
||||
int GetAudioPacketLength(int Offset = 0);
|
||||
int GetPacketLength(int Offset = 0);
|
||||
public:
|
||||
cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
||||
virtual ~cRingBuffer();
|
||||
@ -459,7 +461,7 @@ int cRingBuffer::Byte(int Offset)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cRingBuffer::Set(int Offset, int Length, int Value)
|
||||
bool cRingBuffer::Set(int Offset, int Length, int Value)
|
||||
{
|
||||
if (buffer && Offset + Length <= Available() ) {
|
||||
Offset += head;
|
||||
@ -469,7 +471,9 @@ void cRingBuffer::Set(int Offset, int Length, int Value)
|
||||
buffer[Offset] = Value;
|
||||
Offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cRingBuffer::Skip(int n)
|
||||
@ -598,15 +602,15 @@ int cRingBuffer::FindStartCode(uchar Code, int Offset)
|
||||
int c = GetStartCode(Offset + i);
|
||||
if (c == Code)
|
||||
return i;
|
||||
if (i > 0 && c == SC_BLOCK)
|
||||
if (i > 0 && c == SC_PHEAD)
|
||||
break; // found another block start while looking for a different code
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cRingBuffer::GetAudioPacketLength(int Offset)
|
||||
int cRingBuffer::GetPacketLength(int Offset)
|
||||
{
|
||||
// Returns the entire length of the audio packet starting at offset.
|
||||
// Returns the entire length of the packet starting at offset.
|
||||
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
|
||||
}
|
||||
|
||||
@ -729,6 +733,7 @@ private:
|
||||
bool ok, synced, stop;
|
||||
time_t lastDiskSpaceCheck;
|
||||
bool RunningLowOnDiskSpace(void);
|
||||
int ScanVideoPacket(int *PictureType, int Offset);
|
||||
int Synchronize(void);
|
||||
bool NextFile(void);
|
||||
virtual int Write(int Max = -1);
|
||||
@ -807,40 +812,81 @@ bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
||||
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()) {
|
||||
for (int i = Offset; 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.
|
||||
|
||||
int LastPackHeader = -1;
|
||||
|
||||
pictureType = NO_PICTURE;
|
||||
bool Block = false;
|
||||
|
||||
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_BLOCK: if (Block && synced)
|
||||
return i; // found a block, so return its length
|
||||
if (i) {
|
||||
Skip(i);
|
||||
if (synced)
|
||||
esyslog(LOG_ERR, "ERROR: skipped %d bytes", i);
|
||||
i = 0;
|
||||
}
|
||||
Block = true;
|
||||
case SC_PHEAD: LastPackHeader = i;
|
||||
break;
|
||||
case SC_PICTURE: if (Block) {
|
||||
pictureType = GetPictureType(i);
|
||||
switch (pictureType) {
|
||||
case I_FRAME: synced = true;
|
||||
case P_FRAME:
|
||||
case B_FRAME: break;
|
||||
default: esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pictureType);
|
||||
pictureType = NO_PICTURE;
|
||||
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 (LastPackHeader == 0) {
|
||||
if (pt == I_FRAME)
|
||||
synced = true;
|
||||
}
|
||||
else if (LastPackHeader > 0) {
|
||||
Skip(LastPackHeader);
|
||||
LastPackHeader = -1;
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
else { // LastPackHeader < 0
|
||||
Skip(i + l);
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (synced)
|
||||
pictureType = pt;
|
||||
}
|
||||
else if (LastPackHeader > 0)
|
||||
return LastPackHeader;
|
||||
else
|
||||
return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "ERROR: picture header outside of block");
|
||||
i += l - 1; // -1 to compensate for i++ in the loop!
|
||||
LastPackHeader = -1;
|
||||
}
|
||||
break;
|
||||
case SC_AUDIO: i += GetAudioPacketLength(i) - 1; // -1 to compensate for i++ in the loop!
|
||||
case SC_AUDIO: i += GetPacketLength(i) - 1; // -1 to compensate for i++ in the loop!
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -919,12 +965,13 @@ private:
|
||||
int replayFile;
|
||||
eReplayMode mode;
|
||||
int lastIndex, stillIndex;
|
||||
int brakeCounter, stillCounter;
|
||||
int brakeCounter;
|
||||
eReplayCmd command;
|
||||
bool active;
|
||||
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
||||
void Close(void);
|
||||
void SetCmd(eReplayCmd Cmd) { LOCK_THREAD; command = Cmd; }
|
||||
void SetTemporalReference(void);
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
@ -941,7 +988,7 @@ public:
|
||||
void Backward(void) { SetCmd(rcBackward); }
|
||||
int SkipFrames(int Frames);
|
||||
void SkipSeconds(int Seconds);
|
||||
void Goto(int Position);
|
||||
void Goto(int Position, bool Still = false);
|
||||
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||
};
|
||||
|
||||
@ -954,7 +1001,7 @@ cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
||||
videoDev = *OutFile;
|
||||
replayFile = fileName.Open();
|
||||
mode = rmPlay;
|
||||
brakeCounter = stillCounter = 0;
|
||||
brakeCounter = 0;
|
||||
command = rcNone;
|
||||
lastIndex = stillIndex = -1;
|
||||
active = false;
|
||||
@ -1154,18 +1201,18 @@ void cReplayBuffer::SkipSeconds(int Seconds)
|
||||
}
|
||||
}
|
||||
|
||||
void cReplayBuffer::Goto(int Index)
|
||||
void cReplayBuffer::Goto(int Index, bool Still)
|
||||
{
|
||||
LOCK_THREAD;
|
||||
|
||||
command = rcStill;
|
||||
if (Still)
|
||||
command = rcStill;
|
||||
if (++Index <= 0)
|
||||
Index = 1; // not '0', to allow GetNextIFrame() below to work!
|
||||
uchar FileNumber;
|
||||
int FileOffset;
|
||||
if ((stillIndex = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset)) >= 0)
|
||||
NextFile(FileNumber, FileOffset);
|
||||
stillCounter = 20; // apparently we need to repeat the still frame several times to flush all buffers?!
|
||||
SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||
Clear();
|
||||
}
|
||||
@ -1206,6 +1253,23 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
|
||||
return replayFile >= 0;
|
||||
}
|
||||
|
||||
void cReplayBuffer::SetTemporalReference(void)
|
||||
{
|
||||
for (int i = 0; i < Available(); i++) {
|
||||
if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) {
|
||||
switch (Byte(i + 3)) {
|
||||
case SC_PICTURE: {
|
||||
unsigned short m = (Byte(i + 4) << 8) | Byte(i + 5);
|
||||
m &= 0x003F;
|
||||
Set(i + 4, 1, m >> 8);
|
||||
Set(i + 5, 1, m & 0xFF);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cReplayBuffer::Read(int Max = -1)
|
||||
{
|
||||
if (mode != rmPlay) {
|
||||
@ -1213,18 +1277,14 @@ int cReplayBuffer::Read(int Max = -1)
|
||||
if (Available())
|
||||
return 0; // write out the entire block
|
||||
if (mode == rmStill) {
|
||||
if (stillCounter > 0) {
|
||||
stillCounter--;
|
||||
uchar FileNumber;
|
||||
int FileOffset, Length;
|
||||
if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
|
||||
if (!NextFile(FileNumber, FileOffset))
|
||||
return -1;
|
||||
Max = Length;
|
||||
}
|
||||
uchar FileNumber;
|
||||
int FileOffset, Length;
|
||||
if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
|
||||
if (!NextFile(FileNumber, FileOffset))
|
||||
return -1;
|
||||
Max = Length;
|
||||
}
|
||||
else
|
||||
command = rcPause;
|
||||
command = rcPause;
|
||||
}
|
||||
else {
|
||||
int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
|
||||
@ -1268,9 +1328,13 @@ int cReplayBuffer::Read(int Max = -1)
|
||||
} while (readin < Max && Free() > 0);
|
||||
if (mode != rmPlay) {
|
||||
// delete the audio data in modes other than rmPlay:
|
||||
int AudioOffset = FindStartCode(SC_AUDIO);
|
||||
if (AudioOffset >= 0)
|
||||
Set(AudioOffset, GetAudioPacketLength(AudioOffset), 0);
|
||||
int AudioOffset, StartOffset = 0;
|
||||
while ((AudioOffset = FindStartCode(SC_AUDIO, StartOffset)) >= 0) {
|
||||
if (!Set(StartOffset + AudioOffset, GetPacketLength(StartOffset + AudioOffset), 0))
|
||||
break; // to be able to replay old AV_PES recordings!
|
||||
StartOffset += AudioOffset;
|
||||
}
|
||||
SetTemporalReference();
|
||||
}
|
||||
return readin;
|
||||
}
|
||||
@ -2319,10 +2383,10 @@ bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
return false;
|
||||
}
|
||||
|
||||
void cDvbApi::Goto(int Position)
|
||||
void cDvbApi::Goto(int Position, bool Still)
|
||||
{
|
||||
if (replayBuffer)
|
||||
replayBuffer->Goto(Position);
|
||||
replayBuffer->Goto(Position, Still);
|
||||
}
|
||||
|
||||
// --- cEITScanner -----------------------------------------------------------
|
||||
|
7
dvbapi.h
7
dvbapi.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbapi.h 1.29 2000/12/25 15:17:03 kls Exp $
|
||||
* $Id: dvbapi.h 1.30 2001/01/07 15:56:10 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBAPI_H
|
||||
@ -239,8 +239,9 @@ public:
|
||||
bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||
// Returns the current and total frame index, optionally snapped to the
|
||||
// nearest I-frame.
|
||||
void Goto(int Index);
|
||||
// Positions to the given index and displays that frame as a still picture.
|
||||
void Goto(int Index, bool Still = false);
|
||||
// Positions to the given index and displays that frame as a still picture
|
||||
// if Still is true.
|
||||
};
|
||||
|
||||
class cEITScanner {
|
||||
|
6
menu.c
6
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 1.56 2000/12/25 15:18:32 kls Exp $
|
||||
* $Id: menu.c 1.57 2001/01/07 15:59:56 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -2155,7 +2155,7 @@ void cReplayControl::MarkJump(bool Forward)
|
||||
if (dvbApi->GetIndex(Current, Total)) {
|
||||
cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
|
||||
if (m)
|
||||
dvbApi->Goto(m->position);
|
||||
dvbApi->Goto(m->position, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2175,7 +2175,7 @@ void cReplayControl::MarkMove(bool Forward)
|
||||
if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
|
||||
return;
|
||||
}
|
||||
dvbApi->Goto(m->position = p);
|
||||
dvbApi->Goto(m->position = p, true);
|
||||
marks.Save();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user