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() называет панель, на которой вы отображаете видео.

Я ценю, что вы пошли дальше, но, надеюсь, это поможет кому-то еще копаться в архивах.

Другие вопросы по тегам