Page 1 of 2

Capture a movie frame

Posted: Mon Feb 02, 2004 2:07 am
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

Posted: Mon Feb 02, 2004 7:31 am
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?

Posted: Mon Feb 02, 2004 8:55 am
by Dare2
Works on win2k.

Thanks, freak.

Posted: Mon Feb 02, 2004 2:19 pm
by J. Baker
Works great on WIN XP (Home) here. :D

Posted: Mon Feb 02, 2004 4:06 pm
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

Posted: Sun Feb 08, 2004 3:54 pm
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?

Posted: Sun Feb 08, 2004 4:16 pm
by freak
have a look at PB's RenderMovieFrame() function.

Timo

Posted: Sun Feb 08, 2004 4:49 pm
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.

Posted: Sun Feb 08, 2004 5:08 pm
by Justin
Nice one, thanks freak

Posted: Sun Feb 08, 2004 10:20 pm
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

Posted: Mon Feb 09, 2004 11:28 pm
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.

,,

Posted: Wed Feb 11, 2004 10:36 pm
by NoahPhense
Jon wrote:EDIT : Ok, updated to PB 8.1 It worked! Weird?
8.1 ?

[edit]

ah.. 3.81 ;)


- np

Posted: Thu Feb 12, 2004 2:03 am
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

Posted: Thu Feb 12, 2004 2:49 am
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)

Posted: Thu Feb 12, 2004 9:02 am
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.