如何获取GStreamer流数据的持续时间

1 投票
1 回答
4522 浏览
提问于 2025-04-17 05:37

我正在写一个程序,用GStreamer把媒体文件转换成mp3文件。这个程序可以正常工作,但我想知道音频流的时长。下面是简化后的代码。

import logging

import pygst
pygst.require('0.10')
import gst

# this is very important, without this, callbacks from gstreamer thread
# will messed our program up
import gobject
gobject.threads_init()


def on_new_buffer(appsink):
    buf = appsink.emit('pull-buffer')
    print 'new buffer', len(buf)

def on_new_preroll(appsink):
    buf = appsink.emit('pull-preroll')
    print 'new preroll', len(buf)

def on_pad_added(decoder, pad):
    print 'Pad added'
    decoder.link(converter)
    pipeline.set_state(gst.STATE_PLAYING)

def on_msg(msg):
    if msg.type == gst.MESSAGE_ERROR:
        error, debug = msg.parse_error()
        print error, debug
    elif msg.type == gst.MESSAGE_EOS:
        duration = pipeline.query_duration(gst.FORMAT_TIME)
        print 'Duration', duration

pipeline = gst.Pipeline('pipeline')

appsrc = gst.element_factory_make('appsrc', 'src')
decoder = gst.element_factory_make('decodebin2', 'decoder')
converter = gst.element_factory_make('audioconvert', 'converter')
lame = gst.element_factory_make('lamemp3enc', 'lame')
appsink = gst.element_factory_make('appsink', 'sink')

pipeline.add(appsrc, decoder, lame, converter, appsink)
gst.element_link_many(appsrc, decoder)
gst.element_link_many(converter, lame, appsink)

# -- setup appskink --

# -- setup decoder --
decoder.connect('pad-added', on_pad_added)

# -- setup mp3 encoder --
lame.set_property('bitrate', 128)

# -- setup appsink --
# this makes appsink emit singals
appsink.set_property('emit-signals', True)
# turns off sync to make decoding as fast as possible
appsink.set_property('sync', False)
appsink.connect('new-buffer', on_new_buffer)
appsink.connect('new-preroll', on_new_preroll)

pipeline.set_state(gst.STATE_PAUSED)

data = open(r'D:\Musics\Fiona Fung - Proud Of You.mp3', 'rb').read()
buf = gst.Buffer(data)
appsrc.emit('push-buffer', buf)
appsrc.emit('end-of-stream')

bus = pipeline.get_bus()
while True:
    msg = bus.poll(gst.MESSAGE_ANY, -1)
    on_msg(msg)

我没有使用filesrc作为数据源,而是用了appsrc。我想从互联网读取流数据,而不是从文件中读取。奇怪的是,结果是输出的时长是-1。

....
new buffer 315
new buffer 320
new buffer 335
new buffer 553
Duration (-1L, <enum GST_FORMAT_TIME of type GstFormat>)

如果我把appsrc换成filesrc,那么时长就正确了。

import logging

import pygst
pygst.require('0.10')
import gst

# this is very important, without this, callbacks from gstreamer thread
# will messed our program up
import gobject
gobject.threads_init()


def on_new_buffer(appsink):
    buf = appsink.emit('pull-buffer')
    print 'new buffer', len(buf)

def on_new_preroll(appsink):
    buf = appsink.emit('pull-preroll')
    print 'new preroll', len(buf)

def on_pad_added(decoder, pad):
    print 'Pad added'
    decoder.link(converter)
    pipeline.set_state(gst.STATE_PLAYING)

def on_msg(msg):
    if msg.type == gst.MESSAGE_ERROR:
        error, debug = msg.parse_error()
        print error, debug
    elif msg.type == gst.MESSAGE_EOS:
        duration = pipeline.query_duration(gst.FORMAT_TIME)
        print 'Duration', duration

pipeline = gst.Pipeline('pipeline')

filesrc = gst.element_factory_make('filesrc', 'src')
decoder = gst.element_factory_make('decodebin2', 'decoder')
converter = gst.element_factory_make('audioconvert', 'converter')
lame = gst.element_factory_make('lamemp3enc', 'lame')
appsink = gst.element_factory_make('appsink', 'sink')

pipeline.add(filesrc, decoder, lame, converter, appsink)
gst.element_link_many(filesrc, decoder)
gst.element_link_many(converter, lame, appsink)

# -- setup filesrc --
filesrc.set_property('location', r'D:\Musics\Fiona Fung - Proud Of You.mp3')

# -- setup decoder --
decoder.connect('pad-added', on_pad_added)

# -- setup mp3 encoder --
lame.set_property('bitrate', 128)

# -- setup appsink --
# this makes appsink emit singals
appsink.set_property('emit-signals', True)
# turns off sync to make decoding as fast as possible
appsink.set_property('sync', False)
appsink.connect('new-buffer', on_new_buffer)
appsink.connect('new-preroll', on_new_preroll)

pipeline.set_state(gst.STATE_PAUSED)

bus = pipeline.get_bus()
while True:
    msg = bus.poll(gst.MESSAGE_ANY, -1)
    on_msg(msg)

如你所见,现在结果是正确的。

new buffer 322
new buffer 323
new buffer 315
new buffer 320
new buffer 549
Duration (189459000000L, <enum GST_FORMAT_TIME of type GstFormat>)

所以,我的问题是 - 如何从appsrc获取音频流数据的正确时长呢?

谢谢。

1 个回答

1

很遗憾,使用 appsrc 的时候,无法准确获取流的持续时间。虽然对于一些固定比特率的格式,可以根据文件长度来估算,但对于那些使用可变比特率的格式,就无法知道具体的长度了。

这是因为 appsrc 是基于接收的数据块来工作的(可以是推送或拉取的方式)。它接收到一段数据后会消耗掉,然后再请求下一段数据或者等待提供下一段数据,因此几乎不可能估算媒体的持续时间。而且在推送模式下,媒体也无法进行快进或回放。

撰写回答