mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Recording both audio tracks
This commit is contained in:
parent
4b8968f7e1
commit
9de548ee59
9
FORMATS
9
FORMATS
@ -128,3 +128,12 @@ Video Disk Recorder File Formats
|
|||||||
- marks must have a frame number, and that frame MUST be an I-frame (this
|
- 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
|
means that only marks generated by VDR itself can be used, since they
|
||||||
will always be guaranteed to mark I-frames).
|
will always be guaranteed to mark I-frames).
|
||||||
|
|
||||||
|
* 001.vdr ... 255.vdr
|
||||||
|
|
||||||
|
These are the actual recorded MPEG data files. In order to keep the size of
|
||||||
|
an individual file below a given limit, a recording is split into several
|
||||||
|
files. The contents of these files is "Packetized Elementary Stream" (PES)
|
||||||
|
and contains ES packets with ids 0xE0 for video, 0xC0 for audio 1 and 0xC1
|
||||||
|
for audio 2 (if available).
|
||||||
|
|
||||||
|
17
HISTORY
17
HISTORY
@ -510,3 +510,20 @@ Video Disk Recorder Revision History
|
|||||||
|
|
||||||
- Increased timeout until reporting "broken video data stream" when recording.
|
- Increased timeout until reporting "broken video data stream" when recording.
|
||||||
- Modified method of turning off PIDs when switching channel.
|
- Modified method of turning off PIDs when switching channel.
|
||||||
|
- Increased amount of non-useful data received by cRemux before assuming the
|
||||||
|
recording will fail.
|
||||||
|
- If there are two audio PIDs defined for a channel, both audio tracks will
|
||||||
|
now be recorded and can be selectively replayed later. See the FORMATS file
|
||||||
|
for details on how these different audio tracks are stored in the recorded
|
||||||
|
files. In order for this to work properly you need to make sure that the
|
||||||
|
StartHWFilter() function in the driver's 'dvb.c' has
|
||||||
|
|
||||||
|
u16 mode=0x0320;
|
||||||
|
|
||||||
|
instead of the default
|
||||||
|
|
||||||
|
u16 mode=0x0820;
|
||||||
|
|
||||||
|
This will create packets for the second audio track that are small enough
|
||||||
|
to multiplex smoothly with the video data.
|
||||||
|
|
||||||
|
2
MANUAL
2
MANUAL
@ -122,6 +122,8 @@ Video Disk Recorder User's Manual
|
|||||||
to toggle between these. There can be two different audio PIDs per channel,
|
to toggle between these. There can be two different audio PIDs per channel,
|
||||||
assuming that typically a channel broadcasts a country specific language
|
assuming that typically a channel broadcasts a country specific language
|
||||||
plus the movie's original soundtrack.
|
plus the movie's original soundtrack.
|
||||||
|
Recordings made form such channels will contain both audio tracks, and when
|
||||||
|
replaying the desired audio track can be selected the same way.
|
||||||
|
|
||||||
* Switching through channel groups
|
* Switching through channel groups
|
||||||
|
|
||||||
|
149
dvbapi.c
149
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.72 2001/06/14 08:19:43 kls Exp $
|
* $Id: dvbapi.c 1.73 2001/06/14 15:10:16 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
@ -451,14 +451,14 @@ protected:
|
|||||||
virtual void Input(void);
|
virtual void Input(void);
|
||||||
virtual void Output(void);
|
virtual void Output(void);
|
||||||
public:
|
public:
|
||||||
cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid);
|
cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid1, dvb_pid_t APid2);
|
||||||
virtual ~cRecordBuffer();
|
virtual ~cRecordBuffer();
|
||||||
};
|
};
|
||||||
|
|
||||||
cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid)
|
cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, dvb_pid_t VPid, dvb_pid_t APid1, dvb_pid_t APid2)
|
||||||
:cRingBuffer(VIDEOBUFSIZE, true)
|
:cRingBuffer(VIDEOBUFSIZE, true)
|
||||||
,fileName(FileName, true)
|
,fileName(FileName, true)
|
||||||
,remux(VPid, APid, true)
|
,remux(VPid, APid1, APid2, true)
|
||||||
{
|
{
|
||||||
dvbApi = DvbApi;
|
dvbApi = DvbApi;
|
||||||
index = NULL;
|
index = NULL;
|
||||||
@ -608,12 +608,14 @@ private:
|
|||||||
bool eof;
|
bool eof;
|
||||||
int blockInput, blockOutput;
|
int blockInput, blockOutput;
|
||||||
bool paused, fastForward, fastRewind;
|
bool paused, fastForward, fastRewind;
|
||||||
int lastIndex, stillIndex;
|
int lastIndex, stillIndex, playIndex;
|
||||||
|
bool canToggleAudioTrack;
|
||||||
|
uchar audioTrack;
|
||||||
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
|
||||||
void Clear(bool Block = false);
|
void Clear(bool Block = false);
|
||||||
void Close(void);
|
void Close(void);
|
||||||
int ReadFrame(uchar *b, int Length, int Max);
|
int ReadFrame(uchar *b, int Length, int Max);
|
||||||
void StripAudioPackets(uchar *b, int Length);
|
void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
|
||||||
void DisplayFrame(uchar *b, int Length);
|
void DisplayFrame(uchar *b, int Length);
|
||||||
int Resume(void);
|
int Resume(void);
|
||||||
bool Save(void);
|
bool Save(void);
|
||||||
@ -631,6 +633,8 @@ public:
|
|||||||
void SkipSeconds(int Seconds);
|
void SkipSeconds(int Seconds);
|
||||||
void Goto(int Position, bool Still = false);
|
void Goto(int Position, bool Still = false);
|
||||||
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
|
||||||
|
bool CanToggleAudioTrack(void) { return canToggleAudioTrack; }
|
||||||
|
void ToggleAudioTrack(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
|
cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
|
||||||
@ -646,7 +650,9 @@ cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const
|
|||||||
eof = false;
|
eof = false;
|
||||||
blockInput = blockOutput = false;
|
blockInput = blockOutput = false;
|
||||||
paused = fastForward = fastRewind = false;
|
paused = fastForward = fastRewind = false;
|
||||||
lastIndex = stillIndex = -1;
|
lastIndex = stillIndex = playIndex = -1;
|
||||||
|
canToggleAudioTrack = false;
|
||||||
|
audioTrack = 0xC0;
|
||||||
if (!fileName.Name())
|
if (!fileName.Name())
|
||||||
return;
|
return;
|
||||||
// Create the index file:
|
// Create the index file:
|
||||||
@ -701,12 +707,19 @@ void cReplayBuffer::Input(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lastIndex = Index;
|
lastIndex = Index;
|
||||||
|
playIndex = -1;
|
||||||
r = ReadFrame(b, Length, sizeof(b));
|
r = ReadFrame(b, Length, sizeof(b));
|
||||||
StripAudioPackets(b, Length);
|
StripAudioPackets(b, r);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lastIndex = -1;
|
lastIndex = -1;
|
||||||
r = read(replayFile, b, sizeof(b));
|
playIndex = (playIndex >= 0) ? playIndex + 1 : index->Get(fileName.Number(), fileOffset);
|
||||||
|
uchar FileNumber;
|
||||||
|
int FileOffset, Length;
|
||||||
|
if (!(index->Get(playIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
|
||||||
|
break;
|
||||||
|
r = ReadFrame(b, Length, sizeof(b));
|
||||||
|
StripAudioPackets(b, r, audioTrack);
|
||||||
}
|
}
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
uchar *p = b;
|
uchar *p = b;
|
||||||
@ -778,17 +791,20 @@ int cReplayBuffer::ReadFrame(uchar *b, int Length, int Max)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cReplayBuffer::StripAudioPackets(uchar *b, int Length)
|
void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Length - 6; i++) {
|
for (int i = 0; i < Length - 6; i++) {
|
||||||
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
|
if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
|
||||||
switch (b[i + 3]) {
|
uchar c = b[i + 3];
|
||||||
|
switch (c) {
|
||||||
case 0xC0 ... 0xDF: // audio
|
case 0xC0 ... 0xDF: // audio
|
||||||
{
|
if (c == 0xC1)
|
||||||
int n = b[i + 4] * 256 + b[i + 5];
|
canToggleAudioTrack = true;
|
||||||
for (int j = i; j < Length && n--; j++)
|
if (!Except || c != Except) {
|
||||||
b[j] = 0x00;
|
int n = b[i + 4] * 256 + b[i + 5];
|
||||||
}
|
for (int j = i; j < Length && n--; j++)
|
||||||
|
b[j] = 0x00;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0xE0 ... 0xEF: // video
|
case 0xE0 ... 0xEF: // video
|
||||||
i += b[i + 4] * 256 + b[i + 5];
|
i += b[i + 4] * 256 + b[i + 5];
|
||||||
@ -816,6 +832,7 @@ void cReplayBuffer::Clear(bool Block)
|
|||||||
usleep(1);
|
usleep(1);
|
||||||
Lock();
|
Lock();
|
||||||
cRingBuffer::Clear();
|
cRingBuffer::Clear();
|
||||||
|
playIndex = -1;
|
||||||
CHECK(ioctl(videoDev, VIDEO_FREEZE));
|
CHECK(ioctl(videoDev, VIDEO_FREEZE));
|
||||||
CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
|
CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
|
||||||
CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
|
CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
|
||||||
@ -969,6 +986,7 @@ void cReplayBuffer::Goto(int Index, bool Still)
|
|||||||
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
|
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
|
||||||
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
|
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
|
||||||
stillIndex = Index;
|
stillIndex = Index;
|
||||||
|
playIndex = -1;
|
||||||
uchar b[MAXFRAMESIZE];
|
uchar b[MAXFRAMESIZE];
|
||||||
int r = ReadFrame(b, Length, sizeof(b));
|
int r = ReadFrame(b, Length, sizeof(b));
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
@ -977,7 +995,7 @@ void cReplayBuffer::Goto(int Index, bool Still)
|
|||||||
paused = true;
|
paused = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stillIndex = -1;
|
stillIndex = playIndex = -1;
|
||||||
Clear(false);
|
Clear(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1015,6 +1033,14 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
|
|||||||
return replayFile >= 0;
|
return replayFile >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cReplayBuffer::ToggleAudioTrack(void)
|
||||||
|
{
|
||||||
|
if (CanToggleAudioTrack()) {
|
||||||
|
audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- cTransferBuffer -------------------------------------------------------
|
// --- cTransferBuffer -------------------------------------------------------
|
||||||
|
|
||||||
class cTransferBuffer : public cRingBuffer {
|
class cTransferBuffer : public cRingBuffer {
|
||||||
@ -1329,33 +1355,34 @@ cDvbApi::cDvbApi(int n)
|
|||||||
|
|
||||||
// Devices that are only present on DVB-C or DVB-S cards:
|
// Devices that are only present on DVB-C or DVB-S cards:
|
||||||
|
|
||||||
fd_qamfe = OstOpen(DEV_OST_QAMFE, n, O_RDWR);
|
fd_qamfe = OstOpen(DEV_OST_QAMFE, n, O_RDWR);
|
||||||
fd_qpskfe = OstOpen(DEV_OST_QPSKFE, n, O_RDWR);
|
fd_qpskfe = OstOpen(DEV_OST_QPSKFE, n, O_RDWR);
|
||||||
fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
|
fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
|
||||||
|
|
||||||
// Devices that all DVB cards must have:
|
// Devices that all DVB cards must have:
|
||||||
|
|
||||||
fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
||||||
fd_demuxa = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
fd_demuxa1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
||||||
fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
fd_demuxa2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
||||||
|
fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
|
||||||
|
|
||||||
// Devices not present on "budget" cards:
|
// Devices not present on "budget" cards:
|
||||||
|
|
||||||
fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
|
fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
|
||||||
fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
|
fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
|
||||||
fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
|
fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
|
||||||
|
|
||||||
// Devices that may not be available, and are not necessary for normal operation:
|
// Devices that may not be available, and are not necessary for normal operation:
|
||||||
|
|
||||||
videoDev = OstOpen(DEV_VIDEO, n, O_RDWR);
|
videoDev = OstOpen(DEV_VIDEO, n, O_RDWR);
|
||||||
|
|
||||||
// Devices that will be dynamically opened and closed when necessary:
|
// Devices that will be dynamically opened and closed when necessary:
|
||||||
|
|
||||||
fd_dvr = -1;
|
fd_dvr = -1;
|
||||||
|
|
||||||
// We only check the devices that must be present - the others will be checked before accessing them:
|
// We only check the devices that must be present - the others will be checked before accessing them:
|
||||||
|
|
||||||
if (((fd_qpskfe >= 0 && fd_sec >= 0) || fd_qamfe >= 0) && fd_demuxv >= 0 && fd_demuxa >= 0 && fd_demuxt >= 0) {
|
if (((fd_qpskfe >= 0 && fd_sec >= 0) || fd_qamfe >= 0) && fd_demuxv >= 0 && fd_demuxa1 >= 0 && fd_demuxa2 >= 0 && fd_demuxt >= 0) {
|
||||||
siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
|
siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
|
||||||
if (!dvbApi[0]) // only the first one shall set the system time
|
if (!dvbApi[0]) // only the first one shall set the system time
|
||||||
siProcessor->SetUseTSTime(Setup.SetSystemTime);
|
siProcessor->SetUseTSTime(Setup.SetSystemTime);
|
||||||
@ -2026,7 +2053,8 @@ bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, dvb_pid_t Pid, dmxOutput_t Ou
|
|||||||
bool cDvbApi::SetPids(bool ForRecording)
|
bool cDvbApi::SetPids(bool ForRecording)
|
||||||
{
|
{
|
||||||
return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
|
return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
|
||||||
SetApid(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER);
|
SetApid1(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
|
||||||
|
SetApid2(ForRecording ? aPid2 : 0, DMX_OUT_TS_TAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Tpid, int Ca, int Pnr)
|
bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Tpid, int Ca, int Pnr)
|
||||||
@ -2048,9 +2076,10 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
|
|||||||
|
|
||||||
// Turn off current PIDs:
|
// Turn off current PIDs:
|
||||||
|
|
||||||
SetVpid(0, DMX_OUT_DECODER);
|
SetVpid( 0, DMX_OUT_DECODER);
|
||||||
SetApid(0, DMX_OUT_DECODER);
|
SetApid1(0, DMX_OUT_DECODER);
|
||||||
SetTpid(0, DMX_OUT_DECODER);
|
SetApid2(0, DMX_OUT_DECODER);
|
||||||
|
SetTpid( 0, DMX_OUT_DECODER);
|
||||||
|
|
||||||
bool ChannelSynced = false;
|
bool ChannelSynced = false;
|
||||||
|
|
||||||
@ -2110,7 +2139,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
|
|||||||
esyslog(LOG_ERR, "ERROR %d in qpsk get event", res);
|
esyslog(LOG_ERR, "ERROR %d in qpsk get event", res);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fprintf(stderr, "ERROR: timeout while tuning\n");
|
esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
|
||||||
}
|
}
|
||||||
else if (fd_qamfe >= 0) { // DVB-C
|
else if (fd_qamfe >= 0) { // DVB-C
|
||||||
|
|
||||||
@ -2137,7 +2166,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
|
|||||||
esyslog(LOG_ERR, "ERROR %d in qam get event", res);
|
esyslog(LOG_ERR, "ERROR %d in qam get event", res);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fprintf(stderr, "ERROR: timeout while tuning\n");
|
esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device");
|
esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device");
|
||||||
@ -2187,28 +2216,6 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cDvbApi::CanToggleAudioPid(void)
|
|
||||||
{
|
|
||||||
return aPid1 && aPid2 && aPid1 != aPid2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cDvbApi::ToggleAudioPid(void)
|
|
||||||
{
|
|
||||||
if (CanToggleAudioPid()) {
|
|
||||||
int a = aPid2;
|
|
||||||
aPid2 = aPid1;
|
|
||||||
aPid1 = a;
|
|
||||||
if (transferringFromDvbApi)
|
|
||||||
return transferringFromDvbApi->ToggleAudioPid();
|
|
||||||
else {
|
|
||||||
if (transferBuffer)
|
|
||||||
transferBuffer->SetAudioPid(aPid1);
|
|
||||||
return SetPids(transferBuffer != NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cDvbApi::Transferring(void)
|
bool cDvbApi::Transferring(void)
|
||||||
{
|
{
|
||||||
return transferBuffer;
|
return transferBuffer;
|
||||||
@ -2278,7 +2285,7 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
|
|||||||
|
|
||||||
// Create recording buffer:
|
// Create recording buffer:
|
||||||
|
|
||||||
recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1);
|
recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2);
|
||||||
|
|
||||||
if (recordBuffer) {
|
if (recordBuffer) {
|
||||||
ca = Ca;
|
ca = Ca;
|
||||||
@ -2390,6 +2397,32 @@ void cDvbApi::Goto(int Position, bool Still)
|
|||||||
replayBuffer->Goto(Position, Still);
|
replayBuffer->Goto(Position, Still);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cDvbApi::CanToggleAudioTrack(void)
|
||||||
|
{
|
||||||
|
return replayBuffer ? replayBuffer->CanToggleAudioTrack() : (aPid1 && aPid2 && aPid1 != aPid2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cDvbApi::ToggleAudioTrack(void)
|
||||||
|
{
|
||||||
|
if (replayBuffer) {
|
||||||
|
replayBuffer->ToggleAudioTrack();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int a = aPid2;
|
||||||
|
aPid2 = aPid1;
|
||||||
|
aPid1 = a;
|
||||||
|
if (transferringFromDvbApi)
|
||||||
|
return transferringFromDvbApi->ToggleAudioTrack();
|
||||||
|
else {
|
||||||
|
if (transferBuffer)
|
||||||
|
transferBuffer->SetAudioPid(aPid1);
|
||||||
|
return SetPids(transferBuffer != NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cEITScanner -----------------------------------------------------------
|
// --- cEITScanner -----------------------------------------------------------
|
||||||
|
|
||||||
cEITScanner::cEITScanner(void)
|
cEITScanner::cEITScanner(void)
|
||||||
|
17
dvbapi.h
17
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.37 2001/06/03 11:51:30 kls Exp $
|
* $Id: dvbapi.h 1.38 2001/06/14 14:54:25 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __DVBAPI_H
|
#ifndef __DVBAPI_H
|
||||||
@ -66,11 +66,12 @@ class cDvbApi {
|
|||||||
friend class cTransferBuffer;
|
friend class cTransferBuffer;
|
||||||
private:
|
private:
|
||||||
int videoDev;
|
int videoDev;
|
||||||
int fd_osd, fd_qpskfe, fd_qamfe, fd_sec, fd_dvr, fd_audio, fd_video, fd_demuxa, fd_demuxv, fd_demuxt;
|
int fd_osd, fd_qpskfe, fd_qamfe, fd_sec, fd_dvr, fd_audio, fd_video, fd_demuxa1, fd_demuxa2, fd_demuxv, fd_demuxt;
|
||||||
int vPid, aPid1, aPid2;
|
int vPid, aPid1, aPid2;
|
||||||
bool SetPid(int fd, dmxPesType_t PesType, dvb_pid_t Pid, dmxOutput_t Output);
|
bool SetPid(int fd, dmxPesType_t PesType, dvb_pid_t Pid, dmxOutput_t Output);
|
||||||
bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); }
|
bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); }
|
||||||
bool SetApid(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa, DMX_PES_AUDIO, Apid, Output); }
|
bool SetApid1(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa1, DMX_PES_AUDIO, Apid, Output); }
|
||||||
|
bool SetApid2(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa2, DMX_PES_OTHER, Apid, Output); }
|
||||||
bool SetTpid(int Tpid, dmxOutput_t Output) { return SetPid(fd_demuxt, DMX_PES_TELETEXT, Tpid, Output); }
|
bool SetTpid(int Tpid, dmxOutput_t Output) { return SetPid(fd_demuxt, DMX_PES_TELETEXT, Tpid, Output); }
|
||||||
bool SetPids(bool ForRecording);
|
bool SetPids(bool ForRecording);
|
||||||
cDvbApi(int n);
|
cDvbApi(int n);
|
||||||
@ -180,8 +181,6 @@ public:
|
|||||||
bool SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Tpid, int Ca, int Pnr);
|
bool SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Tpid, int Ca, int Pnr);
|
||||||
static int CurrentChannel(void) { return PrimaryDvbApi ? PrimaryDvbApi->currentChannel : 0; }
|
static int CurrentChannel(void) { return PrimaryDvbApi ? PrimaryDvbApi->currentChannel : 0; }
|
||||||
int Channel(void) { return currentChannel; }
|
int Channel(void) { return currentChannel; }
|
||||||
bool CanToggleAudioPid(void);
|
|
||||||
bool ToggleAudioPid(void);
|
|
||||||
|
|
||||||
// Transfer facilities
|
// Transfer facilities
|
||||||
|
|
||||||
@ -262,6 +261,14 @@ public:
|
|||||||
void Goto(int Index, bool Still = false);
|
void Goto(int Index, bool Still = false);
|
||||||
// Positions to the given index and displays that frame as a still picture
|
// Positions to the given index and displays that frame as a still picture
|
||||||
// if Still is true.
|
// if Still is true.
|
||||||
|
|
||||||
|
// Audio track facilities
|
||||||
|
|
||||||
|
bool CanToggleAudioTrack(void);
|
||||||
|
// Returns true if we are currently replaying and this recording has two
|
||||||
|
// audio tracks, or if the current channel has two audio PIDs.
|
||||||
|
bool ToggleAudioTrack(void);
|
||||||
|
// Toggles the audio track if possible.
|
||||||
};
|
};
|
||||||
|
|
||||||
class cEITScanner {
|
class cEITScanner {
|
||||||
|
8
menu.c
8
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.72 2001/06/02 13:51:28 kls Exp $
|
* $Id: menu.c 1.73 2001/06/14 14:55:16 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
@ -1726,7 +1726,7 @@ cMenuMain::cMenuMain(bool Replaying)
|
|||||||
}
|
}
|
||||||
if (cVideoCutter::Active())
|
if (cVideoCutter::Active())
|
||||||
Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
|
Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
|
||||||
SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioPid() ? tr("Language") : NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
|
SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
|
||||||
Display();
|
Display();
|
||||||
lastActivity = time(NULL);
|
lastActivity = time(NULL);
|
||||||
SetHasHotkeys();
|
SetHasHotkeys();
|
||||||
@ -1761,9 +1761,9 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
|||||||
case kRed: if (!HasSubMenu())
|
case kRed: if (!HasSubMenu())
|
||||||
state = osRecord;
|
state = osRecord;
|
||||||
break;
|
break;
|
||||||
case kGreen: if (cDvbApi::PrimaryDvbApi->CanToggleAudioPid()) {
|
case kGreen: if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) {
|
||||||
Interface->Clear();
|
Interface->Clear();
|
||||||
cDvbApi::PrimaryDvbApi->ToggleAudioPid();
|
cDvbApi::PrimaryDvbApi->ToggleAudioTrack();
|
||||||
state = osEnd;
|
state = osEnd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
49
remux.c
49
remux.c
@ -8,7 +8,7 @@
|
|||||||
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
|
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
|
||||||
* VDR's needs.
|
* VDR's needs.
|
||||||
*
|
*
|
||||||
* $Id: remux.c 1.3 2001/06/02 15:39:16 kls Exp $
|
* $Id: remux.c 1.4 2001/06/14 15:30:09 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The calling interface of the 'cRemux::Process()' function is defined
|
/* The calling interface of the 'cRemux::Process()' function is defined
|
||||||
@ -107,6 +107,11 @@
|
|||||||
|
|
||||||
#define IPACKS 2048
|
#define IPACKS 2048
|
||||||
|
|
||||||
|
// Start codes:
|
||||||
|
#define SC_PICTURE 0x00 // "picture header"
|
||||||
|
|
||||||
|
#define MAXNONUSEFULDATA (10*1024*1024)
|
||||||
|
|
||||||
class cTS2PES {
|
class cTS2PES {
|
||||||
private:
|
private:
|
||||||
int size;
|
int size;
|
||||||
@ -114,6 +119,7 @@ private:
|
|||||||
int count;
|
int count;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
uint8_t cid;
|
uint8_t cid;
|
||||||
|
uint8_t audioCid;
|
||||||
int plength;
|
int plength;
|
||||||
uint8_t plen[2];
|
uint8_t plen[2];
|
||||||
uint8_t flag1;
|
uint8_t flag1;
|
||||||
@ -132,7 +138,7 @@ private:
|
|||||||
void write_ipack(const uint8_t *Data, int Count);
|
void write_ipack(const uint8_t *Data, int Count);
|
||||||
void instant_repack(const uint8_t *Buf, int Count);
|
void instant_repack(const uint8_t *Buf, int Count);
|
||||||
public:
|
public:
|
||||||
cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size);
|
cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid = 0x00);
|
||||||
~cTS2PES();
|
~cTS2PES();
|
||||||
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
|
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
|
||||||
void Clear(void);
|
void Clear(void);
|
||||||
@ -140,11 +146,12 @@ public:
|
|||||||
|
|
||||||
uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
|
uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
|
||||||
|
|
||||||
cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size)
|
cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid)
|
||||||
{
|
{
|
||||||
resultBuffer = ResultBuffer;
|
resultBuffer = ResultBuffer;
|
||||||
resultCount = ResultCount;
|
resultCount = ResultCount;
|
||||||
size = Size;
|
size = Size;
|
||||||
|
audioCid = AudioCid;
|
||||||
|
|
||||||
if (!(buf = new uint8_t[size]))
|
if (!(buf = new uint8_t[size]))
|
||||||
esyslog(LOG_ERR, "Not enough memory for ts_transform");
|
esyslog(LOG_ERR, "Not enough memory for ts_transform");
|
||||||
@ -164,7 +171,10 @@ void cTS2PES::Clear(void)
|
|||||||
|
|
||||||
void cTS2PES::store(uint8_t *Data, int Count)
|
void cTS2PES::store(uint8_t *Data, int Count)
|
||||||
{
|
{
|
||||||
//XXX overflow check???
|
if (*resultCount + Count > RESULTBUFFERSIZE) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE);
|
||||||
|
Count = RESULTBUFFERSIZE - *resultCount;
|
||||||
|
}
|
||||||
memcpy(resultBuffer + *resultCount, Data, Count);
|
memcpy(resultBuffer + *resultCount, Data, Count);
|
||||||
*resultCount += Count;
|
*resultCount += Count;
|
||||||
}
|
}
|
||||||
@ -188,7 +198,7 @@ void cTS2PES::send_ipack(void)
|
|||||||
{
|
{
|
||||||
if (count < 10)
|
if (count < 10)
|
||||||
return;
|
return;
|
||||||
buf[3] = cid;
|
buf[3] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : cid;
|
||||||
buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
|
buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
|
||||||
buf[5] = (uint8_t)((count - 6) & 0x00FF);
|
buf[5] = (uint8_t)((count - 6) & 0x00FF);
|
||||||
store(buf, count);
|
store(buf, count);
|
||||||
@ -409,22 +419,25 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
|
|||||||
|
|
||||||
// --- cRemux ----------------------------------------------------------------
|
// --- cRemux ----------------------------------------------------------------
|
||||||
|
|
||||||
cRemux::cRemux(dvb_pid_t VPid, dvb_pid_t APid, bool ExitOnFailure)
|
cRemux::cRemux(dvb_pid_t VPid, dvb_pid_t APid1, dvb_pid_t APid2, bool ExitOnFailure)
|
||||||
{
|
{
|
||||||
vPid = VPid;
|
vPid = VPid;
|
||||||
aPid = APid;
|
aPid1 = APid1;
|
||||||
|
aPid2 = APid2;
|
||||||
exitOnFailure = ExitOnFailure;
|
exitOnFailure = ExitOnFailure;
|
||||||
synced = false;
|
synced = false;
|
||||||
skipped = 0;
|
skipped = 0;
|
||||||
resultCount = resultDelivered = 0;
|
resultCount = resultDelivered = 0;
|
||||||
vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
|
vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
|
||||||
aTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
|
aTS2PES1 = new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0);
|
||||||
|
aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cRemux::~cRemux()
|
cRemux::~cRemux()
|
||||||
{
|
{
|
||||||
delete vTS2PES;
|
delete vTS2PES;
|
||||||
delete aTS2PES;
|
delete aTS2PES1;
|
||||||
|
delete aTS2PES2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRemux::GetPid(const uchar *Data)
|
int cRemux::GetPid(const uchar *Data)
|
||||||
@ -463,9 +476,9 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
|
|||||||
|
|
||||||
void cRemux::SetAudioPid(int APid)
|
void cRemux::SetAudioPid(int APid)
|
||||||
{
|
{
|
||||||
aPid = APid;
|
aPid1 = APid;
|
||||||
vTS2PES->Clear();
|
vTS2PES->Clear();
|
||||||
aTS2PES->Clear();
|
aTS2PES1->Clear();
|
||||||
resultCount = resultDelivered = 0;
|
resultCount = resultDelivered = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,8 +514,10 @@ XXX*/
|
|||||||
if (Data[i + 3] & 0x10) { // got payload
|
if (Data[i + 3] & 0x10) { // got payload
|
||||||
if (pid == vPid)
|
if (pid == vPid)
|
||||||
vTS2PES->ts_to_pes(Data + i);
|
vTS2PES->ts_to_pes(Data + i);
|
||||||
else if (pid == aPid)
|
else if (pid == aPid1)
|
||||||
aTS2PES->ts_to_pes(Data + i);
|
aTS2PES1->ts_to_pes(Data + i);
|
||||||
|
else if (pid == aPid2 && aTS2PES2)
|
||||||
|
aTS2PES2->ts_to_pes(Data + i);
|
||||||
}
|
}
|
||||||
used += TS_SIZE;
|
used += TS_SIZE;
|
||||||
if (resultCount > (int)sizeof(resultBuffer) / 2)
|
if (resultCount > (int)sizeof(resultBuffer) / 2)
|
||||||
@ -520,7 +535,7 @@ XXX*/
|
|||||||
// Check if we're getting anywhere here:
|
// Check if we're getting anywhere here:
|
||||||
|
|
||||||
if (!synced && skipped >= 0) {
|
if (!synced && skipped >= 0) {
|
||||||
if (skipped > 1024*1024) {
|
if (skipped > MAXNONUSEFULDATA) {
|
||||||
esyslog(LOG_ERR, "ERROR: no useful data seen within %d byte of video stream", skipped);
|
esyslog(LOG_ERR, "ERROR: no useful data seen within %d byte of video stream", skipped);
|
||||||
skipped = -1;
|
skipped = -1;
|
||||||
if (exitOnFailure)
|
if (exitOnFailure)
|
||||||
@ -538,7 +553,7 @@ XXX*/
|
|||||||
for (int i = 0; i < resultCount; i++) {
|
for (int i = 0; i < resultCount; i++) {
|
||||||
if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
|
if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
|
||||||
switch (resultBuffer[i + 3]) {
|
switch (resultBuffer[i + 3]) {
|
||||||
case SC_VIDEO:
|
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
|
||||||
{
|
{
|
||||||
uchar pt = NO_PICTURE;
|
uchar pt = NO_PICTURE;
|
||||||
int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
|
int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
|
||||||
@ -572,7 +587,7 @@ XXX*/
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SC_AUDIO:
|
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
|
||||||
{
|
{
|
||||||
int l = GetPacketLength(resultBuffer, resultCount, i);
|
int l = GetPacketLength(resultBuffer, resultCount, i);
|
||||||
if (l < 0)
|
if (l < 0)
|
||||||
|
21
remux.h
21
remux.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: remux.h 1.3 2001/06/02 15:15:43 kls Exp $
|
* $Id: remux.h 1.4 2001/06/14 15:27:07 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __REMUX_H
|
#ifndef __REMUX_H
|
||||||
@ -19,18 +19,11 @@
|
|||||||
#define P_FRAME 2
|
#define P_FRAME 2
|
||||||
#define B_FRAME 3
|
#define B_FRAME 3
|
||||||
|
|
||||||
//XXX -> remux.c???
|
|
||||||
// Start codes:
|
|
||||||
#define SC_PICTURE 0x00 // "picture header"
|
|
||||||
#define SC_SEQU 0xB3 // "sequence header"
|
|
||||||
#define SC_PHEAD 0xBA // "pack header"
|
|
||||||
#define SC_SHEAD 0xBB // "system header"
|
|
||||||
#define SC_AUDIO 0xC0
|
|
||||||
#define SC_VIDEO 0xE0
|
|
||||||
|
|
||||||
// The minimum amount of video data necessary to identify frames:
|
// The minimum amount of video data necessary to identify frames:
|
||||||
#define MINVIDEODATA (16*1024) // just a safe guess (max. size of any frame block, plus some safety)
|
#define MINVIDEODATA (16*1024) // just a safe guess (max. size of any frame block, plus some safety)
|
||||||
|
|
||||||
|
#define RESULTBUFFERSIZE (MINVIDEODATA * 4)
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
typedef unsigned char uchar;
|
||||||
class cTS2PES;
|
class cTS2PES;
|
||||||
|
|
||||||
@ -39,16 +32,16 @@ private:
|
|||||||
bool exitOnFailure;
|
bool exitOnFailure;
|
||||||
bool synced;
|
bool synced;
|
||||||
int skipped;
|
int skipped;
|
||||||
dvb_pid_t vPid, aPid;
|
dvb_pid_t vPid, aPid1, aPid2;
|
||||||
cTS2PES *vTS2PES, *aTS2PES;
|
cTS2PES *vTS2PES, *aTS2PES1, *aTS2PES2;
|
||||||
uchar resultBuffer[MINVIDEODATA * 4];//XXX
|
uchar resultBuffer[RESULTBUFFERSIZE];
|
||||||
int resultCount;
|
int resultCount;
|
||||||
int resultDelivered;
|
int resultDelivered;
|
||||||
int GetPid(const uchar *Data);
|
int GetPid(const uchar *Data);
|
||||||
int GetPacketLength(const uchar *Data, int Count, int Offset);
|
int GetPacketLength(const uchar *Data, int Count, int Offset);
|
||||||
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
|
||||||
public:
|
public:
|
||||||
cRemux(dvb_pid_t VPid, dvb_pid_t APid, bool ExitOnFailure = false);
|
cRemux(dvb_pid_t VPid, dvb_pid_t APid1, dvb_pid_t APid2 = 0, bool ExitOnFailure = false);
|
||||||
~cRemux();
|
~cRemux();
|
||||||
void SetAudioPid(int APid);
|
void SetAudioPid(int APid);
|
||||||
const uchar *Process(const uchar *Data, int &Count, int &Result, uchar *PictureType = NULL);
|
const uchar *Process(const uchar *Data, int &Count, int &Result, uchar *PictureType = NULL);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user