mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Implemented 'on disk editing'
This commit is contained in:
parent
be137ee37f
commit
4e354bc9a0
23
FORMATS
23
FORMATS
@ -90,3 +90,26 @@ Video Disk Recorder File Formats
|
|||||||
CPU status : /usr/loval/bin/cpustatus 2>&1
|
CPU status : /usr/loval/bin/cpustatus 2>&1
|
||||||
Disk space : df -h | grep '/video' | awk '{ print 100 - $5 "% free"; }'
|
Disk space : df -h | grep '/video' | awk '{ print 100 - $5 "% free"; }'
|
||||||
|
|
||||||
|
* marks.vdr
|
||||||
|
|
||||||
|
This file (if present in a recording directory) contains the editing marks
|
||||||
|
defined for this recording.
|
||||||
|
|
||||||
|
Each line contains the definition of one mark in the following format:
|
||||||
|
|
||||||
|
hh:mm:ss.ff comment
|
||||||
|
|
||||||
|
where 'hh:mm:ss.ff' is a frame position within the recording, given as "hours,
|
||||||
|
minutes, seconds and (optional) frame number". 'comment' can be any string
|
||||||
|
and may be used to describe this mark. If present, 'comment' must be separated
|
||||||
|
from the frame position by at least one blank.
|
||||||
|
|
||||||
|
The lines in this file need not necessarily appear in the correct temporal
|
||||||
|
sequence, they will be automatically sorted by time index.
|
||||||
|
|
||||||
|
CURRENT RESTRICTIONS:
|
||||||
|
|
||||||
|
- the 'comment' is currently not used by VDR
|
||||||
|
- marks must have a frame number, and that frame MUST be an I-frame (this
|
||||||
|
means that only marks generated by VDR itself can be used, since they
|
||||||
|
will always be guaranteed to mark I-frames).
|
||||||
|
4
HISTORY
4
HISTORY
@ -312,7 +312,7 @@ Video Disk Recorder Revision History
|
|||||||
an early state and may still cause some problems, but it appears to work nice
|
an early state and may still cause some problems, but it appears to work nice
|
||||||
already.
|
already.
|
||||||
|
|
||||||
2000-12-08: Version 0.70
|
2000-12-28: Version 0.70
|
||||||
|
|
||||||
- VDR now requires driver version 0.80 or higher.
|
- VDR now requires driver version 0.80 or higher.
|
||||||
- Recordings are now saved in PES mode. Note that you now need to install the
|
- Recordings are now saved in PES mode. Note that you now need to install the
|
||||||
@ -327,3 +327,5 @@ Video Disk Recorder Revision History
|
|||||||
- Fixed handling of channel switching with the "Blue" button in the "What's on
|
- Fixed handling of channel switching with the "Blue" button in the "What's on
|
||||||
now/next?" menus.
|
now/next?" menus.
|
||||||
- Fixed saving the MarginStop setup parameter.
|
- Fixed saving the MarginStop setup parameter.
|
||||||
|
- Fixed missing initialization in cConfig.
|
||||||
|
- Implemented "On Disk Editing".
|
||||||
|
5
INSTALL
5
INSTALL
@ -37,7 +37,7 @@ following values 'make' call to activate the respective control mode:
|
|||||||
REMOTE=RCU control via the "Remote Control Unit" receiver
|
REMOTE=RCU control via the "Remote Control Unit" receiver
|
||||||
(see http://www.cadsoft.de/people/kls/vdr/remote.htm)
|
(see http://www.cadsoft.de/people/kls/vdr/remote.htm)
|
||||||
REMOTE=LIRC control via the "Linux Infrared Remote Control"
|
REMOTE=LIRC control via the "Linux Infrared Remote Control"
|
||||||
(see http://fsinfo.cs.uni-sb.de/~columbus/lirc)
|
(see http://www.lirc.org)
|
||||||
|
|
||||||
Adding "DEBUG_OSD=1" will use the PC screen (or current window)
|
Adding "DEBUG_OSD=1" will use the PC screen (or current window)
|
||||||
to display texts instead of the DVB card's on-screen display
|
to display texts instead of the DVB card's on-screen display
|
||||||
@ -166,6 +166,5 @@ into learning mode.
|
|||||||
|
|
||||||
If the program has been compiled with 'REMOTE=LIRC', no 'keys.conf' file
|
If the program has been compiled with 'REMOTE=LIRC', no 'keys.conf' file
|
||||||
will be used. Instead, the key names as listed in the source file 'config.c'
|
will be used. Instead, the key names as listed in the source file 'config.c'
|
||||||
must be used when setting up LIRC. See http://www2.arnes.si/~mthale1 for
|
must be used when setting up LIRC. See http://www.lirc.org for more about LIRC.
|
||||||
more about LIRC.
|
|
||||||
|
|
||||||
|
46
MANUAL
46
MANUAL
@ -174,6 +174,52 @@ Video Disk Recorder User's Manual
|
|||||||
used to easily delete a recording after watching it, or to switch
|
used to easily delete a recording after watching it, or to switch
|
||||||
to a different recording.
|
to a different recording.
|
||||||
|
|
||||||
|
* Editing a Recording
|
||||||
|
|
||||||
|
While in Replay mode, the following keys can be used to manipulate editing
|
||||||
|
marks:
|
||||||
|
|
||||||
|
- 0 Toggles an editing mark. If the mark indicator shows a red triangle,
|
||||||
|
the current mark is deleted. Otherwise a new mark is set at the
|
||||||
|
current position.
|
||||||
|
- 4, 6 Move an editing mark back and forward. You need to first jump to
|
||||||
|
an editing mark for this to work.
|
||||||
|
- 7, 9 Jump back and forward between editing marks. Replay goes into still
|
||||||
|
mode after jumping to a mark.
|
||||||
|
- 8 Positions replay at a point 3 seconds before the current or next
|
||||||
|
"start" mark and starts replay.
|
||||||
|
- 2 Start the actual cutting process.
|
||||||
|
|
||||||
|
Editing marks are represented by black, vertical lines in the progress display.
|
||||||
|
A small black triangle at the top of the mark means that this is a "start"
|
||||||
|
mark, and a triangle at the bottom means that this is an "end" mark.
|
||||||
|
The cutting process will save all video data between "start" and "end" marks
|
||||||
|
into a new file (the original recording remains untouched). The new file will
|
||||||
|
have the same name as the original recording, preceeded with a '%' character
|
||||||
|
(imagine the '%' somehow looking like a pair of scissors ;-). Red bars in the
|
||||||
|
progress display indicate which video sequences will be saved by the cutting
|
||||||
|
process.
|
||||||
|
|
||||||
|
The video sequences to be saved by the cutting process are determined by an
|
||||||
|
"even/odd" algorithm. This means that every odd numbered editing mark (i.e.
|
||||||
|
1, 3, 5,...) represents a "start" mark, while every even numbered mark (2, 4,
|
||||||
|
6,...) is an "end" mark. Inserting or toggling a mark on or off automatically
|
||||||
|
adjusts the sequence to the right side of that mark.
|
||||||
|
|
||||||
|
Use the keys described under "Replay Control" to position to, e.g., the
|
||||||
|
beginning and end of commercial breaks and press the '0' key to set the
|
||||||
|
necessary editing marks. After that you may want to use the '7' and '9'
|
||||||
|
keys to jump to each mark and maybe use the '4' and '6' keys to fine tune
|
||||||
|
them. Once all marks are in place, press '2' to start the actual cutting
|
||||||
|
process, which will run as a background process. When replaying the edited
|
||||||
|
version of the recording you can use the '8' key to jump to a point just
|
||||||
|
before the next cut and have a look at the resulting sequence.
|
||||||
|
|
||||||
|
Currently editing marks can only be set at I-frames, which typically is
|
||||||
|
every 12th frame. So editing can be done with a resolution of roughly half
|
||||||
|
a second. A "start" mark marks the first frame of a resulting video
|
||||||
|
sequence, and an "end" mark marks the last frame of that sequence.
|
||||||
|
|
||||||
* Programming the Timer
|
* Programming the Timer
|
||||||
|
|
||||||
Use the "Timer" menu to maintain your list of timer controlled recordings.
|
Use the "Timer" menu to maintain your list of timer controlled recordings.
|
||||||
|
2
README
2
README
@ -27,7 +27,7 @@ driver software (of course, the hardware still has to be bought).
|
|||||||
|
|
||||||
The on screen menu system is simple, but shall provide all the
|
The on screen menu system is simple, but shall provide all the
|
||||||
possibilites necessary to perform timer controlled recording,
|
possibilites necessary to perform timer controlled recording,
|
||||||
file management and, maybe, even "on disk editing". The menus
|
file management and even "on disk editing". The menus
|
||||||
of commercial set-top-boxes usually are a lot more fancy than
|
of commercial set-top-boxes usually are a lot more fancy than
|
||||||
the ones in this system, but here we have the full source code
|
the ones in this system, but here we have the full source code
|
||||||
and can modify the menus in whatever way desired.
|
and can modify the menus in whatever way desired.
|
||||||
|
3
TODO
3
TODO
@ -3,8 +3,5 @@ TODO list for the Video Disk Recorder project
|
|||||||
|
|
||||||
* Implement simultaneous record/replay with a single DVB card once
|
* Implement simultaneous record/replay with a single DVB card once
|
||||||
the card driver/firmware allows this.
|
the card driver/firmware allows this.
|
||||||
* Implement "on-disk editing" to allow "cutting out" of certain
|
|
||||||
scenes in order to archive them (or, reversely, cut out
|
|
||||||
commercial breaks).
|
|
||||||
* Implement channel scanning.
|
* Implement channel scanning.
|
||||||
* Implement remaining commands in SVDRP.
|
* Implement remaining commands in SVDRP.
|
||||||
|
23
config.h
23
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.35 2000/12/08 13:57:23 kls Exp $
|
* $Id: config.h 1.36 2000/12/25 14:20:09 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H
|
#ifndef __CONFIG_H
|
||||||
@ -14,6 +14,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
@ -42,6 +43,15 @@ enum eKeys { // "Up" and "Down" must be the first two keys!
|
|||||||
k_Flags = k_Repeat | k_Release,
|
k_Flags = k_Repeat | k_Release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is in preparation for having more key codes:
|
||||||
|
#define kMarkToggle k0
|
||||||
|
#define kMarkMoveBack k4
|
||||||
|
#define kMarkMoveForward k6
|
||||||
|
#define kMarkJumpBack k7
|
||||||
|
#define kMarkJumpForward k9
|
||||||
|
#define kEditCut k2
|
||||||
|
#define kEditTest k8
|
||||||
|
|
||||||
#define RAWKEY(k) ((k) & ~k_Flags)
|
#define RAWKEY(k) ((k) & ~k_Flags)
|
||||||
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
|
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
|
||||||
#define NORMALKEY(k) ((k) & ~k_Repeat)
|
#define NORMALKEY(k) ((k) & ~k_Repeat)
|
||||||
@ -157,16 +167,20 @@ private:
|
|||||||
cList<T>::Clear();
|
cList<T>::Clear();
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
cConfig(void) { fileName = NULL; }
|
||||||
|
virtual ~cConfig() { delete fileName; }
|
||||||
virtual bool Load(const char *FileName)
|
virtual bool Load(const char *FileName)
|
||||||
{
|
{
|
||||||
isyslog(LOG_INFO, "loading %s", FileName);
|
|
||||||
bool result = true;
|
|
||||||
Clear();
|
Clear();
|
||||||
fileName = strdup(FileName);
|
fileName = strdup(FileName);
|
||||||
|
bool result = false;
|
||||||
|
if (access(FileName, F_OK) == 0) {
|
||||||
|
isyslog(LOG_INFO, "loading %s", FileName);
|
||||||
FILE *f = fopen(fileName, "r");
|
FILE *f = fopen(fileName, "r");
|
||||||
if (f) {
|
if (f) {
|
||||||
int line = 0;
|
int line = 0;
|
||||||
char buffer[MaxBuffer];
|
char buffer[MaxBuffer];
|
||||||
|
result = true;
|
||||||
while (fgets(buffer, sizeof(buffer), f) > 0) {
|
while (fgets(buffer, sizeof(buffer), f) > 0) {
|
||||||
line++;
|
line++;
|
||||||
T *l = new T;
|
T *l = new T;
|
||||||
@ -181,9 +195,8 @@ public:
|
|||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
LOG_ERROR_STR(fileName);
|
LOG_ERROR_STR(fileName);
|
||||||
result = false;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
603
dvbapi.c
603
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.43 2000/12/10 11:00:00 kls Exp $
|
* $Id: dvbapi.c 1.44 2000/12/25 15:18:02 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
@ -21,6 +21,7 @@ extern "C" {
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
|
#include "recording.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ static void SetPlayMode(int VideoDev, int Mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *IndexToStr(int Index, bool WithFrame)
|
const char *IndexToHMSF(int Index, bool WithFrame)
|
||||||
{
|
{
|
||||||
static char buffer[16];
|
static char buffer[16];
|
||||||
int f = (Index % FRAMESPERSEC) + 1;
|
int f = (Index % FRAMESPERSEC) + 1;
|
||||||
@ -94,6 +95,14 @@ const char *IndexToStr(int Index, bool WithFrame)
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HMSFToIndex(const char *HMSF)
|
||||||
|
{
|
||||||
|
int h, m, s, f = 0;
|
||||||
|
if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
|
||||||
|
return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cResumeFile ------------------------------------------------------------
|
// --- cResumeFile ------------------------------------------------------------
|
||||||
|
|
||||||
cResumeFile::cResumeFile(const char *FileName)
|
cResumeFile::cResumeFile(const char *FileName)
|
||||||
@ -156,13 +165,13 @@ private:
|
|||||||
cResumeFile resumeFile;
|
cResumeFile resumeFile;
|
||||||
bool CatchUp(void);
|
bool CatchUp(void);
|
||||||
public:
|
public:
|
||||||
cIndexFile(const char *FileName, bool Record = false);
|
cIndexFile(const char *FileName, bool Record);
|
||||||
~cIndexFile();
|
~cIndexFile();
|
||||||
void Write(uchar PictureType, uchar FileNumber, int FileOffset);
|
void Write(uchar PictureType, uchar FileNumber, int FileOffset);
|
||||||
bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL);
|
bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
|
||||||
int GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length = NULL);
|
int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL);
|
||||||
int Get(uchar FileNumber, int FileOffset);
|
int Get(uchar FileNumber, int FileOffset);
|
||||||
int Last(void) { return last; }
|
int Last(void) { CatchUp(); return last; }
|
||||||
int GetResume(void) { return resumeFile.Read(); }
|
int GetResume(void) { return resumeFile.Read(); }
|
||||||
bool StoreResume(int Index) { return resumeFile.Save(Index); }
|
bool StoreResume(int Index) { return resumeFile.Save(Index); }
|
||||||
};
|
};
|
||||||
@ -296,7 +305,7 @@ void cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType)
|
bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
|
||||||
{
|
{
|
||||||
if (index) {
|
if (index) {
|
||||||
CatchUp();
|
CatchUp();
|
||||||
@ -305,6 +314,14 @@ bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *Pictu
|
|||||||
*FileOffset = index[Index].offset;
|
*FileOffset = index[Index].offset;
|
||||||
if (PictureType)
|
if (PictureType)
|
||||||
*PictureType = index[Index].type;
|
*PictureType = index[Index].type;
|
||||||
|
if (Length) {
|
||||||
|
int fn = index[Index + 1].number;
|
||||||
|
int fo = index[Index + 1].offset;
|
||||||
|
if (fn == *FileNumber)
|
||||||
|
*Length = fo - *FileOffset;
|
||||||
|
else
|
||||||
|
*Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,8 +338,14 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *F
|
|||||||
Index += d;
|
Index += d;
|
||||||
if (Index >= 0 && Index <= last - 100) { // '- 100': need to stay off the end!
|
if (Index >= 0 && Index <= last - 100) { // '- 100': need to stay off the end!
|
||||||
if (index[Index].type == I_FRAME) {
|
if (index[Index].type == I_FRAME) {
|
||||||
|
if (FileNumber)
|
||||||
*FileNumber = index[Index].number;
|
*FileNumber = index[Index].number;
|
||||||
|
else
|
||||||
|
FileNumber = &index[Index].number;
|
||||||
|
if (FileOffset)
|
||||||
*FileOffset = index[Index].offset;
|
*FileOffset = index[Index].offset;
|
||||||
|
else
|
||||||
|
FileOffset = &index[Index].offset;
|
||||||
if (Length) {
|
if (Length) {
|
||||||
// all recordings end with a non-I_FRAME, so the following should be safe:
|
// all recordings end with a non-I_FRAME, so the following should be safe:
|
||||||
int fn = index[Index + 1].number;
|
int fn = index[Index + 1].number;
|
||||||
@ -387,6 +410,11 @@ protected:
|
|||||||
int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
|
int Writeable(void) { return (tail >= head) ? tail - head : size - head; }
|
||||||
int Byte(int Offset);
|
int Byte(int Offset);
|
||||||
void Set(int Offset, int Length, int Value);
|
void 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);
|
||||||
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();
|
||||||
@ -559,49 +587,7 @@ int cRingBuffer::Write(int Max)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cFileBuffer -----------------------------------------------------------
|
int cRingBuffer::FindStartCode(uchar Code, int Offset)
|
||||||
|
|
||||||
class cFileBuffer : public cRingBuffer {
|
|
||||||
protected:
|
|
||||||
cIndexFile *index;
|
|
||||||
uchar fileNumber;
|
|
||||||
char *fileName, *pFileNumber;
|
|
||||||
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);
|
|
||||||
public:
|
|
||||||
cFileBuffer(int *InFile, int *OutFile, const char *FileName, bool Recording, int Size, int FreeLimit = 0, int AvailLimit = 0);
|
|
||||||
virtual ~cFileBuffer();
|
|
||||||
};
|
|
||||||
|
|
||||||
cFileBuffer::cFileBuffer(int *InFile, int *OutFile, const char *FileName, bool Recording, int Size, int FreeLimit = 0, int AvailLimit = 0)
|
|
||||||
:cRingBuffer(InFile, OutFile, Size, FreeLimit, AvailLimit)
|
|
||||||
{
|
|
||||||
index = NULL;
|
|
||||||
fileNumber = 0;
|
|
||||||
// Prepare the file name:
|
|
||||||
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
|
|
||||||
if (!fileName) {
|
|
||||||
esyslog(LOG_ERR, "ERROR: can't copy file name '%s'", fileName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
strcpy(fileName, FileName);
|
|
||||||
pFileNumber = fileName + strlen(fileName);
|
|
||||||
// Create the index file:
|
|
||||||
index = new cIndexFile(FileName, Recording);
|
|
||||||
if (!index)
|
|
||||||
esyslog(LOG_ERR, "ERROR: can't allocate index");
|
|
||||||
// let's continue without index, so we'll at least have the recording
|
|
||||||
}
|
|
||||||
|
|
||||||
cFileBuffer::~cFileBuffer()
|
|
||||||
{
|
|
||||||
delete index;
|
|
||||||
delete fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cFileBuffer::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.
|
||||||
@ -618,16 +604,124 @@ int cFileBuffer::FindStartCode(uchar Code, int Offset)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cFileBuffer::GetAudioPacketLength(int Offset)
|
int cRingBuffer::GetAudioPacketLength(int Offset)
|
||||||
{
|
{
|
||||||
// Returns the entire length of the audio packet starting at offset.
|
// Returns the entire length of the audio packet starting at offset.
|
||||||
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
|
return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cFileName -------------------------------------------------------------
|
||||||
|
|
||||||
|
class cFileName {
|
||||||
|
private:
|
||||||
|
int file;
|
||||||
|
int fileNumber;
|
||||||
|
char *fileName, *pFileNumber;
|
||||||
|
bool record;
|
||||||
|
public:
|
||||||
|
cFileName(const char *FileName, bool Record);
|
||||||
|
~cFileName();
|
||||||
|
const char *Name(void) { return fileName; }
|
||||||
|
int Number(void) { return fileNumber; }
|
||||||
|
int Open(void);
|
||||||
|
void Close(void);
|
||||||
|
int SetOffset(int Number, int Offset = 0);
|
||||||
|
int NextFile(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
cFileName::cFileName(const char *FileName, bool Record)
|
||||||
|
{
|
||||||
|
file = -1;
|
||||||
|
fileNumber = 0;
|
||||||
|
record = Record;
|
||||||
|
// Prepare the file name:
|
||||||
|
fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
|
||||||
|
if (!fileName) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't copy file name '%s'", fileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strcpy(fileName, FileName);
|
||||||
|
pFileNumber = fileName + strlen(fileName);
|
||||||
|
SetOffset(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cFileName::~cFileName()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
delete fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cFileName::Open(void)
|
||||||
|
{
|
||||||
|
if (file < 0) {
|
||||||
|
if (record) {
|
||||||
|
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
||||||
|
file = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
|
||||||
|
if (file < 0)
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (access(fileName, R_OK) == 0) {
|
||||||
|
dsyslog(LOG_INFO, "playing '%s'", fileName);
|
||||||
|
file = open(fileName, O_RDONLY | O_NONBLOCK);
|
||||||
|
if (file < 0)
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
}
|
||||||
|
else if (errno != ENOENT)
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cFileName::Close(void)
|
||||||
|
{
|
||||||
|
if (file >= 0) {
|
||||||
|
if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
file = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cFileName::SetOffset(int Number, int Offset)
|
||||||
|
{
|
||||||
|
if (fileNumber != Number)
|
||||||
|
Close();
|
||||||
|
if (0 < Number && Number <= MAXFILESPERRECORDING) {
|
||||||
|
fileNumber = Number;
|
||||||
|
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
||||||
|
if (record) {
|
||||||
|
if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
|
||||||
|
return SetOffset(Number + 1);
|
||||||
|
else if (errno != ENOENT) { // something serious has happened
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// found a non existing file suffix
|
||||||
|
}
|
||||||
|
if (Open() >= 0) {
|
||||||
|
if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
|
||||||
|
LOG_ERROR_STR(fileName);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cFileName::NextFile(void)
|
||||||
|
{
|
||||||
|
return SetOffset(fileNumber + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// --- cRecordBuffer ---------------------------------------------------------
|
// --- cRecordBuffer ---------------------------------------------------------
|
||||||
|
|
||||||
class cRecordBuffer : public cFileBuffer, public cThread {
|
class cRecordBuffer : public cRingBuffer, public cThread {
|
||||||
private:
|
private:
|
||||||
|
cFileName fileName;
|
||||||
|
cIndexFile *index;
|
||||||
uchar pictureType;
|
uchar pictureType;
|
||||||
int fileSize;
|
int fileSize;
|
||||||
int videoDev;
|
int videoDev;
|
||||||
@ -647,26 +741,23 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
||||||
:cFileBuffer(InFile, &recordFile, FileName, true, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
|
:cRingBuffer(InFile, &recordFile, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0)
|
||||||
|
,fileName(FileName, true)
|
||||||
{
|
{
|
||||||
|
index = NULL;
|
||||||
pictureType = NO_PICTURE;
|
pictureType = NO_PICTURE;
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
videoDev = *InFile;
|
videoDev = *InFile;
|
||||||
recordFile = -1;
|
recordFile = fileName.Open();
|
||||||
ok = synced = stop = false;
|
ok = synced = stop = false;
|
||||||
lastDiskSpaceCheck = time(NULL);
|
lastDiskSpaceCheck = time(NULL);
|
||||||
if (!fileName)
|
if (!fileName.Name())
|
||||||
return;//XXX find a better way???
|
|
||||||
// Find the highest existing file suffix:
|
|
||||||
for (;;) {
|
|
||||||
sprintf(pFileNumber, RECORDFILESUFFIX, ++fileNumber);
|
|
||||||
if (access(fileName, F_OK) < 0) {
|
|
||||||
if (errno == ENOENT)
|
|
||||||
break; // found a non existing file suffix
|
|
||||||
LOG_ERROR;
|
|
||||||
return;
|
return;
|
||||||
}
|
// Create the index file:
|
||||||
}
|
index = new cIndexFile(FileName, true);
|
||||||
|
if (!index)
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't allocate index");
|
||||||
|
// let's continue without index, so we'll at least have the recording
|
||||||
ok = true;
|
ok = true;
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
@ -675,8 +766,7 @@ cRecordBuffer::~cRecordBuffer()
|
|||||||
{
|
{
|
||||||
stop = true;
|
stop = true;
|
||||||
Cancel(3);
|
Cancel(3);
|
||||||
if (recordFile >= 0)
|
delete index;
|
||||||
CloseVideoFile(recordFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRecordBuffer::Action(void)
|
void cRecordBuffer::Action(void)
|
||||||
@ -707,7 +797,7 @@ void cRecordBuffer::Action(void)
|
|||||||
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
||||||
{
|
{
|
||||||
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||||
uint Free = FreeDiskSpaceMB(fileName);
|
uint Free = FreeDiskSpaceMB(fileName.Name());
|
||||||
lastDiskSpaceCheck = time(NULL);
|
lastDiskSpaceCheck = time(NULL);
|
||||||
if (Free < MINFREEDISKSPACE) {
|
if (Free < MINFREEDISKSPACE) {
|
||||||
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
|
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
|
||||||
@ -762,26 +852,11 @@ 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
|
||||||
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
|
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
|
||||||
if (CloseVideoFile(recordFile) < 0)
|
recordFile = fileName.NextFile();
|
||||||
LOG_ERROR;
|
|
||||||
// don't return 'false', maybe we can still record into the next file
|
|
||||||
recordFile = -1;
|
|
||||||
fileNumber++;
|
|
||||||
if (fileNumber == 0)
|
|
||||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (recordFile < 0) {
|
return recordFile >= 0;
|
||||||
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
|
||||||
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
|
||||||
recordFile = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
|
|
||||||
if (recordFile < 0) {
|
|
||||||
LOG_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRecordBuffer::Write(int Max)
|
int cRecordBuffer::Write(int Max)
|
||||||
@ -798,10 +873,10 @@ int cRecordBuffer::Write(int Max)
|
|||||||
}
|
}
|
||||||
if (NextFile()) {
|
if (NextFile()) {
|
||||||
if (index && pictureType != NO_PICTURE)
|
if (index && pictureType != NO_PICTURE)
|
||||||
index->Write(pictureType, fileNumber, fileSize);
|
index->Write(pictureType, fileName.Number(), fileSize);
|
||||||
int written = 0;
|
int written = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int w = cFileBuffer::Write(n);
|
int w = cRingBuffer::Write(n);
|
||||||
if (w >= 0) {
|
if (w >= 0) {
|
||||||
fileSize += w;
|
fileSize += w;
|
||||||
written += w;
|
written += w;
|
||||||
@ -833,16 +908,18 @@ bool cRecordBuffer::WriteWithTimeout(void)
|
|||||||
|
|
||||||
// --- cReplayBuffer ---------------------------------------------------------
|
// --- cReplayBuffer ---------------------------------------------------------
|
||||||
|
|
||||||
class cReplayBuffer : public cFileBuffer, public cThread {
|
class cReplayBuffer : public cRingBuffer, public cThread {
|
||||||
private:
|
private:
|
||||||
enum eReplayCmd { rcNone, rcPause, rcPlay, rcForward, rcBackward };
|
enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };
|
||||||
enum eReplayMode { rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
|
enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind };
|
||||||
|
cIndexFile *index;
|
||||||
|
cFileName fileName;
|
||||||
int fileOffset;
|
int fileOffset;
|
||||||
int videoDev;
|
int videoDev;
|
||||||
int replayFile;
|
int replayFile;
|
||||||
eReplayMode mode;
|
eReplayMode mode;
|
||||||
int lastIndex;
|
int lastIndex, stillIndex;
|
||||||
int brakeCounter;
|
int brakeCounter, stillCounter;
|
||||||
eReplayCmd command;
|
eReplayCmd command;
|
||||||
bool active;
|
bool active;
|
||||||
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
||||||
@ -862,25 +939,32 @@ public:
|
|||||||
void Play(void) { SetCmd(rcPlay); }
|
void Play(void) { SetCmd(rcPlay); }
|
||||||
void Forward(void) { SetCmd(rcForward); }
|
void Forward(void) { SetCmd(rcForward); }
|
||||||
void Backward(void) { SetCmd(rcBackward); }
|
void Backward(void) { SetCmd(rcBackward); }
|
||||||
|
int SkipFrames(int Frames);
|
||||||
void SkipSeconds(int Seconds);
|
void SkipSeconds(int Seconds);
|
||||||
void GetIndex(int &Current, int &Total);
|
void Goto(int Position);
|
||||||
|
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName)
|
||||||
:cFileBuffer(&replayFile, OutFile, FileName, false, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
:cRingBuffer(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
||||||
|
,fileName(FileName, false)
|
||||||
{
|
{
|
||||||
|
index = NULL;
|
||||||
fileOffset = 0;
|
fileOffset = 0;
|
||||||
videoDev = *OutFile;
|
videoDev = *OutFile;
|
||||||
replayFile = -1;
|
replayFile = fileName.Open();
|
||||||
mode = rmPlay;
|
mode = rmPlay;
|
||||||
brakeCounter = 0;
|
brakeCounter = stillCounter = 0;
|
||||||
command = rcNone;
|
command = rcNone;
|
||||||
lastIndex = -1;
|
lastIndex = stillIndex = -1;
|
||||||
active = false;
|
active = false;
|
||||||
if (!fileName)
|
if (!fileName.Name())
|
||||||
return;//XXX find a better way???
|
return;
|
||||||
// All recordings start with '1':
|
// Create the index file:
|
||||||
fileNumber = 1; //TODO what if it doesn't start with '1'???
|
index = new cIndexFile(FileName, false);
|
||||||
|
if (!index)
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't allocate index");
|
||||||
|
// let's continue without index, so we'll at least have the recording
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,6 +975,7 @@ cReplayBuffer::~cReplayBuffer()
|
|||||||
Close();
|
Close();
|
||||||
SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||||
SetPlayMode(videoDev, VID_PLAY_RESET);
|
SetPlayMode(videoDev, VID_PLAY_RESET);
|
||||||
|
delete index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cReplayBuffer::Action(void)
|
void cReplayBuffer::Action(void)
|
||||||
@ -903,15 +988,20 @@ void cReplayBuffer::Action(void)
|
|||||||
|
|
||||||
int ResumeIndex = Resume();
|
int ResumeIndex = Resume();
|
||||||
if (ResumeIndex >= 0)
|
if (ResumeIndex >= 0)
|
||||||
isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, IndexToStr(ResumeIndex, true));
|
isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, IndexToHMSF(ResumeIndex, true));
|
||||||
active = true;
|
active = true;
|
||||||
for (; active;) {
|
while (active) {
|
||||||
usleep(1); // this keeps the CPU load low
|
usleep(1); // this keeps the CPU load low
|
||||||
|
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
if (command != rcNone) {
|
if (command != rcNone) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
case rcStill: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||||
|
SetPlayMode(videoDev, VID_PLAY_NORMAL);
|
||||||
|
SetMode(rmStill);
|
||||||
|
Paused = FastForward = FastRewind = false;
|
||||||
|
break;
|
||||||
case rcPause: SetPlayMode(videoDev, Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
|
case rcPause: SetPlayMode(videoDev, Paused ? VID_PLAY_NORMAL : VID_PLAY_PAUSE);
|
||||||
Paused = !Paused;
|
Paused = !Paused;
|
||||||
if (FastForward || FastRewind) {
|
if (FastForward || FastRewind) {
|
||||||
@ -920,6 +1010,8 @@ void cReplayBuffer::Action(void)
|
|||||||
}
|
}
|
||||||
FastForward = FastRewind = false;
|
FastForward = FastRewind = false;
|
||||||
SetMode(rmPlay);
|
SetMode(rmPlay);
|
||||||
|
if (!Paused)
|
||||||
|
stillIndex = -1;
|
||||||
break;
|
break;
|
||||||
case rcPlay: if (FastForward || FastRewind || Paused) {
|
case rcPlay: if (FastForward || FastRewind || Paused) {
|
||||||
SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||||
@ -927,6 +1019,7 @@ void cReplayBuffer::Action(void)
|
|||||||
FastForward = FastRewind = Paused = false;
|
FastForward = FastRewind = Paused = false;
|
||||||
SetMode(rmPlay);
|
SetMode(rmPlay);
|
||||||
}
|
}
|
||||||
|
stillIndex = -1;
|
||||||
break;
|
break;
|
||||||
case rcForward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
case rcForward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||||
Clear();
|
Clear();
|
||||||
@ -940,6 +1033,7 @@ void cReplayBuffer::Action(void)
|
|||||||
SetPlayMode(videoDev, VID_PLAY_NORMAL);
|
SetPlayMode(videoDev, VID_PLAY_NORMAL);
|
||||||
SetMode(FastForward ? rmFastForward : rmPlay);
|
SetMode(FastForward ? rmFastForward : rmPlay);
|
||||||
}
|
}
|
||||||
|
stillIndex = -1;
|
||||||
break;
|
break;
|
||||||
case rcBackward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
case rcBackward: SetPlayMode(videoDev, VID_PLAY_CLEAR_BUFFER);
|
||||||
Clear();
|
Clear();
|
||||||
@ -953,6 +1047,7 @@ void cReplayBuffer::Action(void)
|
|||||||
SetPlayMode(videoDev, VID_PLAY_NORMAL);
|
SetPlayMode(videoDev, VID_PLAY_NORMAL);
|
||||||
SetMode(FastRewind ? rmFastRewind : rmPlay);
|
SetMode(FastRewind ? rmFastRewind : rmPlay);
|
||||||
}
|
}
|
||||||
|
stillIndex = -1;
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
@ -969,8 +1064,7 @@ void cReplayBuffer::Action(void)
|
|||||||
void cReplayBuffer::Close(void)
|
void cReplayBuffer::Close(void)
|
||||||
{
|
{
|
||||||
if (replayFile >= 0) {
|
if (replayFile >= 0) {
|
||||||
if (close(replayFile) < 0)
|
fileName.Close();
|
||||||
LOG_ERROR;
|
|
||||||
replayFile = -1;
|
replayFile = -1;
|
||||||
fileOffset = 0;
|
fileOffset = 0;
|
||||||
}
|
}
|
||||||
@ -1001,14 +1095,11 @@ int cReplayBuffer::Resume(void)
|
|||||||
bool cReplayBuffer::Save(void)
|
bool cReplayBuffer::Save(void)
|
||||||
{
|
{
|
||||||
if (index) {
|
if (index) {
|
||||||
int Index = index->Get(fileNumber, fileOffset);
|
int Index = index->Get(fileName.Number(), fileOffset);
|
||||||
if (Index >= 0) {
|
if (Index >= 0) {
|
||||||
Index -= RESUMEBACKUP;
|
Index -= RESUMEBACKUP;
|
||||||
if (Index > 0) {
|
if (Index > 0)
|
||||||
uchar FileNumber;
|
Index = index->GetNextIFrame(Index, false);
|
||||||
int FileOffset;
|
|
||||||
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
Index = 0;
|
Index = 0;
|
||||||
if (Index >= 0)
|
if (Index >= 0)
|
||||||
@ -1018,6 +1109,21 @@ bool cReplayBuffer::Save(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cReplayBuffer::SkipFrames(int Frames)
|
||||||
|
{
|
||||||
|
if (index && Frames) {
|
||||||
|
|
||||||
|
LOCK_THREAD;
|
||||||
|
|
||||||
|
int Current, Total;
|
||||||
|
GetIndex(Current, Total, true);
|
||||||
|
int OldCurrent = Current;
|
||||||
|
Current = index->GetNextIFrame(Current + Frames, Frames > 0);
|
||||||
|
return Current >= 0 ? Current : OldCurrent;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void cReplayBuffer::SkipSeconds(int Seconds)
|
void cReplayBuffer::SkipSeconds(int Seconds)
|
||||||
{
|
{
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
@ -1030,7 +1136,7 @@ void cReplayBuffer::SkipSeconds(int Seconds)
|
|||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
if (index && Seconds) {
|
if (index && Seconds) {
|
||||||
int Index = index->Get(fileNumber, fileOffset);
|
int Index = index->Get(fileName.Number(), fileOffset);
|
||||||
if (Index >= 0) {
|
if (Index >= 0) {
|
||||||
if (Seconds < 0) {
|
if (Seconds < 0) {
|
||||||
int sec = index->Last() / FRAMESPERSEC;
|
int sec = index->Last() / FRAMESPERSEC;
|
||||||
@ -1043,19 +1149,43 @@ void cReplayBuffer::SkipSeconds(int Seconds)
|
|||||||
uchar FileNumber;
|
uchar FileNumber;
|
||||||
int FileOffset;
|
int FileOffset;
|
||||||
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
|
if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
|
||||||
if ((Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset)) >= 0)
|
|
||||||
NextFile(FileNumber, FileOffset);
|
NextFile(FileNumber, FileOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cReplayBuffer::GetIndex(int &Current, int &Total)
|
void cReplayBuffer::Goto(int Index)
|
||||||
|
{
|
||||||
|
LOCK_THREAD;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||||
{
|
{
|
||||||
if (index) {
|
if (index) {
|
||||||
|
|
||||||
LOCK_THREAD;
|
LOCK_THREAD;
|
||||||
|
|
||||||
Current = index->Get(fileNumber, fileOffset);
|
if (stillIndex >= 0)
|
||||||
|
Current = stillIndex;
|
||||||
|
else {
|
||||||
|
Current = index->Get(fileName.Number(), fileOffset);
|
||||||
|
if (SnapToIFrame) {
|
||||||
|
int i1 = index->GetNextIFrame(Current + 1, false);
|
||||||
|
int i2 = index->GetNextIFrame(Current, true);
|
||||||
|
Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
|
||||||
|
}
|
||||||
|
}
|
||||||
Total = index->Last();
|
Total = index->Last();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1066,39 +1196,14 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
|
|||||||
{
|
{
|
||||||
if (FileNumber > 0) {
|
if (FileNumber > 0) {
|
||||||
Clear();
|
Clear();
|
||||||
if (FileNumber != fileNumber) {
|
fileOffset = FileOffset;
|
||||||
|
replayFile = fileName.SetOffset(FileNumber, FileOffset);
|
||||||
|
}
|
||||||
|
else if (replayFile >= 0 && EndOfFile()) {
|
||||||
Close();
|
Close();
|
||||||
fileNumber = FileNumber;
|
replayFile = fileName.NextFile();
|
||||||
}
|
}
|
||||||
}
|
return replayFile >= 0;
|
||||||
if (replayFile >= 0 && EndOfFile()) {
|
|
||||||
Close();
|
|
||||||
fileNumber++;
|
|
||||||
if (fileNumber == 0)
|
|
||||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
|
||||||
}
|
|
||||||
if (replayFile < 0) {
|
|
||||||
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
|
||||||
if (access(fileName, R_OK) == 0) {
|
|
||||||
dsyslog(LOG_INFO, "playing '%s'", fileName);
|
|
||||||
replayFile = open(fileName, O_RDONLY | O_NONBLOCK);
|
|
||||||
if (replayFile < 0) {
|
|
||||||
LOG_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (errno != ENOENT)
|
|
||||||
LOG_ERROR;
|
|
||||||
}
|
|
||||||
if (replayFile >= 0) {
|
|
||||||
if (FileOffset >= 0) {
|
|
||||||
if ((fileOffset = lseek(replayFile, FileOffset, SEEK_SET)) != FileOffset)
|
|
||||||
LOG_ERROR;
|
|
||||||
// don't return 'false', maybe we can still replay the file
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cReplayBuffer::Read(int Max = -1)
|
int cReplayBuffer::Read(int Max = -1)
|
||||||
@ -1107,14 +1212,29 @@ int cReplayBuffer::Read(int Max = -1)
|
|||||||
if (index) {
|
if (index) {
|
||||||
if (Available())
|
if (Available())
|
||||||
return 0; // write out the entire block
|
return 0; // write out the entire block
|
||||||
int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileNumber, fileOffset);
|
if (mode == rmStill) {
|
||||||
if (Index >= 0) {
|
if (stillCounter > 0) {
|
||||||
|
stillCounter--;
|
||||||
uchar FileNumber;
|
uchar FileNumber;
|
||||||
int FileOffset, Length;
|
int FileOffset, Length;
|
||||||
|
if (index->GetNextIFrame(stillIndex + 1, false, &FileNumber, &FileOffset, &Length) >= 0) {
|
||||||
|
if (!NextFile(FileNumber, FileOffset))
|
||||||
|
return -1;
|
||||||
|
Max = Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
command = rcPause;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
|
||||||
|
if (Index >= 0) {
|
||||||
if (mode == rmSlowRewind && (brakeCounter++ % 24) != 0) {
|
if (mode == rmSlowRewind && (brakeCounter++ % 24) != 0) {
|
||||||
// show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
|
// show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
|
||||||
Index = index->GetNextIFrame(Index, true, &FileNumber, &FileOffset, &Length); // jump ahead one frame
|
Index = index->GetNextIFrame(Index, true); // jump ahead one frame
|
||||||
}
|
}
|
||||||
|
uchar FileNumber;
|
||||||
|
int FileOffset, Length;
|
||||||
Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
|
Index = index->GetNextIFrame(Index, mode == rmFastForward, &FileNumber, &FileOffset, &Length);
|
||||||
if (Index >= 0) {
|
if (Index >= 0) {
|
||||||
if (!NextFile(FileNumber, FileOffset))
|
if (!NextFile(FileNumber, FileOffset))
|
||||||
@ -1132,6 +1252,7 @@ int cReplayBuffer::Read(int Max = -1)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
lastIndex = -1;
|
lastIndex = -1;
|
||||||
//XXX timeout as in recording???
|
//XXX timeout as in recording???
|
||||||
@ -1139,7 +1260,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 = cFileBuffer::Read(Max);
|
int r = cRingBuffer::Read(Max);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
readin += r;
|
readin += r;
|
||||||
else
|
else
|
||||||
@ -1164,7 +1285,7 @@ int cReplayBuffer::Write(int Max)
|
|||||||
if (Max) {
|
if (Max) {
|
||||||
int w;
|
int w;
|
||||||
do {
|
do {
|
||||||
w = cFileBuffer::Write(Max);
|
w = cRingBuffer::Write(Max);
|
||||||
if (w >= 0) {
|
if (w >= 0) {
|
||||||
fileOffset += w;
|
fileOffset += w;
|
||||||
Written += w;
|
Written += w;
|
||||||
@ -1218,7 +1339,7 @@ void cTransferBuffer::Action(void)
|
|||||||
Buffer.Read(); // initializes fromDevice for reading
|
Buffer.Read(); // initializes fromDevice for reading
|
||||||
usleep(1); // this keeps the CPU load low
|
usleep(1); // this keeps the CPU load low
|
||||||
}
|
}
|
||||||
for (; active;) {
|
while (active) {
|
||||||
if (Buffer.Read() < 0 || Buffer.Write() < 0)
|
if (Buffer.Read() < 0 || Buffer.Write() < 0)
|
||||||
break;
|
break;
|
||||||
usleep(1); // this keeps the CPU load low
|
usleep(1); // this keeps the CPU load low
|
||||||
@ -1226,6 +1347,160 @@ void cTransferBuffer::Action(void)
|
|||||||
dsyslog(LOG_INFO, "data transfer thread stopped (pid=%d)", getpid());
|
dsyslog(LOG_INFO, "data transfer thread stopped (pid=%d)", getpid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cCuttingBuffer --------------------------------------------------------
|
||||||
|
|
||||||
|
class cCuttingBuffer : public cRingBuffer, public cThread {
|
||||||
|
private:
|
||||||
|
bool active;
|
||||||
|
int fromFile, toFile;
|
||||||
|
cFileName *fromFileName, *toFileName;
|
||||||
|
cIndexFile *fromIndex, *toIndex;
|
||||||
|
cMarks fromMarks, toMarks;
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
public:
|
||||||
|
cCuttingBuffer(const char *FromFileName, const char *ToFileName);
|
||||||
|
virtual ~cCuttingBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
|
||||||
|
:cRingBuffer(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)
|
||||||
|
{
|
||||||
|
active = false;
|
||||||
|
fromFile = toFile = -1;
|
||||||
|
fromFileName = toFileName = NULL;
|
||||||
|
fromIndex = toIndex = NULL;
|
||||||
|
if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
|
||||||
|
fromFileName = new cFileName(FromFileName, false);
|
||||||
|
toFileName = new cFileName(ToFileName, true);
|
||||||
|
fromIndex = new cIndexFile(FromFileName, false);
|
||||||
|
toIndex = new cIndexFile(ToFileName, true);
|
||||||
|
toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "no editing marks found for %s", FromFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
cCuttingBuffer::~cCuttingBuffer()
|
||||||
|
{
|
||||||
|
active = false;
|
||||||
|
Cancel(3);
|
||||||
|
delete fromFileName;
|
||||||
|
delete toFileName;
|
||||||
|
delete fromIndex;
|
||||||
|
delete toIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cCuttingBuffer::Action(void)
|
||||||
|
{
|
||||||
|
dsyslog(LOG_INFO, "video cutting thread started (pid=%d)", getpid());
|
||||||
|
|
||||||
|
cMark *Mark = fromMarks.First();
|
||||||
|
if (Mark) {
|
||||||
|
fromFile = fromFileName->Open();
|
||||||
|
toFile = toFileName->Open();
|
||||||
|
active = fromFile >= 0 && toFile >= 0;
|
||||||
|
int Index = Mark->position;
|
||||||
|
Mark = fromMarks.Next(Mark);
|
||||||
|
int FileSize = 0;
|
||||||
|
int CurrentFileNumber = 0;
|
||||||
|
int LastIFrame = 0;
|
||||||
|
toMarks.Add(0);
|
||||||
|
toMarks.Save();
|
||||||
|
while (active) {
|
||||||
|
uchar FileNumber;
|
||||||
|
int FileOffset, Length;
|
||||||
|
uchar PictureType;
|
||||||
|
|
||||||
|
Clear(); // makes sure one frame is completely read and written
|
||||||
|
|
||||||
|
// Read one frame:
|
||||||
|
|
||||||
|
if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
|
||||||
|
if (FileNumber != CurrentFileNumber) {
|
||||||
|
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
|
||||||
|
CurrentFileNumber = FileNumber;
|
||||||
|
}
|
||||||
|
if (fromFile >= 0)
|
||||||
|
Length = cRingBuffer::Read(Length);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Write one frame:
|
||||||
|
|
||||||
|
if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||||
|
if (FileSize > MAXVIDEOFILESIZE) {
|
||||||
|
toFile = toFileName->NextFile();
|
||||||
|
if (toFile < 0)
|
||||||
|
break;
|
||||||
|
FileSize = 0;
|
||||||
|
}
|
||||||
|
LastIFrame = 0;
|
||||||
|
}
|
||||||
|
cRingBuffer::Write(Length);
|
||||||
|
toIndex->Write(PictureType, toFileName->Number(), FileSize);
|
||||||
|
FileSize += Length;
|
||||||
|
if (!LastIFrame)
|
||||||
|
LastIFrame = toIndex->Last();
|
||||||
|
|
||||||
|
// Check editing marks:
|
||||||
|
|
||||||
|
if (Mark && Index >= Mark->position) {
|
||||||
|
Mark = fromMarks.Next(Mark);
|
||||||
|
if (Mark) {
|
||||||
|
Index = Mark->position;
|
||||||
|
Mark = fromMarks.Next(Mark);
|
||||||
|
CurrentFileNumber = 0; // triggers SetOffset before reading next frame
|
||||||
|
toMarks.Add(LastIFrame);
|
||||||
|
toMarks.Add(toIndex->Last() + 1);
|
||||||
|
toMarks.Save();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break; // final end mark reached
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "no editing marks found!");
|
||||||
|
dsyslog(LOG_INFO, "end video cutting thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cVideoCutter ----------------------------------------------------------
|
||||||
|
|
||||||
|
cCuttingBuffer *cVideoCutter::cuttingBuffer = NULL;
|
||||||
|
|
||||||
|
bool cVideoCutter::Start(const char *FileName)
|
||||||
|
{
|
||||||
|
if (!cuttingBuffer) {
|
||||||
|
const char *EditedVersionName = PrefixVideoFileName(FileName, '%');
|
||||||
|
if (EditedVersionName && RemoveVideoFile(EditedVersionName) && MakeDirs(EditedVersionName, true)) {
|
||||||
|
cuttingBuffer = new cCuttingBuffer(FileName, EditedVersionName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cVideoCutter::Stop(void)
|
||||||
|
{
|
||||||
|
delete cuttingBuffer;
|
||||||
|
cuttingBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cVideoCutter::Active(void)
|
||||||
|
{
|
||||||
|
if (cuttingBuffer) {
|
||||||
|
if (cuttingBuffer->Active())
|
||||||
|
return true;
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cDvbApi ---------------------------------------------------------------
|
// --- cDvbApi ---------------------------------------------------------------
|
||||||
|
|
||||||
int cDvbApi::NumDvbApis = 0;
|
int cDvbApi::NumDvbApis = 0;
|
||||||
@ -1892,6 +2167,11 @@ void cDvbApi::StopTransfer(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cDvbApi::SecondsToFrames(int Seconds)
|
||||||
|
{
|
||||||
|
return Seconds * FRAMESPERSEC;
|
||||||
|
}
|
||||||
|
|
||||||
bool cDvbApi::Recording(void)
|
bool cDvbApi::Recording(void)
|
||||||
{
|
{
|
||||||
if (recordBuffer && !recordBuffer->Active())
|
if (recordBuffer && !recordBuffer->Active())
|
||||||
@ -2017,21 +2297,34 @@ void cDvbApi::Backward(void)
|
|||||||
replayBuffer->Backward();
|
replayBuffer->Backward();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDvbApi::Skip(int Seconds)
|
void cDvbApi::SkipSeconds(int Seconds)
|
||||||
{
|
{
|
||||||
if (replayBuffer)
|
if (replayBuffer)
|
||||||
replayBuffer->SkipSeconds(Seconds);
|
replayBuffer->SkipSeconds(Seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cDvbApi::GetIndex(int &Current, int &Total)
|
int cDvbApi::SkipFrames(int Frames)
|
||||||
|
{
|
||||||
|
if (replayBuffer)
|
||||||
|
return replayBuffer->SkipFrames(Frames);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||||
{
|
{
|
||||||
if (replayBuffer) {
|
if (replayBuffer) {
|
||||||
replayBuffer->GetIndex(Current, Total);
|
replayBuffer->GetIndex(Current, Total, SnapToIFrame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cDvbApi::Goto(int Position)
|
||||||
|
{
|
||||||
|
if (replayBuffer)
|
||||||
|
replayBuffer->Goto(Position);
|
||||||
|
}
|
||||||
|
|
||||||
// --- cEITScanner -----------------------------------------------------------
|
// --- cEITScanner -----------------------------------------------------------
|
||||||
|
|
||||||
cEITScanner::cEITScanner(void)
|
cEITScanner::cEITScanner(void)
|
||||||
|
32
dvbapi.h
32
dvbapi.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: dvbapi.h 1.28 2000/12/09 10:54:09 kls Exp $
|
* $Id: dvbapi.h 1.29 2000/12/25 15:17:03 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __DVBAPI_H
|
#ifndef __DVBAPI_H
|
||||||
@ -22,6 +22,7 @@ typedef unsigned char __u8;
|
|||||||
#include <dvb.h>
|
#include <dvb.h>
|
||||||
#include "dvbosd.h"
|
#include "dvbosd.h"
|
||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
// Overlay facilities
|
// Overlay facilities
|
||||||
#define MAXCLIPRECTS 100
|
#define MAXCLIPRECTS 100
|
||||||
@ -42,11 +43,24 @@ public:
|
|||||||
bool Save(int Index);
|
bool Save(int Index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *IndexToStr(int Index, bool WithFrame = false);
|
const char *IndexToHMSF(int Index, bool WithFrame = false);
|
||||||
// Converts the given index to a string, optionally containing the frame number.
|
// Converts the given index to a string, optionally containing the frame number.
|
||||||
|
int HMSFToIndex(const char *HMSF);
|
||||||
|
// Converts the given string (format: "hh:mm:ss.ff") to an index.
|
||||||
|
|
||||||
class cRecordBuffer;
|
class cRecordBuffer;
|
||||||
class cReplayBuffer;
|
class cReplayBuffer;
|
||||||
class cTransferBuffer;
|
class cTransferBuffer;
|
||||||
|
class cCuttingBuffer;
|
||||||
|
|
||||||
|
class cVideoCutter {
|
||||||
|
private:
|
||||||
|
static cCuttingBuffer *cuttingBuffer;
|
||||||
|
public:
|
||||||
|
static bool Start(const char *FileName);
|
||||||
|
static void Stop(void);
|
||||||
|
static bool Active(void);
|
||||||
|
};
|
||||||
|
|
||||||
class cDvbApi {
|
class cDvbApi {
|
||||||
private:
|
private:
|
||||||
@ -180,6 +194,8 @@ protected:
|
|||||||
// Returns the priority of the current recording session (0..99),
|
// Returns the priority of the current recording session (0..99),
|
||||||
// or -1 if no recording is currently active.
|
// or -1 if no recording is currently active.
|
||||||
public:
|
public:
|
||||||
|
int SecondsToFrames(int Seconds);
|
||||||
|
// Returns the number of frames corresponding to the given number of seconds.
|
||||||
bool Recording(void);
|
bool Recording(void);
|
||||||
// Returns true if we are currently recording.
|
// Returns true if we are currently recording.
|
||||||
bool Replaying(void);
|
bool Replaying(void);
|
||||||
@ -211,12 +227,20 @@ public:
|
|||||||
// Runs the current replay session forward at a higher speed.
|
// Runs the current replay session forward at a higher speed.
|
||||||
void Backward(void);
|
void Backward(void);
|
||||||
// Runs the current replay session backwards at a higher speed.
|
// Runs the current replay session backwards at a higher speed.
|
||||||
void Skip(int Seconds);
|
void SkipSeconds(int Seconds);
|
||||||
// Skips the given number of seconds in the current replay session.
|
// Skips the given number of seconds in the current replay session.
|
||||||
// The sign of 'Seconds' determines the direction in which to skip.
|
// The sign of 'Seconds' determines the direction in which to skip.
|
||||||
// Use a very large negative value to go all the way back to the
|
// Use a very large negative value to go all the way back to the
|
||||||
// beginning of the recording.
|
// beginning of the recording.
|
||||||
bool GetIndex(int &Current, int &Total);
|
int SkipFrames(int Frames);
|
||||||
|
// Returns the new index into the current replay session after skipping
|
||||||
|
// the given number of frames (no actual repositioning is done!).
|
||||||
|
// The sign of 'Frames' determines the direction in which to skip.
|
||||||
|
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.
|
||||||
};
|
};
|
||||||
|
|
||||||
class cEITScanner {
|
class cEITScanner {
|
||||||
|
30
i18n.c
30
i18n.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: i18n.c 1.6 2000/11/19 12:12:53 kls Exp $
|
* $Id: i18n.c 1.7 2000/12/25 17:51:55 kls Exp $
|
||||||
*
|
*
|
||||||
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
|
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
|
||||||
*
|
*
|
||||||
@ -165,22 +165,26 @@ const tPhrase Phrases[] = {
|
|||||||
"Urnik",
|
"Urnik",
|
||||||
},
|
},
|
||||||
// Confirmations:
|
// Confirmations:
|
||||||
{ "Delete Channel?",
|
{ "Delete channel?",
|
||||||
"Kanal löschen?",
|
"Kanal löschen?",
|
||||||
"Odstrani kanal?",
|
"Odstrani kanal?",
|
||||||
},
|
},
|
||||||
{ "Delete Timer?",
|
{ "Delete timer?",
|
||||||
"Timer löschen?",
|
"Timer löschen?",
|
||||||
"Odstani termin?",
|
"Odstani termin?",
|
||||||
},
|
},
|
||||||
{ "Delete Recording?",
|
{ "Delete recording?",
|
||||||
"Aufzeichnung löschen?",
|
"Aufzeichnung löschen?",
|
||||||
"Odstrani posnetek?",
|
"Odstrani posnetek?",
|
||||||
},
|
},
|
||||||
{ "Stop Recording?",
|
{ "Stop recording?",
|
||||||
"Aufzeichnung beenden?",
|
"Aufzeichnung beenden?",
|
||||||
"Koncaj snemanje?",
|
"Koncaj snemanje?",
|
||||||
},
|
},
|
||||||
|
{ "Cancel editing?",
|
||||||
|
"Schneiden abbrechen?",
|
||||||
|
"Zelite prekiniti urejanje?",
|
||||||
|
},
|
||||||
// Channel parameters:
|
// Channel parameters:
|
||||||
{ "Name",
|
{ "Name",
|
||||||
"Name",
|
"Name",
|
||||||
@ -280,6 +284,14 @@ const tPhrase Phrases[] = {
|
|||||||
"Kanal blockiert (zeichnet auf)!",
|
"Kanal blockiert (zeichnet auf)!",
|
||||||
"Zaklenjen kanal (snemanje)!",
|
"Zaklenjen kanal (snemanje)!",
|
||||||
},
|
},
|
||||||
|
{ "Can't start editing process!",
|
||||||
|
"Schnitt kann nicht gestartet werden!",
|
||||||
|
"Ne morem zaceti urejanja!",
|
||||||
|
},
|
||||||
|
{ "Editing process already active!",
|
||||||
|
"Schnitt bereits aktiv!",
|
||||||
|
"Urejanje je ze aktivno!",
|
||||||
|
},
|
||||||
// Setup parameters:
|
// Setup parameters:
|
||||||
{ "OSD-Language",
|
{ "OSD-Language",
|
||||||
"OSD-Sprache",
|
"OSD-Sprache",
|
||||||
@ -445,6 +457,10 @@ const tPhrase Phrases[] = {
|
|||||||
"Aufzeichnung beenden ",
|
"Aufzeichnung beenden ",
|
||||||
"Prekini shranjevanje ",
|
"Prekini shranjevanje ",
|
||||||
},
|
},
|
||||||
|
{ "Cancel editing",
|
||||||
|
"Schneiden abbrechen",
|
||||||
|
"Prekini urejanje",
|
||||||
|
},
|
||||||
{ "Switching primary DVB...",
|
{ "Switching primary DVB...",
|
||||||
"Primäres Interface wird umgeschaltet...",
|
"Primäres Interface wird umgeschaltet...",
|
||||||
"Preklapljanje primarne naprave...",
|
"Preklapljanje primarne naprave...",
|
||||||
@ -453,6 +469,10 @@ const tPhrase Phrases[] = {
|
|||||||
"Auf/Ab für neue Position - dann OK",
|
"Auf/Ab für neue Position - dann OK",
|
||||||
"Gor/Dol za novo poz. - Ok za premik",
|
"Gor/Dol za novo poz. - Ok za premik",
|
||||||
},
|
},
|
||||||
|
{ "Editing process started",
|
||||||
|
"Schnitt gestartet",
|
||||||
|
"Urejanje se je zacelo",
|
||||||
|
},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
186
menu.c
186
menu.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: menu.c 1.55 2000/12/09 11:03:21 kls Exp $
|
* $Id: menu.c 1.56 2000/12/25 15:18:32 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
@ -669,7 +669,7 @@ eOSState cMenuChannels::Del(void)
|
|||||||
return osContinue;
|
return osContinue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Interface->Confirm(tr("Delete Channel?"))) {
|
if (Interface->Confirm(tr("Delete channel?"))) {
|
||||||
// Move and renumber the channels:
|
// Move and renumber the channels:
|
||||||
Channels.Del(channel);
|
Channels.Del(channel);
|
||||||
Channels.ReNumber();
|
Channels.ReNumber();
|
||||||
@ -1039,7 +1039,7 @@ eOSState cMenuTimers::Del(void)
|
|||||||
cTimer *ti = Timers.Get(Index);
|
cTimer *ti = Timers.Get(Index);
|
||||||
if (ti) {
|
if (ti) {
|
||||||
if (!ti->recording) {
|
if (!ti->recording) {
|
||||||
if (Interface->Confirm(tr("Delete Timer?"))) {
|
if (Interface->Confirm(tr("Delete timer?"))) {
|
||||||
Timers.Del(Timers.Get(Index));
|
Timers.Del(Timers.Get(Index));
|
||||||
cOsdMenu::Del(Index);
|
cOsdMenu::Del(Index);
|
||||||
Timers.Save();
|
Timers.Save();
|
||||||
@ -1489,7 +1489,7 @@ eOSState cMenuRecordings::Del(void)
|
|||||||
if (ri) {
|
if (ri) {
|
||||||
//XXX what if this recording's file is currently in use???
|
//XXX what if this recording's file is currently in use???
|
||||||
//XXX if (!ti->recording) {
|
//XXX if (!ti->recording) {
|
||||||
if (Interface->Confirm(tr("Delete Recording?"))) {
|
if (Interface->Confirm(tr("Delete recording?"))) {
|
||||||
if (ri->recording->Delete()) {
|
if (ri->recording->Delete()) {
|
||||||
cReplayControl::ClearLastReplayed(ri->recording->FileName());
|
cReplayControl::ClearLastReplayed(ri->recording->FileName());
|
||||||
cOsdMenu::Del(Current());
|
cOsdMenu::Del(Current());
|
||||||
@ -1663,6 +1663,8 @@ cMenuMain::cMenuMain(bool Replaying)
|
|||||||
Add(new cOsdItem(buffer, osStopRecord));
|
Add(new cOsdItem(buffer, osStopRecord));
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
|
if (cVideoCutter::Active())
|
||||||
|
Add(new cOsdItem(tr("Cancel editing"), osCancelEdit));
|
||||||
SetHelp(tr("Record"), NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
|
SetHelp(tr("Record"), NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
|
||||||
Display();
|
Display();
|
||||||
lastActivity = time(NULL);
|
lastActivity = time(NULL);
|
||||||
@ -1679,13 +1681,19 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
|||||||
case osRecordings: return AddSubMenu(new cMenuRecordings);
|
case osRecordings: return AddSubMenu(new cMenuRecordings);
|
||||||
case osSetup: return AddSubMenu(new cMenuSetup);
|
case osSetup: return AddSubMenu(new cMenuSetup);
|
||||||
case osCommands: return AddSubMenu(new cMenuCommands);
|
case osCommands: return AddSubMenu(new cMenuCommands);
|
||||||
case osStopRecord: if (Interface->Confirm(tr("Stop Recording?"))) {
|
case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
|
||||||
cOsdItem *item = Get(Current());
|
cOsdItem *item = Get(Current());
|
||||||
if (item) {
|
if (item) {
|
||||||
cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING));
|
cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING));
|
||||||
return osEnd;
|
return osEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
|
||||||
|
cVideoCutter::Stop();
|
||||||
|
return osEnd;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: switch (Key) {
|
default: switch (Key) {
|
||||||
case kMenu: state = osEnd; break;
|
case kMenu: state = osEnd; break;
|
||||||
case kRed: if (!HasSubMenu())
|
case kRed: if (!HasSubMenu())
|
||||||
@ -1993,6 +2001,50 @@ void cRecordControls::Process(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cProgressBar ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cProgressBar : public cBitmap {
|
||||||
|
protected:
|
||||||
|
int total;
|
||||||
|
int Pos(int p) { return p * width / total; }
|
||||||
|
void Mark(int x, bool Start, bool Current);
|
||||||
|
public:
|
||||||
|
cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks);
|
||||||
|
};
|
||||||
|
|
||||||
|
cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks)
|
||||||
|
:cBitmap(Width, Height)
|
||||||
|
{
|
||||||
|
total = Total;
|
||||||
|
if (total > 0) {
|
||||||
|
int p = Pos(Current);
|
||||||
|
Fill(0, 0, p, Height - 1, clrGreen);
|
||||||
|
Fill(p + 1, 0, Width - 1, Height - 1, clrWhite);
|
||||||
|
bool Start = true;
|
||||||
|
for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) {
|
||||||
|
int p1 = Pos(m->position);
|
||||||
|
if (Start) {
|
||||||
|
const cMark *m2 = Marks.Next(m);
|
||||||
|
int p2 = Pos(m2 ? m2->position : total);
|
||||||
|
int h = Height / 3;
|
||||||
|
Fill(p1, h, p2, Height - h, clrRed);
|
||||||
|
}
|
||||||
|
Mark(p1, Start, m->position == Current);
|
||||||
|
Start = !Start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cProgressBar::Mark(int x, bool Start, bool Current)
|
||||||
|
{
|
||||||
|
Fill(x, 0, x, height - 1, clrBlack);
|
||||||
|
const int d = height / (Current ? 3 : 9);
|
||||||
|
for (int i = 0; i < d; i++) {
|
||||||
|
int h = Start ? i : height - 1 - i;
|
||||||
|
Fill(x - d + i, h, x + d - i, h, Current ? clrRed : clrBlack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- cReplayControl --------------------------------------------------------
|
// --- cReplayControl --------------------------------------------------------
|
||||||
|
|
||||||
char *cReplayControl::fileName = NULL;
|
char *cReplayControl::fileName = NULL;
|
||||||
@ -2001,10 +2053,12 @@ char *cReplayControl::title = NULL;
|
|||||||
cReplayControl::cReplayControl(void)
|
cReplayControl::cReplayControl(void)
|
||||||
{
|
{
|
||||||
dvbApi = cDvbApi::PrimaryDvbApi;
|
dvbApi = cDvbApi::PrimaryDvbApi;
|
||||||
visible = shown = false;
|
visible = shown = displayFrames = false;
|
||||||
if (fileName)
|
if (fileName) {
|
||||||
|
marks.Load(fileName);
|
||||||
dvbApi->StartReplay(fileName);
|
dvbApi->StartReplay(fileName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cReplayControl::~cReplayControl()
|
cReplayControl::~cReplayControl()
|
||||||
{
|
{
|
||||||
@ -2054,37 +2108,110 @@ bool cReplayControl::ShowProgress(bool Initial)
|
|||||||
{
|
{
|
||||||
int Current, Total;
|
int Current, Total;
|
||||||
|
|
||||||
if (dvbApi->GetIndex(Current, Total)) {
|
if (dvbApi->GetIndex(Current, Total) && Total > 0) {
|
||||||
if (Initial) {
|
if (Initial) {
|
||||||
Interface->Clear();
|
Interface->Clear();
|
||||||
if (title)
|
if (title)
|
||||||
Interface->Write(0, 0, title);
|
Interface->Write(0, 0, title);
|
||||||
|
displayFrames = marks.Count() > 0;
|
||||||
}
|
}
|
||||||
Interface->Write(-7, 2, IndexToStr(Total));
|
Interface->Write(-7, 2, IndexToHMSF(Total));
|
||||||
Interface->Flush();
|
Interface->Flush();
|
||||||
#ifdef DEBUG_OSD
|
#ifdef DEBUG_OSD
|
||||||
int p = Width() * Current / Total;
|
int p = Width() * Current / Total;
|
||||||
Interface->Fill(0, 1, p, 1, clrGreen);
|
Interface->Fill(0, 1, p, 1, clrGreen);
|
||||||
Interface->Fill(p, 1, Width() - p, 1, clrWhite);
|
Interface->Fill(p, 1, Width() - p, 1, clrWhite);
|
||||||
#else
|
#else
|
||||||
int w = Width() * dvbApi->CellWidth();
|
cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks);
|
||||||
int h = dvbApi->LineHeight();
|
|
||||||
int p = w * Current / Total;
|
|
||||||
cBitmap ProgressBar(w, h);
|
|
||||||
|
|
||||||
ProgressBar.Fill(0, 0, p, h - 1, clrGreen);
|
|
||||||
ProgressBar.Fill(p + 1, 0, w - 1, h - 1, clrWhite);
|
|
||||||
Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar);
|
Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar);
|
||||||
|
|
||||||
Interface->Flush();
|
Interface->Flush();
|
||||||
#endif
|
#endif
|
||||||
Interface->Write(0, 2, IndexToStr(Current));
|
Interface->Write(0, 2, IndexToHMSF(Current, displayFrames));
|
||||||
Interface->Flush();
|
Interface->Flush();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cReplayControl::MarkToggle(void)
|
||||||
|
{
|
||||||
|
int Current, Total;
|
||||||
|
if (dvbApi->GetIndex(Current, Total, true)) {
|
||||||
|
cMark *m = marks.Get(Current);
|
||||||
|
if (m)
|
||||||
|
marks.Del(m);
|
||||||
|
else
|
||||||
|
marks.Add(Current);
|
||||||
|
marks.Save();
|
||||||
|
}
|
||||||
|
displayFrames = marks.Count() > 0;
|
||||||
|
if (!displayFrames)
|
||||||
|
Interface->Fill(0, 2, Width() / 2, 1, clrBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cReplayControl::MarkJump(bool Forward)
|
||||||
|
{
|
||||||
|
int Current, Total;
|
||||||
|
if (dvbApi->GetIndex(Current, Total)) {
|
||||||
|
cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
|
||||||
|
if (m)
|
||||||
|
dvbApi->Goto(m->position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cReplayControl::MarkMove(bool Forward)
|
||||||
|
{
|
||||||
|
int Current, Total;
|
||||||
|
if (dvbApi->GetIndex(Current, Total)) {
|
||||||
|
cMark *m = marks.Get(Current);
|
||||||
|
if (m) {
|
||||||
|
int p = dvbApi->SkipFrames(Forward ? 1 : -1);
|
||||||
|
cMark *m2;
|
||||||
|
if (Forward) {
|
||||||
|
if ((m2 = marks.Next(m)) != NULL && m2->position <= p)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dvbApi->Goto(m->position = p);
|
||||||
|
marks.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cReplayControl::EditCut(void)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
if (!cVideoCutter::Active()) {
|
||||||
|
if (!cVideoCutter::Start(fileName))
|
||||||
|
Interface->Error(tr("Can't start editing process!"));
|
||||||
|
else
|
||||||
|
Interface->Info(tr("Editing process started"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Interface->Error(tr("Editing process already active!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cReplayControl::EditTest(void)
|
||||||
|
{
|
||||||
|
int Current, Total;
|
||||||
|
if (dvbApi->GetIndex(Current, Total)) {
|
||||||
|
cMark *m = marks.Get(Current);
|
||||||
|
if (!m)
|
||||||
|
m = marks.GetNext(Current);
|
||||||
|
if (m) {
|
||||||
|
if ((m->Index() & 0x01) != 0)
|
||||||
|
m = marks.Next(m);
|
||||||
|
if (m) {
|
||||||
|
dvbApi->Goto(m->position - dvbApi->SecondsToFrames(3));
|
||||||
|
dvbApi->Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eOSState cReplayControl::ProcessKey(eKeys Key)
|
eOSState cReplayControl::ProcessKey(eKeys Key)
|
||||||
{
|
{
|
||||||
if (!dvbApi->Replaying())
|
if (!dvbApi->Replaying())
|
||||||
@ -2092,20 +2219,33 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
|
|||||||
if (visible)
|
if (visible)
|
||||||
shown = ShowProgress(!shown) || shown;
|
shown = ShowProgress(!shown) || shown;
|
||||||
switch (Key) {
|
switch (Key) {
|
||||||
|
// Positioning:
|
||||||
case kUp: dvbApi->Play(); break;
|
case kUp: dvbApi->Play(); break;
|
||||||
case kDown: dvbApi->Pause(); break;
|
case kDown: dvbApi->Pause(); break;
|
||||||
case kBlue: Hide();
|
|
||||||
dvbApi->StopReplay();
|
|
||||||
return osEnd;
|
|
||||||
case kLeft: dvbApi->Backward(); break;
|
case kLeft: dvbApi->Backward(); break;
|
||||||
case kRight: dvbApi->Forward(); break;
|
case kRight: dvbApi->Forward(); break;
|
||||||
case kLeft|k_Release:
|
case kLeft|k_Release:
|
||||||
case kRight|k_Release:
|
case kRight|k_Release:
|
||||||
dvbApi->Play(); break;
|
dvbApi->Play(); break;
|
||||||
case kGreen|k_Repeat:
|
case kGreen|k_Repeat:
|
||||||
case kGreen: dvbApi->Skip(-60); break;
|
case kGreen: dvbApi->SkipSeconds(-60); break;
|
||||||
case kYellow|k_Repeat:
|
case kYellow|k_Repeat:
|
||||||
case kYellow: dvbApi->Skip(60); break;
|
case kYellow: dvbApi->SkipSeconds(60); break;
|
||||||
|
case kBlue: Hide();
|
||||||
|
dvbApi->StopReplay();
|
||||||
|
return osEnd;
|
||||||
|
// Editing:
|
||||||
|
//XXX should we do this only when the ProgressDisplay is on???
|
||||||
|
case kMarkToggle: MarkToggle(); break;
|
||||||
|
case kMarkJumpBack: MarkJump(false); break;
|
||||||
|
case kMarkJumpForward: MarkJump(true); break;
|
||||||
|
case kMarkMoveBack|k_Repeat:
|
||||||
|
case kMarkMoveBack: MarkMove(false); break;
|
||||||
|
case kMarkMoveForward|k_Repeat:
|
||||||
|
case kMarkMoveForward: MarkMove(true); break;
|
||||||
|
case kEditCut: EditCut(); break;
|
||||||
|
case kEditTest: EditTest(); break;
|
||||||
|
// Menu control:
|
||||||
case kMenu: Hide(); return osMenu; // allow direct switching to menu
|
case kMenu: Hide(); return osMenu; // allow direct switching to menu
|
||||||
case kOk: visible ? Hide() : Show(); break;
|
case kOk: visible ? Hide() : Show(); break;
|
||||||
case kBack: return osRecordings;
|
case kBack: return osRecordings;
|
||||||
|
10
menu.h
10
menu.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: menu.h 1.15 2000/12/09 10:40:13 kls Exp $
|
* $Id: menu.h 1.16 2000/12/25 14:25:29 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _MENU_H
|
#ifndef _MENU_H
|
||||||
@ -79,12 +79,18 @@ public:
|
|||||||
class cReplayControl : public cOsdBase {
|
class cReplayControl : public cOsdBase {
|
||||||
private:
|
private:
|
||||||
cDvbApi *dvbApi;
|
cDvbApi *dvbApi;
|
||||||
bool visible, shown;
|
cMarks marks;
|
||||||
|
bool visible, shown, displayFrames;
|
||||||
void Show(void);
|
void Show(void);
|
||||||
void Hide(void);
|
void Hide(void);
|
||||||
static char *fileName;
|
static char *fileName;
|
||||||
static char *title;
|
static char *title;
|
||||||
bool ShowProgress(bool Initial);
|
bool ShowProgress(bool Initial);
|
||||||
|
void MarkToggle(void);
|
||||||
|
void MarkJump(bool Forward);
|
||||||
|
void MarkMove(bool Forward);
|
||||||
|
void EditCut(void);
|
||||||
|
void EditTest(void);
|
||||||
public:
|
public:
|
||||||
cReplayControl(void);
|
cReplayControl(void);
|
||||||
virtual ~cReplayControl();
|
virtual ~cReplayControl();
|
||||||
|
3
osd.h
3
osd.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: osd.h 1.17 2000/11/12 15:27:34 kls Exp $
|
* $Id: osd.h 1.18 2000/12/24 10:16:52 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __OSD_H
|
#ifndef __OSD_H
|
||||||
@ -29,6 +29,7 @@ enum eOSState { osUnknown,
|
|||||||
osReplay,
|
osReplay,
|
||||||
osStopRecord,
|
osStopRecord,
|
||||||
osStopReplay,
|
osStopReplay,
|
||||||
|
osCancelEdit,
|
||||||
osSwitchDvb,
|
osSwitchDvb,
|
||||||
osBack,
|
osBack,
|
||||||
osEnd,
|
osEnd,
|
||||||
|
107
recording.c
107
recording.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: recording.c 1.21 2000/11/18 16:22:29 kls Exp $
|
* $Id: recording.c 1.22 2000/12/16 14:25:14 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -26,6 +26,7 @@
|
|||||||
#define NAMEFORMAT "%s/%s/" DATAFORMAT
|
#define NAMEFORMAT "%s/%s/" DATAFORMAT
|
||||||
|
|
||||||
#define SUMMARYFILESUFFIX "/summary.vdr"
|
#define SUMMARYFILESUFFIX "/summary.vdr"
|
||||||
|
#define MARKSFILESUFFIX "/marks.vdr"
|
||||||
|
|
||||||
#define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
|
#define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
|
||||||
|
|
||||||
@ -269,3 +270,107 @@ bool cRecordings::Load(bool Deleted)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cMark -----------------------------------------------------------------
|
||||||
|
|
||||||
|
char *cMark::buffer = NULL;
|
||||||
|
|
||||||
|
cMark::cMark(int Position, const char *Comment)
|
||||||
|
{
|
||||||
|
position = Position;
|
||||||
|
comment = Comment ? strdup(Comment) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMark::~cMark()
|
||||||
|
{
|
||||||
|
delete comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cMark::ToText(void)
|
||||||
|
{
|
||||||
|
delete buffer;
|
||||||
|
asprintf(&buffer, "%s%s%s\n", IndexToHMSF(position, true), comment ? " " : "", comment ? comment : "");
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cMark::Parse(const char *s)
|
||||||
|
{
|
||||||
|
delete comment;
|
||||||
|
comment = NULL;
|
||||||
|
position = HMSFToIndex(s);
|
||||||
|
const char *p = strchr(s, ' ');
|
||||||
|
if (p) {
|
||||||
|
p = skipspace(p);
|
||||||
|
if (*p) {
|
||||||
|
comment = strdup(p);
|
||||||
|
comment[strlen(comment) - 1] = 0; // strips trailing newline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cMark::Save(FILE *f)
|
||||||
|
{
|
||||||
|
return fprintf(f, ToText()) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cMarks ----------------------------------------------------------------
|
||||||
|
|
||||||
|
bool cMarks::Load(const char *RecordingFileName)
|
||||||
|
{
|
||||||
|
const char *MarksFile = AddDirectory(RecordingFileName, MARKSFILESUFFIX);
|
||||||
|
if (cConfig::Load(MarksFile)) {
|
||||||
|
Sort();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMarks::Sort(void)
|
||||||
|
{
|
||||||
|
for (cMark *m1 = First(); m1; m1 = Next(m1)) {
|
||||||
|
for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) {
|
||||||
|
if (m2->position < m1->position) {
|
||||||
|
swap(m1->position, m2->position);
|
||||||
|
swap(m1->comment, m2->comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cMark *cMarks::Add(int Position)
|
||||||
|
{
|
||||||
|
cMark *m = Get(Position);
|
||||||
|
if (!m) {
|
||||||
|
cConfig::Add(m = new cMark(Position));
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMark *cMarks::Get(int Position)
|
||||||
|
{
|
||||||
|
for (cMark *mi = First(); mi; mi = Next(mi)) {
|
||||||
|
if (mi->position == Position)
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMark *cMarks::GetPrev(int Position)
|
||||||
|
{
|
||||||
|
for (cMark *mi = Last(); mi; mi = Prev(mi)) {
|
||||||
|
if (mi->position < Position)
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cMark *cMarks::GetNext(int Position)
|
||||||
|
{
|
||||||
|
for (cMark *mi = First(); mi; mi = Next(mi)) {
|
||||||
|
if (mi->position > Position)
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
25
recording.h
25
recording.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: recording.h 1.10 2000/10/03 12:27:49 kls Exp $
|
* $Id: recording.h 1.11 2000/12/16 14:25:20 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RECORDING_H
|
#ifndef __RECORDING_H
|
||||||
@ -47,4 +47,27 @@ public:
|
|||||||
bool Load(bool Deleted = false);
|
bool Load(bool Deleted = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class cMark : public cListObject {
|
||||||
|
private:
|
||||||
|
static char *buffer;
|
||||||
|
public:
|
||||||
|
int position;
|
||||||
|
char *comment;
|
||||||
|
cMark(int Position = 0, const char *Comment = NULL);
|
||||||
|
~cMark();
|
||||||
|
const char *ToText(void);
|
||||||
|
bool Parse(const char *s);
|
||||||
|
bool Save(FILE *f);
|
||||||
|
};
|
||||||
|
|
||||||
|
class cMarks : public cConfig<cMark> {
|
||||||
|
public:
|
||||||
|
bool Load(const char *RecordingFileName);
|
||||||
|
void Sort(void);
|
||||||
|
cMark *Add(int Position);
|
||||||
|
cMark *Get(int Position);
|
||||||
|
cMark *GetPrev(int Position);
|
||||||
|
cMark *GetNext(int Position);
|
||||||
|
};
|
||||||
|
|
||||||
#endif //__RECORDING_H
|
#endif //__RECORDING_H
|
||||||
|
3
thread.c
3
thread.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: thread.c 1.6 2000/12/03 15:35:02 kls Exp $
|
* $Id: thread.c 1.7 2000/12/24 12:27:21 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
@ -54,6 +54,7 @@ bool cThread::Start(void)
|
|||||||
running = true;
|
running = true;
|
||||||
parentPid = getpid();
|
parentPid = getpid();
|
||||||
pthread_create(&thread, NULL, (void *(*) (void *))&StartThread, (void *)this);
|
pthread_create(&thread, NULL, (void *(*) (void *))&StartThread, (void *)this);
|
||||||
|
usleep(10000); // otherwise calling Active() immediately after Start() causes a "pure virtual method called" error
|
||||||
}
|
}
|
||||||
return true; //XXX return value of pthread_create()???
|
return true; //XXX return value of pthread_create()???
|
||||||
}
|
}
|
||||||
|
6
tools.c
6
tools.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: tools.c 1.24 2000/12/03 15:39:11 kls Exp $
|
* $Id: tools.c 1.25 2000/12/24 12:38:22 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -240,10 +240,12 @@ bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
|
|||||||
if (remove(FileName) == 0)
|
if (remove(FileName) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else if (errno != ENOENT) {
|
||||||
LOG_ERROR_STR(FileName);
|
LOG_ERROR_STR(FileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cFile -----------------------------------------------------------------
|
// --- cFile -----------------------------------------------------------------
|
||||||
|
|
||||||
|
6
tools.h
6
tools.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: tools.h 1.21 2000/12/03 15:32:54 kls Exp $
|
* $Id: tools.h 1.22 2000/12/10 11:49:42 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TOOLS_H
|
#ifndef __TOOLS_H
|
||||||
@ -31,6 +31,8 @@ extern int SysLogLevel;
|
|||||||
|
|
||||||
#define DELETENULL(p) (delete (p), p = NULL)
|
#define DELETENULL(p) (delete (p), p = NULL)
|
||||||
|
|
||||||
|
template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; };
|
||||||
|
|
||||||
void writechar(int filedes, char c);
|
void writechar(int filedes, char c);
|
||||||
char *readline(FILE *f);
|
char *readline(FILE *f);
|
||||||
char *strn0cpy(char *dest, const char *src, size_t n);
|
char *strn0cpy(char *dest, const char *src, size_t n);
|
||||||
@ -98,6 +100,8 @@ template<class T> class cList : public cListBase {
|
|||||||
public:
|
public:
|
||||||
T *Get(int Index) const { return (T *)cListBase::Get(Index); }
|
T *Get(int Index) const { return (T *)cListBase::Get(Index); }
|
||||||
T *First(void) const { return (T *)objects; }
|
T *First(void) const { return (T *)objects; }
|
||||||
|
T *Last(void) const { return (T *)lastObject; }
|
||||||
|
T *Prev(const T *object) const { return (T *)object->Prev(); }
|
||||||
T *Next(const T *object) const { return (T *)object->Next(); }
|
T *Next(const T *object) const { return (T *)object->Next(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
7
vdr.c
7
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.47 2000/12/03 15:36:46 kls Exp $
|
* $Id: vdr.c 1.48 2000/12/25 09:43:08 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -306,10 +306,13 @@ int main(int argc, char *argv[])
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Menu)
|
if (!Menu) {
|
||||||
EITScanner.Process();
|
EITScanner.Process();
|
||||||
|
cVideoCutter::Active();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isyslog(LOG_INFO, "caught signal %d", Interrupted);
|
isyslog(LOG_INFO, "caught signal %d", Interrupted);
|
||||||
|
cVideoCutter::Stop();
|
||||||
delete Menu;
|
delete Menu;
|
||||||
delete ReplayControl;
|
delete ReplayControl;
|
||||||
delete Interface;
|
delete Interface;
|
||||||
|
19
videodir.c
19
videodir.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: videodir.c 1.2 2000/09/15 13:23:47 kls Exp $
|
* $Id: videodir.c 1.3 2000/12/24 12:51:41 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
@ -180,3 +180,20 @@ bool VideoFileSpaceAvailable(unsigned int SizeMB)
|
|||||||
}
|
}
|
||||||
return Dir.FreeMB() >= SizeMB;
|
return Dir.FreeMB() >= SizeMB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *PrefixVideoFileName(const char *FileName, char Prefix)
|
||||||
|
{
|
||||||
|
static char *PrefixedName = NULL;
|
||||||
|
|
||||||
|
if (!PrefixedName || strlen(PrefixedName) <= strlen(FileName))
|
||||||
|
PrefixedName = (char *)realloc(PrefixedName, strlen(FileName) + 2);
|
||||||
|
if (PrefixedName) {
|
||||||
|
strcpy(PrefixedName, VideoDirectory);
|
||||||
|
char *p = PrefixedName + strlen(PrefixedName);
|
||||||
|
*p++ = '/';
|
||||||
|
*p++ = Prefix;
|
||||||
|
strcpy(p, FileName + strlen(VideoDirectory) + 1);
|
||||||
|
}
|
||||||
|
return PrefixedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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: videodir.h 1.1 2000/07/29 14:08:27 kls Exp $
|
* $Id: videodir.h 1.2 2000/12/24 12:41:10 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __VIDEODIR_H
|
#ifndef __VIDEODIR_H
|
||||||
@ -17,5 +17,6 @@ int CloseVideoFile(int FileHandle);
|
|||||||
bool RenameVideoFile(const char *OldName, const char *NewName);
|
bool RenameVideoFile(const char *OldName, const char *NewName);
|
||||||
bool RemoveVideoFile(const char *FileName);
|
bool RemoveVideoFile(const char *FileName);
|
||||||
bool VideoFileSpaceAvailable(unsigned int SizeMB);
|
bool VideoFileSpaceAvailable(unsigned int SizeMB);
|
||||||
|
const char *PrefixVideoFileName(const char *FileName, char Prefix);
|
||||||
|
|
||||||
#endif //__VIDEODIR_H
|
#endif //__VIDEODIR_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user