Working PlayTsXXX to PlayXXX wrapper

Message ID 497CED72.8050006@gmx.de
State New
Headers

Commit Message

Udo Richter Jan. 25, 2009, 10:53 p.m. UTC
  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

Klaus Schmidinger Jan. 26, 2009, 7:39 a.m. UTC | #1
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
  
Udo Richter Jan. 26, 2009, 11:10 a.m. UTC | #2
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
  
Udo Richter Jan. 26, 2009, 12:30 p.m. UTC | #3
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
  

Patch

Index: device.c
===================================================================
--- device.c	(revision 1083)
+++ device.c	(working copy)
@@ -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;
Index: device.h
===================================================================
--- device.h	(revision 1083)
+++ device.h	(working copy)
@@ -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: