Python + GStreamer: масштабировать видео до окна
У меня возникли некоторые проблемы с изменением размера видеовыхода GStreamer в соответствии с размером окна, в котором отображается видео (сохраняется соотношение сторон видео). Проблема в том, что мне сначала нужно предварительно прокрутить видео, чтобы иметь возможность определить его размеры путем извлечения согласованных заглавных букв, а затем вычислить размеры, в которых оно должно отображаться, чтобы соответствовать окну. После того, как я предварительно проверил видео и получил ограничения размеров, я больше не могу изменять размер видео. Установка новых заглавных букв по-прежнему приводит к выводу видео в его исходном размере. Что я должен сделать, чтобы решить это?
Просто чтобы быть полным. В текущей реализации я не могу визуализировать текстуру OpenGL, которая бы легко разрешила эту проблему, потому что вы могли бы просто визуализировать вывод в текстуру и масштабировать ее по размеру экрана. Я должен нарисовать вывод на поверхности пигмея, который должен иметь правильные размеры. Pygame предлагает функциональность для масштабирования своих поверхностей, но я думаю, что такая реализация (как у меня сейчас) медленнее, чем извлечение кадров правильного размера непосредственно из GStreamer (я прав?)
Это мой код для загрузки и отображения видео (я пропустил основной цикл):
def calcScaledRes(self, screen_res, image_res):
"""Calculate image size so it fits the screen
Args
screen_res (tuple) - Display window size/Resolution
image_res (tuple) - Image width and height
Returns
tuple - width and height of image scaled to window/screen
"""
rs = screen_res[0]/float(screen_res[1])
ri = image_res[0]/float(image_res[1])
if rs > ri:
return (int(image_res[0] * screen_res[1]/image_res[1]), screen_res[1])
else:
return (screen_res[0], int(image_res[1]*screen_res[0]/image_res[0]))
def load(self, vfile):
"""
Loads a videofile and makes it ready for playback
Arguments:
vfile -- the uri to the file to be played
"""
# Info required for color space conversion (YUV->RGB)
# masks are necessary for correct display on unix systems
_VIDEO_CAPS = ','.join([
'video/x-raw-rgb',
'red_mask=(int)0xff0000',
'green_mask=(int)0x00ff00',
'blue_mask=(int)0x0000ff'
])
self.caps = gst.Caps(_VIDEO_CAPS)
# Create videoplayer and load URI
self.player = gst.element_factory_make("playbin2", "player")
self.player.set_property("uri", vfile)
# Enable deinterlacing of video if necessary
self.player.props.flags |= (1 << 9)
# Reroute frame output to Python
self._videosink = gst.element_factory_make('appsink', 'videosink')
self._videosink.set_property('caps', self.caps)
self._videosink.set_property('async', True)
self._videosink.set_property('drop', True)
self._videosink.set_property('emit-signals', True)
self._videosink.connect('new-buffer', self.__handle_videoframe)
self.player.set_property('video-sink', self._videosink)
# Preroll movie to get dimension data
self.player.set_state(gst.STATE_PAUSED)
# If movie is loaded correctly, info about the clip should be available
if self.player.get_state(gst.CLOCK_TIME_NONE)[0] == gst.STATE_CHANGE_SUCCESS:
pads = self._videosink.pads()
for pad in pads:
caps = pad.get_negotiated_caps()[0]
self.vidsize = caps['width'], caps['height']
else:
raise exceptions.runtime_error("Failed to retrieve video size")
# Calculate size of video when fit to screen
self.scaledVideoSize = self.calcScaledRes((self.screen_width,self.screen_height), self.vidsize)
# Calculate the top left corner of the video (to later center it vertically on screen)
self.vidPos = ((self.screen_width - self.scaledVideoSize [0]) / 2, (self.screen_height - self.scaledVideoSize [1]) / 2)
# Add width and height info to video caps and reload caps
_VIDEO_CAPS += ", width={0}, heigh={1}".format(self.scaledVideoSize[0], self.scaledVideoSize[1])
self.caps = gst.Caps(_VIDEO_CAPS)
self._videosink.set_property('caps', self.caps) #??? not working, video still displayed in original size
def __handle_videoframe(self, appsink):
"""
Callback method for handling a video frame
Arguments:
appsink -- the sink to which gst supplies the frame (not used)
"""
buffer = self._videosink.emit('pull-buffer')
img = pygame.image.frombuffer(buffer.data, self.vidsize, "RGB")
# Upscale image to new surfuace if presented fullscreen
# Create the surface if it doesn't exist yet and keep rendering to this surface
# for future frames (should be faster)
if not hasattr(self,"destSurf"):
self.destSurf = pygame.transform.scale(img, self.destsize)
else:
pygame.transform.scale(img, self.destsize, self.destSurf)
self.screen.blit(self.destSurf, self.vidPos)
# Swap the buffers
pygame.display.flip()
# Increase frame counter
self.frameNo += 1
1 ответ
Я почти уверен, что ваша проблема была в том (что прошло много времени с тех пор, как вы задавали этот вопрос), что вы никогда не подключали шину, чтобы следить за сообщениями, которые были отправлены.
Код для этого обычно выглядит примерно так:
def some_function(self):
#code defining Gplayer (the pipeline)
#
# here
Gplayer.set_property('flags', self.GST_VIDEO|self.GST_AUDIO|self.GST_TEXT|self.GST_SOFT_VOLUME|self.GST_DEINTERLACE)
# more code
#
# finally
# Create the bus to listen for messages
bus = Gplayer.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('message', self.OnBusMessage)
bus.connect('sync-message::element', self.OnSyncMessage)
# Listen for gstreamer bus messages
def OnBusMessage(self, bus, message):
t = message.type
if t == Gst.MessageType.ERROR:
pass
elif t == Gst.MessageType.EOS:
print ("End of Audio")
return True
def OnSyncMessage(self, bus, msg):
if msg.get_structure() is None:
return True
if message_name == 'prepare-window-handle':
imagesink = msg.src
imagesink.set_property('force-aspect-ratio', True)
imagesink.set_window_handle(self.panel1.GetHandle())
Ключевым битом для вашей проблемы является настройка обратного вызова для сообщения синхронизации и в этом обратном вызове установка свойства force-aspect-ratio
к Истине.
Это свойство гарантирует, что видео всегда соответствует окну, в котором оно отображается.
Обратите внимание, что функция self.panel1.GetHandle() называет панель, на которой вы отображаете видео.
Я ценю, что вы пошли дальше, но, надеюсь, это поможет кому-то еще копаться в архивах.