Есть ли другой способ экспортировать кадр в ffmpeg в texture2d? Мой код работает в Windows, но не в Linux - PullRequest
1 голос
/ 31 марта 2019

Звук работает в Linux так же, как и в Windows. Но видео - это просто черный экран, и когда я пытаюсь сохранить кадры в виде файлов BMP, все они были поврежденными / пустыми файлами. Я использую Ffmpeg.Autogen для взаимодействия с библиотеками. https://github.com/Ruslan-B/FFmpeg.AutoGen. Файл представляет собой VP8 и OGG в контейнере MKV. Хотя расширение почему-то AVI.

Я попытался немного поиграться с порядком кода. Я проверил, чтобы убедиться, что сборка Ffmpeg в Linux имеет VP8. Я искал в Интернете, но мне было трудно найти другой способ сделать то, что я делаю. Это должно способствовать проекту OpenVIII. Моя вилка-> https://github.com/Sebanisu/OpenVIII

Это просто подготавливает скейлер к изменению формата пикселя, иначе у людей будут синие лица.

        private void PrepareScaler()
        {

            if (MediaType != AVMediaType.AVMEDIA_TYPE_VIDEO)
            {
                return;
            }

            ScalerContext = ffmpeg.sws_getContext(
                Decoder.CodecContext->width, Decoder.CodecContext->height, Decoder.CodecContext->pix_fmt,
                Decoder.CodecContext->width, Decoder.CodecContext->height, AVPixelFormat.AV_PIX_FMT_RGBA,
                ffmpeg.SWS_ACCURATE_RND, null, null, null);
            Return = ffmpeg.sws_init_context(ScalerContext, null, null);

            CheckReturn();
        }

Преобразует кадр в BMP Я думаю, что это где проблема. Потому что я добавил к этому bitmap.save и получил пустые BMP.

public Bitmap FrameToBMP()
        {
            Bitmap bitmap = null;
            BitmapData bitmapData = null;

            try
            {
                bitmap = new Bitmap(Decoder.CodecContext->width, Decoder.CodecContext->height, PixelFormat.Format32bppArgb);
                AVPixelFormat v = Decoder.CodecContext->pix_fmt;

                // lock the bitmap
                bitmapData = bitmap.LockBits(new Rectangle(0, 0, Decoder.CodecContext->width, Decoder.CodecContext->height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

                byte* ptr = (byte*)(bitmapData.Scan0);

                byte*[] srcData = { ptr, null, null, null };
                int[] srcLinesize = { bitmapData.Stride, 0, 0, 0 };

                // convert video frame to the RGB bitmap
                ffmpeg.sws_scale(ScalerContext, Decoder.Frame->data, Decoder.Frame->linesize, 0, Decoder.CodecContext->height, srcData, srcLinesize); //sws_scale broken on linux?
            }
            finally
            {
                if (bitmap != null && bitmapData != null)
                {
                    bitmap.UnlockBits(bitmapData);
                }
            }
            return bitmap;

        }

После получения растрового изображения мы превращаем его в Texture2D, чтобы мы могли его нарисовать.

 public Texture2D FrameToTexture2D()
        {
            //Get Bitmap. there might be a way to skip this step.
            using (Bitmap frame = FrameToBMP())
            {
                //string filename = Path.Combine(Path.GetTempPath(), $"{Path.GetFileNameWithoutExtension(DecodedFileName)}_rawframe.{Decoder.CodecContext->frame_number}.bmp");

                //frame.Save(filename);
                BitmapData bmpdata = null;
                Texture2D frameTex = null;
                try
                {
                    //Create Texture
                    frameTex = new Texture2D(Memory.spriteBatch.GraphicsDevice, frame.Width, frame.Height, false, SurfaceFormat.Color); //GC will collect frameTex
                                                                                                                                        //Fill it with the bitmap.
                    bmpdata = frame.LockBits(new Rectangle(0, 0, frame.Width, frame.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);// System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    byte[] texBuffer = new byte[bmpdata.Width * bmpdata.Height * 4]; //GC here
                    Marshal.Copy(bmpdata.Scan0, texBuffer, 0, texBuffer.Length);

                    frameTex.SetData(texBuffer);


                }
                finally
                {
                    if (bmpdata != null)
                    {
                        frame.UnlockBits(bmpdata);
                    }
                }
                return frameTex;

            }
        }

Я могу опубликовать больше, если вы хотите, это почти все на моей вилке

Видео будет воспроизводиться так же, как в Windows. Столь же плавно, как 15 кадров в секунду. :)

1 Ответ

1 голос
/ 02 апреля 2019

В итоге я удалил растровую часть кода. И это сработало! Поэтому ранее я преобразовывал кадр в растровое изображение, и он копировал пиксели из растрового изображения в Texture2D. Я посмотрел поближе и понял, что могу пропустить этот шаг растрового изображения. Прошу прощения за недостаточно четкое разъяснение моего вопроса.

        /// <summary>
        /// Converts Frame to Texture
        /// </summary>
        /// <returns>Texture2D</returns>
        public Texture2D FrameToTexture2D()
        {
            Texture2D frameTex = new Texture2D(Memory.spriteBatch.GraphicsDevice, Decoder.CodecContext->width, Decoder.CodecContext->height, false, SurfaceFormat.Color);
            const int bpp = 4;
            byte[] texBuffer = new byte[Decoder.CodecContext->width * Decoder.CodecContext->height * bpp]; 
            fixed (byte* ptr = &texBuffer[0])
            {
                byte*[] srcData = { ptr, null, null, null };
                int[] srcLinesize = { Decoder.CodecContext->width * bpp, 0, 0, 0 };
                // convert video frame to the RGB data
                ffmpeg.sws_scale(ScalerContext, Decoder.Frame->data, Decoder.Frame->linesize, 0, Decoder.CodecContext->height, srcData, srcLinesize);
            }
            frameTex.SetData(texBuffer);
            return frameTex;
        }
...