PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Сохранить кадр из видеофайла


Сохранить кадр из видеофайла

Сообщений 1 страница 15 из 15

1

Здравствуйте, задача - нужно вытащить кадр из видеофайла. Есть способ средствами пурика? Или win api? Сам разобраться не смог.
Может быть можно как то выдернуть изображение миниатюры, которую создает винда для проводника?

Отредактировано server1982 (28.03.2021 12:01:47)

0

2

Код:
Structure PB_StructureMovie 
  Movie.IGraphBuilder 
  MediaControl.IMediaControl 
  MediaEvent.IMediaEventEx 
  Window.IVideoWindow 
  Audio.IBasicAudio 
  Video.IBasicVideo 
  MediaSeeking.IMediaSeeking 
  state.l 
EndStructure 

Procedure.l CaptureFrame(MovieNumber, ImageNumber)
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result
  
  *Movie  = IsMovie(MovieNumber)  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie)
  *Window = *Movie\Window
  *Video  = *Movie\Video
  
  Result  = 0 
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK
    
    *ImageData = AllocateMemory(DataSize)
    If *ImageData
      
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK
        
        If IsImage( ImageNumber )
          hBmp  = ImageID( ImageNumber )
          
          w = ImageWidth(ImageNumber)
          h = ImageHeight(ImageNumber)
          
          bmi.BITMAPINFO
          bmi\bmiHeader\biSize   = SizeOf(BITMAPINFOHEADER) 
          bmi\bmiHeader\biWidth  = w
          bmi\bmiHeader\biHeight = h 
          bmi\bmiHeader\biPlanes = 1 
          
          bmi\bmiHeader\biBitCount = ImageDepth(ImageNumber) 
          bmi\bmiHeader\biBitCount = 32
          bmi\bmiHeader\biCompression = #BI_RGB
          
          hdc = StartDrawing(ImageOutput(ImageNumber))
          If hdc
            SetDIBits_( hdc, hBmp, 0, h, *ImageData, @bmi, #DIB_RGB_COLORS) 
            StopDrawing()
          EndIf
        EndIf
      EndIf
      
      FreeMemory(*ImageData)
    EndIf
    
  EndIf   
  
  *Window\get_Owner(@Parent) 
  RedrawWindow_(Parent,0,0,#RDW_INVALIDATE)
  ProcedureReturn Result
EndProcedure


; ---------------------------------------------------------------------
; Code example:
; ---------------------------------------------------------------------

#Movie = 0
#Image = 0
#Memory = 0
#Window = 0

#Gadget_Capture = 0
#Gadget_Image  = 1

If InitMovie()
  FileName$ = OpenFileRequester("Choose Movie","","Movie Files|*.mpg;*.avi;*.mpeg|All Files|*.*", 0)
  
  If LoadMovie(#Movie, FileName$)
  
    Width = MovieWidth(#Movie)
    Height = MovieHeight(#Movie)
  
    CreateImage(#Image, Width , Height, 32)

    If OpenWindow(#Window, 0, 0, Width , Height*2 + 35, "Frame Capture", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_Invisible)
    
           
        ButtonGadget(#Gadget_Capture, (Width -100)/2, Height+5, 100, 25, "Capture Frame")
        ImageGadget(#Gadget_Image, 0, Height+35, Width, Height, ImageID(#Image))
        
        PlayMovie(#Movie, WindowID(#Window))
        
        HideWindow(#Window, 0)
        
        Repeat
          Event = WaitWindowEvent()
          
          If Event = #PB_Event_Gadget And EventGadget() = #Gadget_Capture
          
            PauseMovie(#Movie)
            
            CaptureFrame(#Movie, #Image)
            SetGadgetState(#Gadget_Image, ImageID(#Image))                        
            
            ResumeMovie(#Movie)         
            
          EndIf
          
        Until Event = #PB_Event_CloseWindow
      
    EndIf        
  EndIf
EndIf

End

0

3

Спасибо! Интересная процедура, буду разбираться. Но в примере идет снятие изображения с видео, поставленного на паузу. А без воспроизведения видео есть варианты?

0

4

server1982
удивлён что и на это есть ответ, я ожидал что для этого нужен API кодека, ведь только он может раскодировать файл и взять из него кадр.
А есть случаи, когда перематываешь и сначала снег потом вырисовывается кадр через пару секунд, так как декодер использует предыдущие кадры для построения нового, такая у него стратегия, как в этом случае получить кадр, если сам декодер не может это сделать в плеере.

Отредактировано AZJIO (28.03.2021 13:33:20)

0

5

AZJIO
подозревал, что будет непросто. Делаю простенькую фото- видеогалерею, хочется сделать миниатюры на папку с видеофайлами.

0

6

Функция MovieSeek позволяет выбрать кадр с которого снять скриншот.
То есть при необходимости можно автоматизировать процесс если известно с каких кадров нужно делать скрины.

0

7

Пётр написал(а):

Функция MovieSeek позволяет выбрать кадр с которого снять скриншот.
То есть при необходимости можно автоматизировать процесс если известно с каких кадров нужно делать скрины.

Пётр, неизвестно. Потому что неизвестно какие будут видео в папке. Поэтому любой.
Но как это сделать без проигрывания видео - пока не понятно. Если это возможно.
АП:
Похоже разобрался, предварительно большое спасибо!

Отредактировано server1982 (28.03.2021 16:26:48)

0

8

server1982 написал(а):

неизвестно

Если неизвестно то как искать кадр, неважно проигрывая видео или нет?
Может известно число секунд от начала видео?

0

9

Пётр написал(а):

Если неизвестно то как искать кадр, неважно проигрывая видео или нет?
Может известно число секунд от начала видео?

Мне нужен любой кадр из видео для миниатюры на изображение папки. Пока получается только первый кадр, не пойму что не так, MovieSeek работает только если видео воспроизводится?
В общем, какая-то засада с MovieSeek. Есть какие-либо "маневры" с номером кадра? У меня всегда на 1й прыгает

Отредактировано server1982 (28.03.2021 18:15:47)

0

10

Может зависит от видео, но с теми что тестировал этот код нормально работает.

Код:
Structure PB_StructureMovie 
  Movie.IGraphBuilder 
  MediaControl.IMediaControl 
  MediaEvent.IMediaEventEx 
  Window.IVideoWindow 
  Audio.IBasicAudio 
  Video.IBasicVideo 
  MediaSeeking.IMediaSeeking 
  state.l 
EndStructure 

Procedure.l CaptureFrame(MovieNumber, ImageNumber)
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result
  
  *Movie  = IsMovie(MovieNumber)  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie)
  *Window = *Movie\Window
  *Video  = *Movie\Video
  
  Result  = 0 
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK
    
    *ImageData = AllocateMemory(DataSize)
    If *ImageData
      
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK
        
        If IsImage( ImageNumber )
          hBmp  = ImageID( ImageNumber )
          
          w = ImageWidth(ImageNumber)
          h = ImageHeight(ImageNumber)
          
          bmi.BITMAPINFO
          bmi\bmiHeader\biSize   = SizeOf(BITMAPINFOHEADER) 
          bmi\bmiHeader\biWidth  = w
          bmi\bmiHeader\biHeight = h 
          bmi\bmiHeader\biPlanes = 1 
          
          bmi\bmiHeader\biBitCount = ImageDepth(ImageNumber) 
          bmi\bmiHeader\biBitCount = 32
          bmi\bmiHeader\biCompression = #BI_RGB
          
          hdc = StartDrawing(ImageOutput(ImageNumber))
          If hdc
            SetDIBits_( hdc, hBmp, 0, h, *ImageData, @bmi, #DIB_RGB_COLORS) 
            StopDrawing()
          EndIf
        EndIf
      EndIf
      
      FreeMemory(*ImageData)
    EndIf
    
  EndIf   
  
  *Window\get_Owner(@Parent) 
  RedrawWindow_(Parent,0,0,#RDW_INVALIDATE)
  ProcedureReturn Result
EndProcedure

FileName$ = "Путь к видео"

InitMovie()
If LoadMovie(0, FileName$)
  Width = MovieWidth(0)
  Height = MovieHeight(0)
  CreateImage(0, Width , Height, 32)
  MovieSeek(0, 100) ; Второй параметр - номер кадра.
  CaptureFrame(0, 0)
  ShowLibraryViewer("Image", 0)
  CallDebugger
EndIf

0

11

Пётр написал(а):

Может зависит от видео, но с теми что тестировал этот код нормально работает.

Похоже, да. AVI сработало, mp4  и mov  - нет. Может я что-то не включил/подгрузил/инициализировал?

0

12

Для MP4 нужно немного изменить код.

Код:
Enumeration ; MediaSeeking
  #AM_SEEKING_NoPositioning        
  #AM_SEEKING_AbsolutePositioning
EndEnumeration

Structure PB_StructureMovie 
  Movie.IGraphBuilder
  MediaControl.IMediaControl
  MediaEvent.IMediaEventEx
  Window.IVideoWindow
  Audio.IBasicAudio
  Video.IBasicVideo
  MediaSeeking.IMediaSeeking
  state.l 
EndStructure 

Procedure.l CaptureFrame(MovieNumber, ImageNumber)
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result
  
  *Movie  = IsMovie(MovieNumber)  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie)
  *Window = *Movie\Window
  *Video  = *Movie\Video
  
  Result  = 0 
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK
    
    *ImageData = AllocateMemory(DataSize)
    If *ImageData
      
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK
        
        If IsImage( ImageNumber )
          hBmp  = ImageID( ImageNumber )
          
          w = ImageWidth(ImageNumber)
          h = ImageHeight(ImageNumber)
          
          bmi.BITMAPINFO
          bmi\bmiHeader\biSize   = SizeOf(BITMAPINFOHEADER) 
          bmi\bmiHeader\biWidth  = w
          bmi\bmiHeader\biHeight = h 
          bmi\bmiHeader\biPlanes = 1 
          
          bmi\bmiHeader\biBitCount = ImageDepth(ImageNumber) 
          bmi\bmiHeader\biBitCount = 32
          bmi\bmiHeader\biCompression = #BI_RGB
          
          hdc = StartDrawing(ImageOutput(ImageNumber))
          If hdc
            SetDIBits_( hdc, hBmp, 0, h, *ImageData, @bmi, #DIB_RGB_COLORS) 
            StopDrawing()
          EndIf
        EndIf
      EndIf
      
      FreeMemory(*ImageData)
    EndIf
    
  EndIf   
  
  *Window\get_Owner(@Parent) 
  RedrawWindow_(Parent,0,0,#RDW_INVALIDATE)
  ProcedureReturn Result
EndProcedure

Procedure MediaSeek(MovieNumber, pos.q)
  Protected *Movie.PB_StructureMovie
  Protected duration.q, *Seek.IMediaSeeking
  
  *Movie  = IsMovie(MovieNumber)  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie)
  *Seek  = *Movie\MediaSeeking
  
  pos * 10000
  *Seek\GetDuration(@duration) 
  ProcedureReturn *Seek\SetPositions(@pos, #AM_SEEKING_AbsolutePositioning,@duration, #AM_SEEKING_NoPositioning)
EndProcedure

FileName$ = "Путь к видео"

InitMovie()
If LoadMovie(0, FileName$)
  Width = MovieWidth(0)
  Height = MovieHeight(0)
  CreateImage(0, Width , Height, 32)
  MediaSeek(0, 10000)
  CaptureFrame(0, 0)
  ShowLibraryViewer("Image", 0)
  CallDebugger
EndIf

0

13

Какой масштаб у параметра pos? 10000 - это 100 кадров? Если сравнивать с предыдущим кодом...
Понял, это время. Спасибо, Пётр, узнал много нового. Понял, что не знаю еще больше =)

Отредактировано server1982 (28.03.2021 20:30:10)

0

14

server1982 написал(а):

10000 - это 100 кадров?

https://docs.microsoft.com/en-us/window … tpositions
Шаг 100 наносекунд, но поскольку pos умножается на 10000 то шаг составляет 1 миллисекунду.
10000 это 10 секунд.

0

15

Пётр написал(а):

https://docs.microsoft.com/en-us/window … tpositions
Шаг 100 наносекунд, но поскольку pos умножается на 10000 то шаг составляет 1 миллисекунду.
10000 это 10 секунд.

да, выше уже отписался. Еще раз спасибо.

0


Вы здесь » PureBasic - форум » Вопросы по PureBasic » Сохранить кадр из видеофайла