1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented 'on disk editing'

This commit is contained in:
Klaus Schmidinger 2000-12-28 12:57:16 +01:00
parent be137ee37f
commit 4e354bc9a0
21 changed files with 1028 additions and 308 deletions

23
FORMATS
View File

@ -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).

View File

@ -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".

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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.

View File

@ -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,33 +167,36 @@ 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);
FILE *f = fopen(fileName, "r"); bool result = false;
if (f) { if (access(FileName, F_OK) == 0) {
int line = 0; isyslog(LOG_INFO, "loading %s", FileName);
char buffer[MaxBuffer]; FILE *f = fopen(fileName, "r");
while (fgets(buffer, sizeof(buffer), f) > 0) { if (f) {
line++; int line = 0;
T *l = new T; char buffer[MaxBuffer];
if (l->Parse(buffer)) result = true;
Add(l); while (fgets(buffer, sizeof(buffer), f) > 0) {
else { line++;
esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line); T *l = new T;
delete l; if (l->Parse(buffer))
result = false; Add(l);
break; else {
esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line);
delete l;
result = false;
break;
}
} }
} fclose(f);
fclose(f); }
} else
else { LOG_ERROR_STR(fileName);
LOG_ERROR_STR(fileName);
result = false;
} }
return result; return result;
} }

755
dvbapi.c

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -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
View File

@ -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,9 +2053,11 @@ 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
View File

@ -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
View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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()???
} }

View File

@ -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,9 +240,11 @@ 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 -----------------------------------------------------------------

View File

@ -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
View File

@ -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;

View File

@ -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;
}

View File

@ -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