Bitmap eines Frames aus einer MPEG-Datei fischen

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Bitmap eines Frames aus einer MPEG-Datei fischen

Beitrag 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 !
Benutzeravatar
Falko
Admin
Beiträge: 3535
Registriert: 29.08.2004 11:27
Computerausstattung: PC: MSI-Z590-GC; 32GB-DDR4, ICore9; 2TB M2 + 2x3TB-SATA2 HDD; Intel ICore9 @ 3600MHZ (Win11 Pro. 64-Bit),
Acer Aspire E15 (Win11 Home X64). Purebasic LTS 6.11b1
HP255G8 Notebook @AMD Ryzen 5 5500U with Radeon Graphics 2.10 GHz 3.4GHz, 32GB_RAM, 3TB_SSD (Win11 Pro 64-Bit)
Kontaktdaten:

Beitrag 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.
Bild
Win11 Pro 64-Bit, PB_6.11b1
Benutzeravatar
Falko
Admin
Beiträge: 3535
Registriert: 29.08.2004 11:27
Computerausstattung: PC: MSI-Z590-GC; 32GB-DDR4, ICore9; 2TB M2 + 2x3TB-SATA2 HDD; Intel ICore9 @ 3600MHZ (Win11 Pro. 64-Bit),
Acer Aspire E15 (Win11 Home X64). Purebasic LTS 6.11b1
HP255G8 Notebook @AMD Ryzen 5 5500U with Radeon Graphics 2.10 GHz 3.4GHz, 32GB_RAM, 3TB_SSD (Win11 Pro 64-Bit)
Kontaktdaten:

Beitrag 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 
Bild
Win11 Pro 64-Bit, PB_6.11b1
Benutzeravatar
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Beitrag 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
Benutzeravatar
Rings
Beiträge: 977
Registriert: 29.08.2004 08:48

Beitrag 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...
Rings hat geschrieben:ziert sich nich beim zitieren
Benutzeravatar
Falko
Admin
Beiträge: 3535
Registriert: 29.08.2004 11:27
Computerausstattung: PC: MSI-Z590-GC; 32GB-DDR4, ICore9; 2TB M2 + 2x3TB-SATA2 HDD; Intel ICore9 @ 3600MHZ (Win11 Pro. 64-Bit),
Acer Aspire E15 (Win11 Home X64). Purebasic LTS 6.11b1
HP255G8 Notebook @AMD Ryzen 5 5500U with Radeon Graphics 2.10 GHz 3.4GHz, 32GB_RAM, 3TB_SSD (Win11 Pro 64-Bit)
Kontaktdaten:

Beitrag 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 
Bild
Win11 Pro 64-Bit, PB_6.11b1
Benutzeravatar
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Beitrag 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" ?
Benutzeravatar
jear
Beiträge: 288
Registriert: 17.10.2004 01:59
Wohnort: Ammerland

Beitrag 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
Benutzeravatar
grapy
Beiträge: 108
Registriert: 09.09.2004 09:05

Beitrag 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
Benutzeravatar
Andre
PureBasic Team
Beiträge: 1765
Registriert: 11.09.2004 16:35
Computerausstattung: MacBook Core2Duo mit MacOS 10.6.8
Lenovo Y50 i7 mit Windows 10
Wohnort: Saxony / Deutscheinsiedel
Kontaktdaten:

Beitrag 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)
Bye,
...André
(PureBasicTeam::Docs - PureArea.net | Bestellen:: PureBasic | PureVisionXP)
Antworten