Seite 1 von 2

Bitmap eines Frames aus einer MPEG-Datei fischen

Verfasst: 06.11.2004 01:47
von jear
Hi Leuts,

Für eine Anwendung zur Analyse von MPEG-Dateien, suche ich eine Möglichkeit, einen einzigen, ganz bestimmten Frame rendern zu lassen und als Bitmap in die Anwendung zu bekommen.

Kann mir da einer der Freaks einen Hinweis geben?

Dank im voraus !

Verfasst: 06.11.2004 18:38
von Falko
Ich würde dir ja gerne einen Beispielcode geben, aber im Moment habe ich damit
Probleme mit dem PB-source in Vergindung mit meiner GraKa HIS9200SE und PB3.92 den Beispielscode MovieSprite.pb ausführen zu können.

Aber Tips kann ich schon mal vermitteln :wink:
Was du dazu verwenden müßtest wären einmal aus der Hilfe
Movie
und weiter im PB-Ordner/sources stehen Beispiele wie PlayMovie.pb
und MovieSprite.pb.
Damit hast du dann eigendlich alles was du brauchst um eine MPEG anzuzeigen, pausieren, weiterspielen, frameposition auslesen und
über Spritefunktionen diese als BMP zu speichern.

MfG Falko

[Edit] Meine GraKa ist i.O. Aber der source spielt nicht alle Mpeg's ab.
Ein kleineres Format lief ohne Probleme.

Verfasst: 07.11.2004 05:05
von Falko
Habe jetzt einen Top-Source aus dem englischen Forum gefunden, womit
Bilder aus mpeg's kopiert werden können. Der Author dieses Source
ist 'Inner' aus dem englischen Forum. Zusätzlich habe ich dann den einfachsten Teil (Bilder mit Zählnr. abspeichern) hinzugefügt :mrgreen:
MfG Falko

Code: Alles auswählen

; --------------------------------------------------------------------- 
;Written by Inner from english forum at Date 25 Apr 2003
;
; 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(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 

Path$="MovieCap\"
If ExamineDirectory(0,Path$, "*.*") = 0 ; if Directory not existing?
CreateDirectory(Path$) 
EndIf
Nr=0
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)
            Nr=Nr+1
            SaveImage(#Image,Path$+"movie"+Str(Nr)+".bmp",#PB_ImagePlugin_BMP)
            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 

Verfasst: 08.11.2004 19:50
von jear
Danke Falko,

leider komme ich damit aber nicht an das gewünschte Ziel.

Es geht ja nicht darum, das Video abzuspielen, sondern nur einen ganz bestimmten Frame als Bitmap herauszuziehen.
Beim Abspielen kann man das Video ja nicht framegenau anhalten und selbst wenn man an dem gesuchten Frame startet, wie bekommt man den Stop vor dem nächsten Frame hin?

nach wie vor ratlos

jear

Verfasst: 08.11.2004 20:04
von Rings
@jear: hast du dir auch den source mal genau angekuckt ?

man kann mit MovieSeek(GetGadgetState(#Gadget_Tracker)*FPS)
exact einen Frame setzen.

darfst halt danach kein Resume machen.

ein bissl programmieren (wenn auch nicht viel) muss man schon...

Verfasst: 08.11.2004 20:57
von Falko
Rings hat's dir ja schon gesagt :mrgreen: . Ich war so frei und habe es noch etwas abgeändert. Wenn du es angehalten hast, kannst du unten auf den Schieber klicken und grob vor oder zurück gehen. Mit den Cursortasten für rechts/links kannst du die Bildposition feiner abstimmen.
[Edit] auch die Bildgröße läßt sich jetzt ändern :allright:
So, jetzt hier noch ein bischen zum Verstehen abgeändert :mrgreen:

Code: Alles auswählen

; --------------------------------------------------------------------- 
;Written by Inner from english forum at Date 25 Apr 2003
;----------------------------
;Falko:  Erweitert, sodas man auch größere Bilder
;machen kann. Ebenso eine Pause-, und Resumefunktion jetzt vorhanden.
;
; 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(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
#Image2 = 1 
#Memory = 0 
#Window = 0 

#Gadget_Capture = 0 
#Gadget_Image  = 1 
#Gadget_Tracker = 2 
#Pause=3
#Weiter=4
Path$="MovieCap\"
If ExamineDirectory(0,Path$, "*.*") = 0 ; if Directory not existing?
CreateDirectory(Path$) 
EndIf
Nr=0
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") 
        ButtonGadget(#Pause,130, Height+5, 100, 25, "Pause Frame")
        ButtonGadget(#Weiter,260, Height+5, 100, 25, "Resume Frame") 
        TrackBarGadget(#Gadget_Tracker,60,Height+30,Width-60,20,0,Length/FPS,#PB_TrackBar_Ticks) 
        ImageGadget(#Gadget_Image, 0, Height+50, Width, Height, UseImage(#Image)) 
        PlayMovie(#Movie, WindowID()) 
        HideWindow(#Window, 0) 
        
        Repeat 
          
          Event = WindowEvent() 
          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Capture 
              CaptureFrame(#Movie, #Image, #Memory)
              Nr=Nr+1
              SetGadgetState(#Gadget_Image, UseImage(#Image))                        
              CopyImage(#Image,#Image2)
              ResizeImage(#Image2, 640, 480); z.B hier eine größere BMP erstellen
              SaveImage(#Image2,Path$+"movie"+Str(Nr)+".bmp",#PB_ImagePlugin_BMP)
              ActivateGadget(#Gadget_Tracker)
              Event=#Gadget_Tracker
              
          EndIf 
          If Event = #PB_EventGadget And EventGadgetID() = #Pause 
            PauseMovie()
          ActivateGadget(#Gadget_Tracker)
          EndIf
          If Event = #PB_EventGadget And EventGadgetID() = #Weiter 
           ResumeMovie()
          ActivateGadget(#Gadget_Tracker)
          EndIf           
          If Event = #PB_EventGadget And EventGadgetID() = #Gadget_Tracker 
            Pos.l= GetGadgetState(#Gadget_Tracker)
            MovieSeek(GetGadgetState(#Gadget_Tracker)*FPS) 
            ResumeMovie() 
            PauseMovie()          
          EndIf 

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

End 

Verfasst: 09.11.2004 01:22
von jear
@Falko,
vielen Dank für Deine Mühen und Hilfen.

Wenn ich hier eine solche Frage stelle, habe ich mich schon eingehend mit der Thematik beschäftigt und rumgesucht. Wenn ich auch hier im Forum neu bin, neu im Geschäft bin ich nicht. Programmiere seit 1964 als man dazu noch die Nadeln durch oder um den Ringkern herum führte.

Wie gesagt es geht um MPEG-Dateien nicht um AVI's. Genauer noch um MPEG-2 Dateien. Mit MPEG-1 und AVI's läuft das Movie-Repertoire, mit MPEG-2's nicht. Das gilt auch für Deinen Code, zumindest auf meiner Maschine und die hat alles, was man zur Videobearbeitung braucht. Da es mir um MPEG-2's geht hatte ich das Ganze mit AVI's nicht probiert.

Das Problem liegt wohl an den Werten die PB-Movie zurückliefert.
Während das inhaltlich gleiche Video als DV-AVI korrekt mit der Länge von 1460 Frames und 25 FPS erkannt wird, hat die MPEG-2 die sagenhafte Länge von 499061030 Frames und es werden 17241 FPS zurückgemeldet.
Um welche Werte handelt es sich denn da ? Ist das ein Fehler in PB? Gibt es irgendwo einen Hinweis, wie man die gemeldeten Werte behandeln muss?

... oder habe ich nur nicht "genug programmiert" ?

Verfasst: 12.11.2004 01:03
von jear
So ganz aufgeben möchte ich noch nicht.
Daher die Frage an die Moderation oder wen immer es interessieren mag :

- Ist es den PB-Entwicklern bewusst, dass die Movie-Funktionen für MPEG2-Dateien falsche Werte liefern, sodass sich damit nichts anfangen lässt?
- Kann die Erscheinung vom genutzten PC abhängig sein? (Fahre DirectX 9 unter WinXP auf einem P4 3.0 HT)
- Hat jemand da draußen ähnliche oder andere Erfahrungen?

Danke

Verfasst: 13.11.2004 00:33
von grapy
Hi,

habe genau dasselbe Problem,
und meine mich erinnern zu können,
dass es schonmal funktioniert hat.

Am Besten beschreibst Du den Bug mal im englischen Forum.
Mal sehen was Fred(der PB-Entwickler) dazu meint. 8)


gruß :mrgreen: grapy

Verfasst: 13.11.2004 02:29
von Andre
grapy hat geschrieben:Am Besten beschreibst Du den Bug mal im englischen Forum. Mal sehen was Fred(der PB-Entwickler) dazu meint. 8)
Ist eine gute Möglichkeit, dann am besten aber inkl. einem kleinen Beispiel-Code + paar Links zu Beispiel-MPEG2-Dateien. :)

Alternativ auch hier und ich leite es dann weiter. 8)