Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Video aus (Teil-)Stream abspielen (https://www.delphipraxis.net/148279-video-aus-teil-stream-abspielen.html)

dominikkv 26. Feb 2010 15:23


Video aus (Teil-)Stream abspielen
 
Hallo,

ich suche eine Möglichkeit, ein Video aus einem Stream heraus abzuspielen. Ich habe eine Datei, in der mehrere Videos hintereinander angehängt sind, die unterschiedliche Formate und Codexe haben, aus der ich nun zB Video Nr 3 abspielen will.

Erste Frage: gibt es Komponenten, die ein Abspielen aus einem Stream heraus ermöglichen? Ein Vor-/Zurückspulen bzw an eine bestimmte Position springen sollten möglich sein.

Zweite Frage: mit welchem Stream mache ich das am besten? Weil ein TFileStream geht ja über die komplette Datei, ich will aber nur einen Teil davon als eigenständigen Stream betrachten, und ein TMemoryStream kopiert ja den Inhalt in den Speicher, was ich bei Videos und den damit verbundenen Dantenmengen vermeiden will.

Vielen Dank für eure Antworten
Dominik

himitsu 26. Feb 2010 16:06

Re: Video aus (Teil-)Stream abspielen
 
Mal was Billiges:
Delphi-Quellcode:
Type TStreamPart = Class(TStream)
  Private
    _Stream: TStream;
    _Offset, _Len: Int64;
  Protected
    Function GetSize: Int64;                         Override;
    Procedure SetSize(NewSize: LongInt);    Overload; Override;
    Procedure SetSize(Const NewSize: Int64); Overload; Override;
  Public
    Constructor Create(Stream: TStream; Const Start, Length: Int64);
    Function Read(Var Buffer; Count: LongInt): LongInt;                      Override;
    Function Write(Const Buffer; Count: LongInt): LongInt;                   Override;
    Function Seek(Offset: LongInt; Origin: Word): LongInt;         Overload; Override;
    Function Seek(Const Offset: Int64; Origin: TSeekOrigin): Int64; Overload; Override;
  End;

Function TStreamPart.GetSize: Int64;
  Begin
    Result := Min(Max(_Stream.Size - _Offset, 0), _Len);
  End;

Procedure TStreamPart.SetSize(NewSize: LongInt);
  Begin
    SetSize(Int64(NewSize));
  End;

Procedure TStreamPart.SetSize(Const NewSize: Int64);
  Begin
    Raise Exception.Create('read only');
  End;

Constructor TStreamPart.Create(Stream: TStream; Const Start, Length: Int64);
  Begin
    _Stream := Stream;
    _Offset := Start;
    _Len   := Length;
  End;

Function TStreamPart.Read(Var Buffer; Count: LongInt): LongInt;
  Begin
    Result := _Stream.Read(Buffer, Min(Int64(Count), Size - Position));
  End;

Function TStreamPart.Write(Const Buffer; Count: LongInt): LongInt;
  Begin
    Result := _Stream.Write(Buffer, Min(Int64(Count), Size - Position));
  End;

Function TStreamPart.Seek(Offset: LongInt; Origin: Word): LongInt;
  Begin
    Result := _Stream.Seek(LongInt(Min(Max(Int64(Offset), 0) + _Offset, MaxInt)), Origin);
  End;

Function TStreamPart.Seek(Const Offset: Int64; Origin: TSeekOrigin): Int64;
  Begin
    Result := _Stream.Seek(Max(Offset, 0) + _Offset, Origin);
  End;
Delphi-Quellcode:
Var X: TStream;

X := TStreamPart.Create(MyFileStream, 1000, 10);
Und schon hat man einen neuen Stream, welcher einen Teil eines anderen Streams liefert.
In diesem Fall z.B. die Bytes 1000 bis 1009 von einem FileStream.
(falls ich mich jetzt nicht verdacht hab)

dominikkv 26. Feb 2010 17:40

Re: Video aus (Teil-)Stream abspielen
 
Hallo himitsu,

vielen Dank für deine Hilfe. Ich habe mir den Code von dir mal genauer angeschaut und glaube, bei dir hat sich ein Fehler eingeschlichen. Für .Read und .Write benutzt du .Size und .Position der Basisklasse, außerdem wird bei .Seek der Offset falsch berechnet.
Delphi-Quellcode:
Function TStreamPart.Read(Var Buffer; Count: LongInt): LongInt;
  Begin
    Result := _Stream.Read(Buffer, Min(Int64(Count), _Len - (_Stream.Position - _Offset)));
  End;

Function TStreamPart.Write(Const Buffer; Count: LongInt): LongInt;
  Begin
    Result := _Stream.Write(Buffer, Min(Int64(Count), _Len - (_Stream.Position - _Offset)));
  End;

Function TStreamPart.Seek(Offset: LongInt; Origin: Word): LongInt;
  Begin
    case TSeekOrigin(Origin) of
      soBeginning: Result := _Stream.Seek(LongInt(Min(Max(Int64(Offset), 0) + _Offset, MaxInt)), Origin);
      soCurrent:  Result := _Stream.Seek(LongInt(Min(Max(Int64(Offset), 0)         , MaxInt)), Origin);
      soEnd:      Result := _Stream.Seek(LongInt(Min(Max(Int64(Offset), 0) + _Stream.Size - (_Offset + _Len), MaxInt)), Origin);
    end;
  End;

Function TStreamPart.Seek(Const Offset: Int64; Origin: TSeekOrigin): Int64;
  Begin
    case Origin of
      soBeginning: Result := _Stream.Seek(LongInt(Min(Max(Offset, 0) + _Offset, MaxInt)), Origin);
      soCurrent:  Result := _Stream.Seek(LongInt(Min(Max(Offset, 0)         , MaxInt)), Origin);
      soEnd:      Result := _Stream.Seek(LongInt(Min(Max(Offset, 0) + _Stream.Size - (_Offset + _Len), MaxInt)), Origin);
    end;
  End;
So müsste das stimmen, oder? :gruebel:

Und hat jemand eine Idee, wie ich diesen Stream nun wiedergeben kann?

himitsu 26. Feb 2010 20:38

Re: Video aus (Teil-)Stream abspielen
 
Mit dem Seek hast du Recht, obwohl ich jetzt auf die Schnelle nicht sagen kann, ob es so nun stimmt,
aber ich hat so das Gefühlt, daß dem leider nicht so ist.

Beim Read und Write war es schon richtig, daß die Grenzen des abgeleiteten Streams genutzt wurden.


Dieses Wochenende hab ich kaum Zeit, so werd' ich wohl erst nächste Woche nochmal genauer nachsehn können.

Zitat:

Zitat von dominikkv
Und hat jemand eine Idee, wie ich diesen Stream nun wiedergeben kann?

theoretisch mit einer Mediaplayer-Komponente, welche über einen Stream abspielen kann.

Luckie 26. Feb 2010 20:58

Re: Video aus (Teil-)Stream abspielen
 
Aber ob das bei einem Video Stream klappt, halte ich für fraglich, da die Headerdaten fehlen. Er müsste dann noch die passenden Headerdaten für den Teilstream generieren, um ihn abspielen zu können.

dominikkv 26. Feb 2010 21:46

Re: Video aus (Teil-)Stream abspielen
 
Danke, himitsu, ich werde das mal testen. Aber .Position und .Size von der Basisklasse wird doch überhaupt nicht initialisiert, oder verstehe ich das falsch? Wir leiten ja nur von TStream ab, damit sich die Klasse "Stream" nennen darf.
Zitat:

Zitat von Luckie
Aber ob das bei einem Video Stream klappt, halte ich für fraglich, da die Headerdaten fehlen. Er müsste dann noch die passenden Headerdaten für den Teilstream generieren, um ihn abspielen zu können.

Hmm die Headerinformationen müssten ja da sein, weil die große Datei aus mehreren Einzelvideos besteht, die, wenn man sie wieder einzeln als Datei speichern würde, normal abspielbar sind. Oder bekommt der Mediaplayer (MPlayer.TMediaPlayer?) die Headerinformationen nicht aus dem Stream?

Luckie 26. Feb 2010 21:52

Re: Video aus (Teil-)Stream abspielen
 
Ach so, das ist natürlich was anderes. Das sind schon aneinander gehangene einzelne Videos?

himitsu 27. Feb 2010 06:22

Re: Video aus (Teil-)Stream abspielen
 
Size kommt über GetSize, welches auf dem QuellStream die Größe ausließt und begrenzt.

Position wird über Seek berechnet ... dort führt der TStream einen Seek(0) auf die aktuelle Position aus und Seek liefert ja dann als Ergebnis die Position, auf welche verschoben wurde, also die aktuelle Position, da ja nicht wirklich verschoben wurde.

himitsu 1. Mär 2010 09:03

Re: Video aus (Teil-)Stream abspielen
 
Soo, ich glaub jetzt dürfte es soweit stimmen.

'ne Positionsprüfung bei Read- und Write-Zugriffen gibt's nun,
denn der Zugriff über den "Hauptstream" existiert ja weiterhin, so daß von dort aus auch was geändert werden könnte, welches so jetzt abgefangen wird.

In .Create wird der Hauptstream absichtlich nicht geprüft, denn Assigned(Stream) würde nur den einen Fall eines NIL-Pointers abgefangen, aber eine ungültige Objektvariable kann einfach nicht mit Sicherheit erkannt werden.

Die Streamgröße kann nun auch geändert werden, aber nur wenn der TeilStream am Ende des Hauptstreams liegt und somit nichts Nachfolgendes gelöscht werden könnte.

Und das Wichtigste: Das fehlerhafte Seek/Position wurde überarbeitet.

Delphi-Quellcode:
Unit StreamPart;

Interface
  Uses SysUtils, Classes, Math;

  Type TStreamPart = Class(TStream)
    Private
      _Stream: TStream;
      _Offset, _MaxLen: Int64;
    Protected
      Function GetSize: Int64;                         Override;
      Procedure SetSize(NewSize: LongInt);    Overload; Override;
      Procedure SetSize(Const NewSize: Int64); Overload; Override;
    Public
      Constructor Create(Stream: TStream; Const Offset, MaxLength: Int64);
      Function Read(Var Buffer; Count: LongInt): LongInt;                      Override;
      Function Write(Const Buffer; Count: LongInt): LongInt;                   Override;
      Function Seek(Offset: LongInt; Origin: Word): LongInt;         Overload; Override;
      Function Seek(Const Offset: Int64; Origin: TSeekOrigin): Int64; Overload; Override;
    End;

Implementation

  Function TStreamPart.GetSize: Int64;
    Begin
      Result := Min(Max(_Stream.Size - _Offset, 0), _MaxLen);
    End;

  Procedure TStreamPart.SetSize(NewSize: LongInt);
    Begin
      SetSize(Int64(NewSize));
    End;

  Procedure TStreamPart.SetSize(Const NewSize: Int64);
    Begin
      If _Stream.Size <= _Offset + _MaxLen Then
        _Stream.Size := _Offset + NewSize
      Else Raise Exception.Create('The StreamPart must lie at the '
          + 'end of the main stream to change its size to be allowed.');
    End;

  Constructor TStreamPart.Create(Stream: TStream; Const Offset, MaxLength: Int64);
    Begin
      _Stream := Stream;
      _Offset := Offset;
      _MaxLen := MaxLength;
    End;

  Function TStreamPart.Read(Var Buffer; Count: LongInt): LongInt;
    Var Pos: Int64;

    Begin
      Pos := _Stream.Position;
      If (Pos < _Offset) or (Pos >= _Offset + _MaxLen) Then
        Raise Exception.Create('The .Position is out of range.');
      Result := _Stream.Read(Buffer, Min(Int64(Count), _Stream.Size - Pos));
    End;

  Function TStreamPart.Write(Const Buffer; Count: LongInt): LongInt;
    Var Pos: Int64;

    Begin
      Pos := _Stream.Position;
      If (Pos < _Offset) or (Pos >= _Offset + _MaxLen) Then
        Raise Exception.Create('The .Position is out of range.');
      Result := _Stream.Write(Buffer, Min(Int64(Count), _Stream.Size - Pos));
    End;

  Function TStreamPart.Seek(Offset: LongInt; Origin: Word): LongInt;
    Begin
      Result := Seek(Int64(Offset), TSeekOrigin(Origin));
    End;

  Function TStreamPart.Seek(Const Offset: Int64; Origin: TSeekOrigin): Int64;
    Begin
      Case Origin of
        soEnd:    Offset := _Offset + _MaxLen + Offset;
        soCurrent: Offset := _Stream.Position + Offset;
        Else      Offset := _Offset          + Offset;
      End;
      Result := _Stream.Seek(Min(Max(Offset, _Offset), _Offset + _MaxLen), soBeginning);
    End;

End.

dominikkv 4. Mär 2010 19:22

Re: Video aus (Teil-)Stream abspielen
 
Wow, danke, himitsu! Funktioniert wie ne Eins :)
Zitat:

Zitat von Luckie
Ach so, das ist natürlich was anderes. Das sind schon aneinander gehangene einzelne Videos?

Genau, nichts daran verändert.

Jetzt brauche ich nur noch eine Möglichkeit diesen Stream wiederzugeben. Als Erweiterung zur Bass.dll gibts die BassVideo.dll (klick), damit sollte das theoretisch gehen, es will aber nicht so recht :|
Die Demos, die dabei sind, spielen die Videos einfach nicht ab... Anzeigebereich bleibt einfach schwarz. Kann das jemand mit einer Delphi-Version unter 2009 bestätigen?

Ich habe mir schon überlegt, selbst über DirectX (DirectShow) die Videos wiederzugeben... wenn ich das hinbekomme :mrgreen:

Kennt jemand sonst ne Komponente die Videos aus Streams wiedergeben kann?


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:32 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz