Capture a movie frame

Share your advanced PureBasic knowledge/code with the community.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Capture a movie frame

Post by freak »

Hello,

I wrote a little routine today, to capture the current frame of a movie
to a pb image. I noticed, that this has been asked here before, without
a solution, so here it is:

Code: Select all

; ---------------------------------------------------------------------
;
; CaptureFrame(#Movie, #Image, #Memory)
;
; Copies the current frame of the #Movie to the #Image.
; The movie must pe paused when calling this, otherwise it will fail.
; The #Image should be the same size as the movie (MovieWidth(), MovieHeight())
; You must provide an unused memory bank number for temporary storage.
;
; ---------------------------------------------------------------------

Structure PB_StructureMovie
  Movie.l          ; IGraphBuilder DirectShow pointer
  MediaControl.l   ; IMediaControl DirectShow pointer
  MediaEvent.l     ; IMediaEventEx DirectShow pointer
  Window.l         ; IVideoWindow DirectShow pointer
  Audio.l          ; IBasicAudio DirectShow pointer
  Video.l          ; IBasicVideo DirectShow pointer
  MediaSeeking.l   ; IMediaSeeking DirectShow pointer
  State.l          ; State of the movie
EndStructure

Procedure.l CaptureFrame(MovieNumber, ImageNumber, MemoryBank)
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result
  
  !extrn _PB_Movie_ObjectsArea
  !mov eax, [_PB_Movie_ObjectsArea]
  !mov [esp+12], eax
  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie)
  *Window = *Movie\Window
  *Video  = *Movie\Video
  
  Result  = 0  
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK
  
    *ImageData = AllocateMemory(MemoryBank, DataSize)
    If *ImageData
    
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK
            
        Result = SetDIBits_(0, UseImage(ImageNumber), 0, *ImageData\biHeight, *ImageData+*ImageData\biSize, *ImageData, #DIB_RGB_COLORS)
      
      EndIf
    
      FreeMemory(MemoryBank)
    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()
    Height = MovieHeight()
  
    CreateImage(#Image, Width , Height)

    If OpenWindow(#Window, 0, 0, Width , Height*2 + 35, #PB_Window_SystemMenu|#PB_Window_Screencentered|#PB_Window_Invisible, "Frame Capture")
    
      If CreateGadgetList(WindowID())
      
        ButtonGadget(#Gadget_Capture, (Width -100)/2, Height+5, 100, 25, "Capture Frame")
        ImageGadget(#Gadget_Image, 0, Height+35, Width, Height, UseImage(#Image))
        
        PlayMovie(#Movie, WindowID())
        
        HideWindow(#Window, 0)
        
        Repeat
          Event = WindowEvent()
          
          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Capture
          
            PauseMovie()
            
            CaptureFrame(#Movie, #Image, #Memory)
            SetGadgetState(#Gadget_Image, UseImage(#Image))                        
            
            ResumeMovie()         
            
          
          EndIf
          
          If Event = 0
            Delay(1)
          EndIf
          
        Until Event = #PB_EventCloseWindow
        
      EndIf        
      
    EndIf        
    
  EndIf
  
EndIf

End
Have fun...

Timo
quidquid Latine dictum sit altum videtur
Jon
User
User
Posts: 20
Joined: Sat Apr 26, 2003 9:12 am
Location: New Zealand
Contact:

Post by Jon »

This barfs on my system? WinXP, PB 3.8.

DEBUGGER : Error at line 39 : Can't allocate a memory block of size zero

LINE 39 : *ImageData = AllocateMemory(MemoryBank, DataSize)


EDIT : Ok, updated to PB 8.1 It worked! Weird?
Last edited by Jon on Mon Feb 09, 2004 7:21 am, edited 1 time in total.
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Works on win2k.

Thanks, freak.
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Post by J. Baker »

Works great on WIN XP (Home) here. :D
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

Jon wrote:This barfs on my system? WinXP, PB 3.8.

DEBUGGER : Error at line 39 : Can't allocate a memory block of size zero

LINE 39 : *ImageData = AllocateMemory(MemoryBank, DataSize)

?
This means, that the interface call was successfull, but returned an imagesize
of 0, that is very strange.
Just add a check, if DataSize is nonzero, before continuing.

Timo
quidquid Latine dictum sit altum videtur
DriakTravo
Enthusiast
Enthusiast
Posts: 346
Joined: Fri Oct 10, 2003 12:42 am
Location: Tampa,FL,USA
Contact:

Post by DriakTravo »

Ok, is there any way to capture the current movie frame into a sprite so you can play the movie in OpenScreen() ? Or is there any way to play a movie in OpenScreen?
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

have a look at PB's RenderMovieFrame() function.

Timo
quidquid Latine dictum sit altum videtur
DriakTravo
Enthusiast
Enthusiast
Posts: 346
Joined: Fri Oct 10, 2003 12:42 am
Location: Tampa,FL,USA
Contact:

Post by DriakTravo »

I tried this:

Code: Select all

InitSprite()
InitMovie()
InitKeyboard()
MoveFile.s = OpenFileRequester("","","MPEG|*.mpg",0)

OpenScreen(800,600,16,"")

If LoadMovie(0,MoveFile)
  UseMovie(0)
  For X = 0 To MovieLength()
    ClearScreen(0,0,0)
    If MovieSeek(X)
      RenderMovieFrame(CreateSprite(X,MovieWidth(),MovieHeight()))
    EndIf
  Next X
EndIf

For X = 0 To MovieLength()
  ClearScreen(0,0,0)
  DisplaySprite(X,0,0)
  FlipBuffers()
  
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Escape)
    End
  EndIf
Next X
But it doesn't work.
Justin
Addict
Addict
Posts: 948
Joined: Sat Apr 26, 2003 2:49 pm

Post by Justin »

Nice one, thanks freak
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

DriakTravo:
When rendering the movie to a sprite, MovieStatus() and MovieSeek() don't
work. The whole think works like a *stream*. You can only play from the
start and not from somewhere inbetween.
So rendering the movie in lot's of sprites isn't possible.

You have to start the movie with PlayMovie() and #PB_Movie_Rendered as
windowid. Then, in your drawing loop, you can use the RenderMovieFrame()
command on a sprite.

Timo
quidquid Latine dictum sit altum videtur
DriakTravo
Enthusiast
Enthusiast
Posts: 346
Joined: Fri Oct 10, 2003 12:42 am
Location: Tampa,FL,USA
Contact:

Post by DriakTravo »

DriakTravo:
When rendering the movie to a sprite, MovieStatus() and MovieSeek() don't
work. The whole think works like a *stream*. You can only play from the
start and not from somewhere inbetween.
So rendering the movie in lot's of sprites isn't possible.

You have to start the movie with PlayMovie() and #PB_Movie_Rendered as
windowid. Then, in your drawing loop, you can use the RenderMovieFrame()
command on a sprite.

Timo

Code: Select all

InitSprite()
InitKeyboard()
InitMovie()
InitSprite3D()
File.s = OpenFileRequester("","","MPG|*.mpg","")
OpenScreen(800,600,16,"")
LoadMovie(0,File)
UseMovie(0)
If PlayMovie(0,#PB_Movie_Rendered) = 0
  CloseScreen()
  MessageRequester("Error","Can't playmovie")
EndIf
Repeat
  ClearScreen(0,0,0)
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Escape)
    End
  EndIf
  RenderMovieFrame(CreateSprite(NOS,MovieWidth(),MovieHeight(),#PB_Sprite_Texture))
  DisplaySprite(NOS,0,0)
  GrabSprite(NO+MovieLength(),0,0,MovieWidth(),MovieHeight(),#PB_Sprite_Texture)
  NOS + 1
  FlipBuffers()
Until NOS = MovieLength()
ClearScreen(0,0,0)
For X = 0 To NOS
  CreateSprite3D(X,X)
  ZoomSprite3D(X, 800, 600)
Next X
SetFrameRate(30)
Repeat
  ClearScreen(0,0,0)
    Start3D()
    DisplaySprite3D(NOS,0,0)
    Stop3D()
  FlipBuffers()
  NOS - 1
Until NOS = 0
Isin't this movie rendering in lots of sprites? I even play the movie in reverse at the end to make sure.
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

,,

Post by NoahPhense »

Jon wrote:EDIT : Ok, updated to PB 8.1 It worked! Weird?
8.1 ?

[edit]

ah.. 3.81 ;)


- np
User avatar
Inner
PureBasic Expert
PureBasic Expert
Posts: 714
Joined: Fri Apr 25, 2003 4:47 pm
Location: New Zealand

Post by Inner »

Works fine under WinXP here, infact I added movie seek to it.

Code: Select all

; --------------------------------------------------------------------- 
; 
; CaptureFrame(#Movie, #Image, #Memory) 
; 
; Copies the current frame of the #Movie to the #Image. 
; The movie must pe paused when calling this, otherwise it will fail. 
; The #Image should be the same size as the movie (MovieWidth(), MovieHeight()) 
; You must provide an unused memory bank number for temporary storage. 
; 
; --------------------------------------------------------------------- 

Structure PB_StructureMovie 
  Movie.l          ; IGraphBuilder DirectShow pointer 
  MediaControl.l   ; IMediaControl DirectShow pointer 
  MediaEvent.l     ; IMediaEventEx DirectShow pointer 
  Window.l         ; IVideoWindow DirectShow pointer 
  Audio.l          ; IBasicAudio DirectShow pointer 
  Video.l          ; IBasicVideo DirectShow pointer 
  MediaSeeking.l   ; IMediaSeeking DirectShow pointer 
  State.l          ; State of the movie 
EndStructure 

Procedure.l CaptureFrame(MovieNumber, ImageNumber, MemoryBank) 
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow 
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result 
  
  !extrn _PB_Movie_ObjectsArea 
  !mov eax, [_PB_Movie_ObjectsArea] 
  !mov [esp+12], eax 
  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie) 
  *Window = *Movie\Window 
  *Video  = *Movie\Video 
  
  Result  = 0  
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK 
  
    *ImageData = AllocateMemory(MemoryBank, DataSize) 
    If *ImageData 
    
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK 
            
        Result = SetDIBits_(0, UseImage(ImageNumber), 0, *ImageData\biHeight, *ImageData+*ImageData\biSize, *ImageData, #DIB_RGB_COLORS) 
      
      EndIf 
    
      FreeMemory(MemoryBank) 
    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
#Gadget_Tracker = 2 

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

    If OpenWindow(#Window, 0, 0, Width , Height*2 + 35, #PB_Window_SystemMenu|#PB_Window_Screencentered|#PB_Window_Invisible, "Frame Capture") 
    
      If CreateGadgetList(WindowID()) 
      
        ButtonGadget(#Gadget_Capture,0, Height+5, 100, 25, "Capture Frame")
        TrackBarGadget(#Gadget_Tracker,110,Height+5,Width-110,20,0,Length/FPS,#PB_TrackBar_Ticks) 
        ImageGadget(#Gadget_Image, 0, Height+35, Width, Height, UseImage(#Image)) 
        
        PlayMovie(#Movie, WindowID()) 
        
        HideWindow(#Window, 0) 
        
        Repeat 
          Event = WindowEvent() 
          
          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Capture 
            PauseMovie() 
            CaptureFrame(#Movie, #Image, #Memory) 
            SetGadgetState(#Gadget_Image, UseImage(#Image))                        
            ResumeMovie()          
          EndIf 

          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Tracker 
           ; PauseMovie() 
            MovieSeek(GetGadgetState(#Gadget_Tracker)*FPS)
           ; ResumeMovie()          
          EndIf

          If Event = 0 
            Delay(1) 
          EndIf 
          
        Until Event = #PB_EventCloseWindow 
        
      EndIf        
      
    EndIf        
    
  EndIf 
  
EndIf 

End
DriakTravo
Enthusiast
Enthusiast
Posts: 346
Joined: Fri Oct 10, 2003 12:42 am
Location: Tampa,FL,USA
Contact:

Post by DriakTravo »

Awsome! . . .

Althogh it would be much better with a pause:

Code: Select all

InitKeyboard()
InitSprite()
; --------------------------------------------------------------------- 
; 
; CaptureFrame(#Movie, #Image, #Memory) 
; 
; Copies the current frame of the #Movie to the #Image. 
; The movie must pe paused when calling this, otherwise it will fail. 
; The #Image should be the same size as the movie (MovieWidth(), MovieHeight()) 
; You must provide an unused memory bank number for temporary storage. 
; 
; --------------------------------------------------------------------- 

Structure PB_StructureMovie 
  Movie.l          ; IGraphBuilder DirectShow pointer 
  MediaControl.l   ; IMediaControl DirectShow pointer 
  MediaEvent.l     ; IMediaEventEx DirectShow pointer 
  Window.l         ; IVideoWindow DirectShow pointer 
  Audio.l          ; IBasicAudio DirectShow pointer 
  Video.l          ; IBasicVideo DirectShow pointer 
  MediaSeeking.l   ; IMediaSeeking DirectShow pointer 
  State.l          ; State of the movie 
EndStructure 

Procedure.l CaptureFrame(MovieNumber, ImageNumber, MemoryBank) 
  Protected *Movie.PB_StructureMovie, *Video.IBasicVideo, *Window.IVideoWindow 
  Protected *ImageData.BITMAPINFOHEADER, DataSize, Parent, Result 
  
  !extrn _PB_Movie_ObjectsArea 
  !mov eax, [_PB_Movie_ObjectsArea] 
  !mov [esp+12], eax 
  
  *Movie  + MovieNumber * SizeOf(PB_StructureMovie) 
  *Window = *Movie\Window 
  *Video  = *Movie\Video 
  
  Result  = 0  
  
  If *Video\GetCurrentImage(@DataSize, 0) = #S_OK 
  
    *ImageData = AllocateMemory(MemoryBank, DataSize) 
    If *ImageData 
    
      If *Video\GetCurrentImage(@DataSize, *ImageData) = #S_OK 
            
        Result = SetDIBits_(0, UseImage(ImageNumber), 0, *ImageData\biHeight, *ImageData+*ImageData\biSize, *ImageData, #DIB_RGB_COLORS) 
      
      EndIf 
    
      FreeMemory(MemoryBank) 
    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 
#Gadget_Tracker = 2 

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

    If OpenWindow(#Window, 0, 0, Width , Height*2 + 35, #PB_Window_SystemMenu|#PB_Window_Screencentered|#PB_Window_Invisible, "Frame Capture") 
    If OpenWindowedScreen(WindowID(),0,0,1,1,0,0,0)
      If CreateGadgetList(WindowID()) 
      
        ButtonGadget(#Gadget_Capture,0, Height+5, 100, 25, "Capture Frame") 
        TrackBarGadget(#Gadget_Tracker,110,Height+5,Width-110,20,0,Length/FPS,#PB_TrackBar_Ticks) 
        ImageGadget(#Gadget_Image, 0, Height+35, Width, Height, UseImage(#Image)) 
        
        PlayMovie(#Movie, WindowID()) 
        
        HideWindow(#Window, 0) 
        
        Repeat 
          Event = WindowEvent() 
          
          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Capture 
            PauseMovie() 
            CaptureFrame(#Movie, #Image, #Memory) 
            SetGadgetState(#Gadget_Image, UseImage(#Image))                        
            ResumeMovie()          
          EndIf 

          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Tracker 
           ; PauseMovie() 
            MovieSeek(GetGadgetState(#Gadget_Tracker)*FPS) 
           ; ResumeMovie()          
          EndIf 

          If Event = 0 
            Delay(1) 
          EndIf 
          
          ExamineKeyboard()
          If KeyboardReleased(#PB_Key_Space)
            If Pause = 0
              Pause = 1
            Else
              Pause = 0
            EndIf
          EndIf
          
          If Pause = 1
            PauseMovie()
          ElseIf Pause = 0
            ResumeMovie()
          EndIf
          
        Until Event = #PB_EventCloseWindow 
        
      EndIf        
      EndIf
    EndIf        
    
  EndIf 
  
EndIf 

End 
(Press space to pause)
techjunkie
Addict
Addict
Posts: 1126
Joined: Wed Oct 15, 2003 12:40 am
Location: Sweden
Contact:

Post by techjunkie »

Ehhh... It doesn't work with all codecs on my machine, for example DivX codec. I can watch the movie but don't grab a frame.
Image
(\__/)
(='.'=) This is Bunny. Copy and paste Bunny into your
(")_(") signature to help him gain world domination.
Post Reply