Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
409 views
in Technique[技术] by (71.8m points)

python - Looping playback of a list of Gst.Sample with GstApp.AppSrc

I'm trying to write a simple music player using GStreamer. I want to play any arbitrary music file ABS_FILE_PATH, store the samples for other purposes and later loop over these indefinitely, once the original end of stream is reached.

Now playing the music works fine until short after the last sample of the track was played. Most of the time there's just silence, but sometimes there are one or two audible samples indicating, that the track just started playing again. The same holds for the terminal output. It shows, that a few samples after looping was started, the need-data signal is sent more frequently than before.

I've used fakesink for dumping the data, which seemed to work perfectly fine. The data was just looped, like it was intended.

So what happens here? Why don't the samples play a second (third, fourth, ...) time? I've run out of ideas.

Following I added a minimal example of what I'm doing without any UI, but with the same problem:

import itertools, signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
from gi.repository import Gst, GstApp, Gtk
Gst.init(None)

# read samples with Gst.AppSink from a playbin
playbin = Gst.ElementFactory.make("playbin")
playbin.props.uri = "file://" + ABS_FILE_PATH # only works with absolute paths
playbin.props.audio_sink = GstApp.AppSink(sync=False, emit_signals=True)
playbin.set_state(Gst.State.PLAYING)

# loop over all samples
def samples(app_sink):
    samples = []
    sample = app_sink.pull_sample()
    while sample:
        yield sample
        samples.append(sample)
        sample = app_sink.pull_sample()
    print('looping')
    for sample in itertools.cycle(samples):
        yield sample

# write samples with Gst.AppSrc
def need_data(appsrc, length, samples):
    print('sample')
    sample = next(samples)
    appsrc.set_caps(sample.get_caps())
    appsrc.push_buffer(sample.get_buffer())
src = GstApp.AppSrc(format=Gst.Format.TIME, emit_signals=True)
src.connect('need-data', need_data, samples(playbin.props.audio_sink))

# to the autoaudiosink or just a fakesink
sink = Gst.ElementFactory.make("autoaudiosink")
#sink = Gst.ElementFactory.make("fakesink")
#sink.props.dump = True # dump contents of fakesink

# playback
play = Gst.Pipeline()
play.add(src)
play.add(sink)
src.link(sink)
play.set_state(Gst.State.PLAYING)

Gtk.main()

gst-plugins-base: 1.4.4

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've found a working solution with help of the comment of thiagoss.

The GstBuffer struct documentation lists all the fields relevant for the timestamp

struct GstBuffer {
  [...]
  /* timestamp */
  GstClockTime           pts;
  GstClockTime           dts;
  GstClockTime           duration;
  [...]
};

where the presentation timestamp pts is the relevant value. Setting it to Gst.CLOCK_TIME_NONE before using the sample for a second time will solve the issue:

# loop over all samples
def samples(app_sink):
    samples = []
    sample = app_sink.pull_sample()
    while sample:
        yield sample
        sample.get_buffer().pts = Gst.CLOCK_TIME_NONE #unset the pts
        samples.append(sample)
        sample = app_sink.pull_sample()
    print('looping')
    for sample in itertools.cycle(samples):
        yield sample

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...