Working PlayTsXXX to PlayXXX wrapper
Commit Message
Hi list,
Attached is a new version of cDevice::PlayTsVideo and
cDevice::PlayTsAudio that properly handles partially accepted buffers of
the PlayVideo and PlayAudio functions. The original functions would
discard any partially written data.
These two functions are only used by devices that do not implement their
own PlayTsXXX functions. The FF DVB devices don't use them any more,
since TS is passed to (patched) DVB drivers. Other devices will soon
play TS directly too.
However, it might still be useful for patches that restore support for
older drivers. ;)
Cheers,
Udo
Comments
On 25.01.2009 23:53, Udo Richter wrote:
> Hi list,
>
>
> Attached is a new version of cDevice::PlayTsVideo and
> cDevice::PlayTsAudio that properly handles partially accepted buffers of
> the PlayVideo and PlayAudio functions. The original functions would
> discard any partially written data.
virtual int PlayVideo(const uchar *Data, int Length);
///< ...
///< PlayVideo() shall process the packet either as a whole (returning
///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
///< ...
virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
///< ...
///< PlayAudio() shall process the packet either as a whole (returning
///< Length) or not at all (returning 0 or -1 and setting 'errno' accordingly).
///< ...
By definition these two functions shall write "all or nothing".
So the higher level functions needn't handle any partially written data.
Klaus
On 26.01.2009 08:39, Klaus Schmidinger wrote:
> On 25.01.2009 23:53, Udo Richter wrote:
>> Attached is a new version of cDevice::PlayTsVideo and
>> cDevice::PlayTsAudio that properly handles partially accepted buffers of
>> the PlayVideo and PlayAudio functions. The original functions would
>> discard any partially written data.
>
> By definition these two functions shall write "all or nothing".
> So the higher level functions needn't handle any partially written data.
But in fact they write all or nothing or timeout after one second, in
which case they return how much was written. And it seems as if these
timeouts do happen.
If you change that behavior so that they never timeout (like in VDR
1.7.2/1.7.3), then PES playback frequently hangs for fractions of a
second for me.
And with the timeout and no partial buffer handling (as in VDR 1.7.4),
any TS playback frequently drops the remains of a buffer, causing
massive picture breakdown.
Cheers,
Udo
On 26.01.2009 12:10, Udo Richter wrote:
> On 26.01.2009 08:39, Klaus Schmidinger wrote:
>> On 25.01.2009 23:53, Udo Richter wrote:
>>> Attached is a new version of cDevice::PlayTsVideo and
>>> cDevice::PlayTsAudio that properly handles partially accepted buffers of
>>> the PlayVideo and PlayAudio functions. The original functions would
>>> discard any partially written data.
>> By definition these two functions shall write "all or nothing".
>> So the higher level functions needn't handle any partially written data.
>
> But in fact they write all or nothing or timeout after one second, in
> which case they return how much was written. And it seems as if these
> timeouts do happen.
I did some more investigating, and things get really strange...
First, there's no evidence for the 1000ms timeout to actually happen.
Data in fact seems to be written 'all or nothing' in reality. Both
cases, all and nothing, do happen.
The PlayTS functions however are still wrong, since they do not handle
the case where -1/EAGAIN is returned immediately. The PES packet was
returned by GetPes and will not be returned again, so this PES packet
will be dropped.
I also experimented with the VDR-1.7.3 solution again:
do {
w = WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
if (w < 0 && errno == EAGAIN) {
cPoller Poller(fd_video, true);
Poller.Poll(200);
}
} while (w != Length);
WriteAllOrNothing returns either Length or -1/EAGAIN. The poller sleeps
for about 0-20ms each time. CPU load isn't too high either. Still I have
an ARD recording that reproducibly hangs at the same point every time
for a fraction of a second, and only if I use the above loop. This
happens 1-2 times per minute, and usually causes heavy audio/video
desyncing.
The VDR-1.7.1/VDR-1.7.4 version does not show this hang:
return WriteAllOrNothing(fd_video, Data, Length, 1000, 10);
Anyone has an idea what really causes this?
Cheers,
Udo
===================================================================
@@ -89,6 +89,10 @@
liveSubtitle = NULL;
dvbSubtitleConverter = NULL;
autoSelectPreferredSubtitleLanguage = true;
+ tsToPesVideoBuffer = NULL;
+ tsToPesVideoLength = 0;
+ tsToPesAudioBuffer = NULL;
+ tsToPesAudioLength = 0;
for (int i = 0; i < MAXRECEIVERS; i++)
receiver[i] = NULL;
@@ -1270,14 +1274,33 @@
int cDevice::PlayTsVideo(const uchar *Data, int Length)
{
+ // Play remains of last buffer if not completely played:
+ while (tsToPesVideoBuffer && tsToPesVideoLength > 0) {
+ int w = PlayVideo(tsToPesVideoBuffer, tsToPesVideoLength);
+ if (w <= 0)
+ return w;
+ tsToPesVideoBuffer += w;
+ tsToPesVideoLength -= w;
+ if (tsToPesVideoLength > 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+ tsToPesVideoBuffer = tsToPesVideo.GetPes(tsToPesVideoLength);
+ }
+
// Video PES has no explicit length, so we can only determine the end of
// a PES packet when the next TS packet that starts a payload comes in:
if (TsPayloadStart(Data)) {
- int l;
- while (const uchar *p = tsToPesVideo.GetPes(l)) {
- int w = PlayVideo(p, l);
+ while ((tsToPesVideoBuffer = tsToPesVideo.GetPes(tsToPesVideoLength))) {
+ int w = PlayVideo(tsToPesVideoBuffer, tsToPesVideoLength);
if (w <= 0)
return w;
+ tsToPesVideoBuffer += w;
+ tsToPesVideoLength -= w;
+ if (tsToPesVideoLength > 0) {
+ errno = EAGAIN;
+ return -1;
+ }
}
tsToPesVideo.Reset();
}
@@ -1287,12 +1310,31 @@
int cDevice::PlayTsAudio(const uchar *Data, int Length)
{
+ // Play remains of last buffer if not completely played:
+ if (tsToPesAudioBuffer && tsToPesAudioLength > 0) {
+ int w = PlayAudio(tsToPesAudioBuffer, tsToPesAudioLength, 0);
+ if (w <= 0)
+ return w;
+ tsToPesAudioBuffer += w;
+ tsToPesAudioLength -= w;
+ if (tsToPesAudioLength > 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+ tsToPesAudio.Reset();
+ }
+
// Audio PES always has an explicit length and consists of single packets:
- int l;
- if (const uchar *p = tsToPesAudio.GetPes(l)) {
- int w = PlayAudio(p, l, 0);
+ if ((tsToPesAudioBuffer = tsToPesAudio.GetPes(tsToPesAudioLength))) {
+ int w = PlayAudio(tsToPesAudioBuffer, tsToPesAudioLength, 0);
if (w <= 0)
return w;
+ tsToPesAudioBuffer += w;
+ tsToPesAudioLength -= w;
+ if (tsToPesAudioLength > 0) {
+ errno = EAGAIN;
+ return -1;
+ }
tsToPesAudio.Reset();
}
tsToPesAudio.PutTs(Data, Length);
@@ -1347,7 +1389,11 @@
else if (Data == NULL) {
patPmtParser.Reset();
tsToPesVideo.Reset();
+ tsToPesVideoBuffer = NULL;
+ tsToPesVideoLength = 0;
tsToPesAudio.Reset();
+ tsToPesAudioBuffer = NULL;
+ tsToPesAudioLength = 0;
tsToPesSubtitle.Reset();
}
return -1;
===================================================================
@@ -475,7 +475,11 @@
cPlayer *player;
cPatPmtParser patPmtParser;
cTsToPes tsToPesVideo;
+ const uchar *tsToPesVideoBuffer;
+ int tsToPesVideoLength;
cTsToPes tsToPesAudio;
+ const uchar *tsToPesAudioBuffer;
+ int tsToPesAudioLength;
cTsToPes tsToPesSubtitle;
bool isPlayingVideo;
protected: