Laden...

Bitmaps effizient(!) als Video abspielen

Erstellt von digi333 vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.986 Views
D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 9 Jahren
Bitmaps effizient(!) als Video abspielen

Ich suche eine schnellere Variante als meine aktuelle Lösung für das Problem ein C++ Pointer eines Bildes (FFMPEG) in einer Oberfläche (Panel) darzustellen. Vielleicht habt ihr ja eine Idee.

Meine Variante:


IntPtr bgr;
bgr = new IntPtr(ff.decodeNextFrame());
bgr = new IntPtr(ff.decodeNextFrame());
byte[] buffer = new byte[3*ff.height()*ff.width()];
Marshal.Copy(bgr, buffer, 0, buffer.Length);
Bitmap tmp_bmp = BGR2Bitmap(ff.height(), ff.width(), buffer);
DrawBitmap(tmp_bmp, panel_player);




public static Bitmap BGR2Bitmap(int height, int width, byte[] bgr)
        {
            if (height <= 0 || width <= 0)
                return new Bitmap(1, 1);

            Bitmap toreturn = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            Rectangle rect = new Rectangle(0, 0, width, height);
            System.Drawing.Imaging.BitmapData bmpData =
                toreturn.LockBits(rect, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            IntPtr ptr = bmpData.Scan0;
            int bytes = bmpData.Stride * toreturn.Height;

            if (bgr.Length == bytes)
            {
                Marshal.Copy(bgr, 0, ptr, bytes);
            }
            else
            {
                byte[] retBGR = new byte[bytes];
                for (int i = 0; i < toreturn.Height; i++)
                    Array.Copy(bgr, i * 3 * toreturn.Width, retBGR, i * bmpData.Stride, toreturn.Width * 3);
                System.Runtime.InteropServices.Marshal.Copy(retBGR, 0, ptr, bytes);

            }
            toreturn.UnlockBits(bmpData);
            return toreturn;
        }

        private void DrawBitmap(System.Drawing.Image bmp, Panel panel)
        {
            Graphics g = panel.CreateGraphics();
            g.DrawImage(bmp, 0, 0, panel_player.Width, panel_player.Height);
        }

C
2.121 Beiträge seit 2010
vor 9 Jahren

Ins Panel zeichnen sollte man während dem Paint Ereignis. Sonst ist das gezeichnete vielleicht schnell wieder weg.

Du führst zweimal Copy aus, einmal in BRR2Bitmap und einmal außerhalb. Da kann man eins streichen.

Wie viel LockBits und UnlockBits an Zeit braucht kann ich nicht sagen. Das könntest du mal messen und bei Bedarf nach Alternativen suchen.

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 9 Jahren

Gibt es etwas besseres als ein Panel? Gibt es etwas in DirectX? Die Geschwindigkeit des Panels ist abhängig von seiner Größe. Ein kleines Panel ist sehr schnell bei der Darstellung, aber ein großes Panel wird dagegen schon sehr langsam.

5.657 Beiträge seit 2006
vor 9 Jahren

Hi digi333,

natürlich dauert das Zeichnen eines größeren Bereichs länger als das Zeichnen in einen kleinen Bereich. Und natürlich gibt es auch eine Lösung, die DirectX verwendet, da brauchst du nur mal die Forensuche zu verwenden.

Aber wie schon gesagt, ist deine Herangehensweise alles andere als effizient. Das hat chillic schon erwähnt, und es steht auch so in [Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox).

Wenn du allerdings deine Frames jeweils aus einer Bitmap-Datei vom Laufwerk liest, dann kann es sein, daß das Lesen der Daten wesentlich länger dauert als das eigentliche Anzeigen. Da solltest du mal den Profiler verwenden, um herauszufinden, was eigentlich am längsten dauert, und wo man ansetzen müßte, um die Performance zu verbessern.

Christian

Weeks of programming can save you hours of planning

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 9 Jahren

Ich bin scheinbar bei DirectX etwas überfordert. Kann mir vielleicht jemand etwas helfen wie ich das Folgende in einer "Box" zeichne?


IntPtr bgr;
bgr = new IntPtr(ff.decodeNextFrame());
byte[] buffer = new byte[3*ff.height()*ff.width()];

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo digi333,

bevor wir jetzt in die Details von DirectX einsteigen, beherzige bitte zunächst den Rat, z.B. mit einem Profiler oder auch mit der Stopwatch-Klasse festzustellen, wo überhaupt der Flaschenhals ist. Ohne eine Analyse ist das nur Stochern im Nebel. Und wenn man fälschlich einen bereits ausreichend effizienten Code-Teil optimiert, ändern sich an der Performance-Problematik gar nichts.

herbivore

37 Beiträge seit 2014
vor 9 Jahren

Hallo zusammen,

für so etwas einfaches würde ich jetzt auch nicht gleich auf DirectX zurückgreifen. Das ist fast wie mit Kanonen auf Spatzen schießen.. Vor allem, wenn man von DX noch keine Ahnung hat, kann das schnell ein sehr aufwändiger Weg werden.

Nutze doch den Performance-Profiler in Visual Studio, um deine Engpässe zu finden.

Spontan würde ich folgendes Untersuchen:*Wie schnell bekommst du die Bilder überhaupt aus der Quelle (=> ist die überhaupt schnell genug) *Wie effizient ist deine Konvertierung in ein Bitmap? *Wie schnell ist dein Draw? (auch hier kannst du optimieren)

Viele Grüße
Roland

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 9 Jahren

Der "Engpass" liegt beim Zeichnen in das Panel. Bei mir also in der Funktion DrawPanel(). Ziehe ich es größer wird das Abspielen sehr langsam. Ein sehr kleines Panel (400x200) schafft 12ms pro Frame. Bei DVD-Auflösung werden es dann schon 50ms pro Frame.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo digi333,

das klingt so, als würde das (jeweils aktuelle) Bild auf die Größe des Panels skaliert gezeichnet. Das kostet natürlich viel mehr als ein unskaliertes Zeichnen. Möglicherweise wird beim Skalieren ein unnötig aufwändiges Verfahren benutzt.

Wie man bei einem Graphics-Objekt das Skalierungsverfahren einstellt, steht in [gelöst] Bitmap-Randproblem beim Verkleinern mit InterpolationMode.HighQualityBilinear. Versuchs mal mit InterpolationMode.NearestNeighbor.

herbivore