Wrong PTS in AppSink and duplicate frames

Robert Tari robert at tari.in
Fri Apr 10 22:33:17 UTC 2020


Hello everyone,

I've been struggling with this for months now. I've looked in all the 
GStreamer docs, read the whole Internet, but couldn't find a cure. I 
would really be grateful for some help. Thanks in advance.

Here is what I'm trying to accomplish (simplified):

- seek to an exact decoded raw audio frame position
- read a number of frames
- (do some processing)
- seek exactly past the previously read frames
- read some more frames
- (and so on)

What I'm expecting to see:
- A contiguous buffer seamlessly memcpy'd together from AppSink's 
buffers, that is basically a perfect copy of the decoded raw audio data. 
This works OK for WAV files.

This is what happens instead:
- For FLAC: after a seek, the first GstSample that I pull from AppSink 
is always offset by exactly 1 frame, so I get a duplicate between the 
end of the previous call to my read function and the current one
- For various other formats: most GstSamples' GstBuffer->pts are either 
-1, 0, or +1 frames of their expected position - so I either get 1 
duplicate, get it right, or I'm 1 frame short

I've got the same results with hundreds of various test files, GStreamer 
1.14.5 and 1.16.2, on both 32 and 64bit versions of Linux.


*****************************************************************************************


Here is the pipeline:
---------------------

"filesrc location=FILE ! decodebin ! audioconvert ! audio/x-raw ! 
appsink name=sink sync=FALSE"


And here is my read function (simplified):
------------------------------------------

void read(gchar *lBuffer, guint64 nStartFrame, guint64 nFramesToRead, 
GstAudioInfo *pAudioInfo, GstPipeline *pPipeline)
{
     guint64 nBytesToRead = nFramesToRead * pAudioInfo->bpf;
     guint64 nBytesRead = 0;
     // We need this temp buffer because of the offset thing
     gchar *lBytes = g_malloc(nBytesToRead + 8192);
     guint nOffsetBytes = 0;

     //We are paused here

     gint64 nTime = GST_FRAMES_TO_CLOCK_TIME(nStartFrame, 
pAudioInfo->rate);
     gst_element_seek_simple(GST_ELEMENT_CAST(pPipeline), 
GST_FORMAT_TIME, GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH, nTime);
     gst_element_get_state(GST_ELEMENT_CAST(pPipeline), NULL, NULL, 
GST_CLOCK_TIME_NONE);

     gst_element_query_position(GST_ELEMENT_CAST(pPipeline), 
GST_FORMAT_TIME, &nTime);


     // Very important to continue where we left off
     g_assert(GST_CLOCK_TIME_TO_FRAMES(nTime, pAudioInfo->rate) == 
nStartFrame);

     gst_element_set_state(GST_ELEMENT_CAST(pPipeline), 
GST_STATE_PLAYING);
     GstAppSink *pAppSink = 
GST_APP_SINK_CAST(gst_bin_get_by_name(GST_BIN_CAST(pPipeline), "sink"));

     while (!gst_app_sink_is_eos(pAppSink))
     {
         GstSample *pSample = gst_app_sink_pull_sample(pAppSink);

         if (pSample)
         {
             GstBuffer *pBuffer = gst_sample_get_buffer(pSample);
             gint64 nOfset = GST_CLOCK_TIME_TO_FRAMES(pBuffer->pts, 
pAudioInfo->rate);
             gint64 nWantOffset = (nBytesRead / pAudioInfo->bpf) + 
nStartFrame;

             if (nOfset > nWantOffset)
             {
                 //WAV and FLAC never get here
                 g_error("Bad pBuffer->pts");
             }
             else if (nOfset < nWantOffset && nOffsetBytes == 0)
             {
                 // FLAC reports multiple bad PTSs, but only the first 
one must be corrected
                 nOffsetBytes = (nWantOffset - nOfset) * pAudioInfo->bpf;
             }

             GstMapInfo pMapInfo;
             gboolean bSuccess = gst_buffer_map(pBuffer, &pMapInfo, 
GST_MAP_READ);

             if (bSuccess)
             {
                 memcpy(lBytes + nBytesRead, pMapInfo.data, 
pMapInfo.size);
                 nBytesRead += pMapInfo.size;
                 gst_buffer_unmap(pBuffer, &pMapInfo);
             }
             else
             {
                 g_error("gst_buffer_map failed");
             }

             gst_sample_unref(pSample);
         }
         else
         {
             g_error("gst_app_sink_pull_sample failed");
         }

         // Normally, it would be: if (nBytesRead == nBytesToRead)
         if (nBytesRead >= nBytesToRead + nOffsetBytes)
         {
             break;
         }
     }

     gst_object_unref(pAppSink);
     gst_element_set_state(GST_ELEMENT_CAST(pPipeline), 
GST_STATE_PAUSED);

     memcpy(lBuffer, lBytes + nOffsetBytes, nBytesToRead);
     g_free(lBytes);
}


More information about the gstreamer-devel mailing list