循环播放 Gst.Sample 和 GstApp.AppSrc 的列表

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

我正在尝试使用 GStreamer 编写一个简单的音乐播放器。我想播放任意音乐文件 ABS_FILE_PATH,存储样本用于其他目的,然后在到达流的原始结尾后无限期地循环播放这些样本。

现在可以正常播放音乐,直到播放完曲目的最后一个样本后不久。大多数时候只是一片寂静,但有时会有一两个声音样本表明曲目刚刚再次开始播放。终端输出也是如此。它表明,在开始循环后的几个样本中,need-data 信号比之前更频繁地发送。

我已经使用 fakesink 来转储数据,这似乎工作得很好。数据只是循环播放,就像预期的那样。

那么这里发生了什么?为什么样本不播放第二次(第三次、第四次……)?我 运行 没主意了。

下面我添加了一个没有任何 UI 的最小示例,但存在同样的问题:

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

我在 of thiagoss 的帮助下找到了一个可行的解决方案。

The GstBuffer struct documentation 列出与时间戳相关的所有字段

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

其中演示时间戳 pts 是相关值。在第二次使用示例之前将其设置为 Gst.CLOCK_TIME_NONE 将解决问题:

# 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