首页 > 其他分享 >跨平台xamarin.Android 开发之 :适配各架构(X86_64 、 X86、arm64-v8a、 armeabi-v7a )FFmpeg 解码

跨平台xamarin.Android 开发之 :适配各架构(X86_64 、 X86、arm64-v8a、 armeabi-v7a )FFmpeg 解码

时间:2023-08-13 17:11:05浏览次数:45  
标签:pDecodecContext X86 decodedFrameSize 适配 跨平台 解码器 av new FFmpeg

此代码的编写花费了脑细胞:在每次解码开启解码器到只需要一次解码器的开启优化

前提:编译好FFMpeg 的各平台的动态库

Windows 、Android (X86_64 、 X86、arm64-v8a、 armeabi-v7a ) 解码 相对编码要简单一些,因为不涉及到AVFrame 取指转换

解码包括:创建解码器、解码、释放解码器

using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace FFmpegAnalyzer
{
    /// <summary>
    /// 解码器
    /// </summary>
    internal unsafe class FFmpegDecoder
    {
        /// <param name="decodedFrameSize">解码后一帧数据的大小</param>
        /// <param name="isRgb">Rgb数据</param>
        public FFmpegDecoder(Size decodedFrameSize, bool isRgb )
        {
            _decodedFrameSize = decodedFrameSize;
            _isRgb = isRgb;
        }

        /// <summary>
        /// 创建解码器
        /// </summary>
        /// <param name="codecFormat">解码格式</param>
        public void CreateDecoder(AVCodecID codecFormat)
        {
            var originPixelFormat = AVPixelFormat.AV_PIX_FMT_YUV420P;
            var destinationPixelFormat = _isRgb ? AVPixelFormat.AV_PIX_FMT_RGB24 : AVPixelFormat.AV_PIX_FMT_BGRA; 

            //获取解码器
            _pDecodec = FFmpeg.avcodec_find_decoder(codecFormat);
            if (_pDecodec == null) throw new InvalidOperationException("Codec not found.");

            _pDecodecContext = FFmpeg.avcodec_alloc_context3(_pDecodec);
            _pDecodecContext->width = _decodedFrameSize.Width;
            _pDecodecContext->height = _decodedFrameSize.Height;
            _pDecodecContext->time_base = new AVRational { num = 1, den = 30 };
            _pDecodecContext->pix_fmt = originPixelFormat;
            _pDecodecContext->framerate = new AVRational { num = 30, den = 1 };                        
            _pDecodecContext->gop_size = 30;
            // 设置预测算法
            _pDecodecContext->flags |= FFmpeg.AV_CODEC_FLAG_PSNR;
            _pDecodecContext->flags2 |= FFmpeg.AV_CODEC_FLAG2_FAST;
            _pDecodecContext->max_b_frames = 0;
            FFmpeg.av_opt_set(_pDecodecContext->priv_data, "preset", "veryfast", 0);
            FFmpeg.av_opt_set(_pDecodecContext->priv_data, "tune", "zerolatency", 0);
            //打开解码器
            FFmpeg.avcodec_open2(_pDecodecContext, _pDecodec, null).ThrowExceptionIfError();
            _pConvertContext = FFmpeg.sws_getContext(_decodedFrameSize.Width, _decodedFrameSize.Height,
                originPixelFormat, _decodedFrameSize.Width, _decodedFrameSize.Height,
                destinationPixelFormat,
               FFmpeg.SWS_FAST_BILINEAR,
                null, null, null);
            if (_pConvertContext == null)
                throw new ApplicationException("Could not initialize the conversion context.");

            var convertedFrameBufferSize = FFmpeg.av_image_get_buffer_size(destinationPixelFormat, _decodedFrameSize.Width, _decodedFrameSize.Height, 4);
            _convertedFrameBufferPtr = Marshal.AllocHGlobal(convertedFrameBufferSize);
            _dstData = new BytePtr4();
            _dstLineSize = new Int4();

            FFmpeg.av_image_fill_arrays(ref _dstData, ref _dstLineSize, (byte*)_convertedFrameBufferPtr, destinationPixelFormat,
                _decodedFrameSize.Width, _decodedFrameSize.Height, 4);
            _isCodecRunning = true;
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="frameBytes"></param>
        /// <returns></returns>
        public  byte[] DecodeFrames(byte[] frameBytes)
        {
            if (!_isCodecRunning)
            {
                throw new InvalidOperationException("解码器未运行!");
            }
            var waitDecodePacket = FFmpeg.av_packet_alloc();
            var waitDecoderFrame = FFmpeg.av_frame_alloc();
            FFmpeg.av_frame_unref(waitDecoderFrame);
            fixed (byte* waitDecodeData = frameBytes)
            {
                waitDecodePacket->data = waitDecodeData;
                waitDecodePacket->size = frameBytes.Length;
                FFmpeg.av_frame_unref(waitDecoderFrame);
                try
                {
                    int error;
                    do
                    {
                        FFmpeg.avcodec_send_packet(_pDecodecContext, waitDecodePacket);
                        error = FFmpeg.avcodec_receive_frame(_pDecodecContext, waitDecoderFrame);
                    } while (error == FFmpeg.AVERROR(FFmpeg.EAGAIN));
                }
                finally
                {
                    FFmpeg.av_packet_unref(waitDecodePacket);
                }

                var decodeAfterFrame = ConvertToRgb(waitDecoderFrame, _decodedFrameSize);

                var length = _isRgb ? decodeAfterFrame.height * decodeAfterFrame.width * 3
                    : decodeAfterFrame.height * decodeAfterFrame.width * 4;

                byte[] buffer = new byte[length];
                Marshal.Copy((IntPtr)decodeAfterFrame.data[0], buffer, 0, buffer.Length);
                return buffer;
            }
        }

        /// <summary>
        /// 释放
        /// </summary>
        public  void Dispose()
        {
            _isCodecRunning = false;
            //释放解码器
            FFmpeg.avcodec_close(_pDecodecContext);
            FFmpeg.av_free(_pDecodecContext);
            //释放转换器
            Marshal.FreeHGlobal(_convertedFrameBufferPtr);
            FFmpeg.sws_freeContext(_pConvertContext);
        }

        /// <summary>
        /// 转换成Rgb
        /// </summary>
        /// <param name="waitDecoderFrame"></param>
        /// <param name="detSize">变化后目标大小</param>
        /// <returns></returns>
        private AVFrame ConvertToRgb(AVFrame* waitDecoderFrame, Size detSize)
        {
            FFmpeg.sws_scale(_pConvertContext, waitDecoderFrame->data, waitDecoderFrame->linesize, 0, waitDecoderFrame->height, _dstData, _dstLineSize).ThrowExceptionIfError(); 
            var decodeAfterData = new BytePtr8();
            decodeAfterData.UpdateFrom(_dstData);
            var lineSize = new Int8();
            lineSize.UpdateFrom(_dstLineSize);

            FFmpeg.av_frame_unref(waitDecoderFrame);
            return new AVFrame
            {
                data = decodeAfterData,
                linesize = lineSize,
                width = detSize.Width,
                height = detSize.Height
            };
        }

        //解码器
        private AVCodec* _pDecodec;
        private AVCodecContext* _pDecodecContext;
        //转换缓存区
        private IntPtr _convertedFrameBufferPtr;
        private BytePtr4 _dstData;
        private Int4 _dstLineSize;
        //格式转换
        private SwsContext* _pConvertContext;

        //解码后一帧数据的大小
        private Size _decodedFrameSize;

        //三通道
        private readonly bool _isRgb;
        //解码器正在运行
        private bool _isCodecRunning;
    }
}

 

标签:pDecodecContext,X86,decodedFrameSize,适配,跨平台,解码器,av,new,FFmpeg
From: https://www.cnblogs.com/terryK/p/17626833.html

相关文章

  • 跨平台xamarin.Android 开发之 :适配各架构(X86_64 、 X86、arm64-v8a、 armeabi-v7a
    从事Windows,项目探索预研跨平台开发,对Android只知道有X86_64、X86、arm64-v8a、  armeabi-v7a这么个东西其他空白。编译入手采用Xamarin.Android开发。通过摸索。在Xamarin.Android中使用FFmpeg编解码,需要获取源码编译成对应Android架构的so动态库,如何编译不在此处讨论,稍......
  • 对话音视频牛哥:如何设计功能齐全的跨平台低延迟RTMP播放器
    开发背景2015年,我们在做移动单兵应急指挥项目的时候,推送端采用了RTMP方案,这在当时算是介入RTMP比较早的了,RTMP推送模块做好以后,我们找了市面上VLC还有Vitamio,来测试整体延迟,实际效果真的不尽人意,大家知道,应急指挥系统,除了稳定性外,对延迟有很高的要求,几秒钟(>3-5秒)的延迟,是我们接受不......
  • 小程序生成App:可跨平台开发的移动应用开发框架
    小程序生成App可以成为一种轻量低门槛的开发App的方式,但是需要根据具体情况进行选择。如果应用需要处理大量数据或需要进行复杂计算,或者需要实现原生特有的功能或交互效果,可能需要选择其他开发方式。在文章开始之前,我们看看目前市面上比较容易上手、低门槛开发App的框架和方式Rea......
  • Axure 9无法设置移动设备适配的解决方法
    虽然Axure9做了很多移动端的适配工作,移动端的设计体验也好了很多,但是只是在PC端预览有移动端的效果,在移动设备上浏览却没有自动适应屏幕的效果,而且也没有设置移动端的适配的入口而在Axure8中是有这个设置面板的,设置非常方便,如下:   那如何才能设置移动端适配呢,还是有办......
  • Android屏幕适配全攻略(最权威的官方适配指导)
    Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因、重要概念、解决方案及最佳实践,我相信如果你能认真的学习本文,对于Android的屏幕适配,你将有所收获! Android屏幕适配出现的原因重要概念屏幕尺寸屏幕......
  • vivo 场景下的 H5无障碍适配实践
    作者:vivo互联网前端团队-ZhangLi、DaiWenkuan随着信息无障碍的建设越来越受重视,开发人员在无障碍适配中也遇到了越来越多的挑战。本文是笔者在vivo开发H5项目做无障碍适配的实践总结。本文主要介绍了在前端项目中常用的无障碍手势和无障碍属性,并且结合具体的开发案例为开发......
  • 在x86构架中 时间片是如何实现的
     在x86架构(通常指的是基于Intel的x86指令集架构)中,时间片的实现通常涉及操作系统、中断机制和时钟硬件。以下是在x86架构中实现时间片的一般步骤:硬件时钟:在x86架构中,存在一个硬件时钟,通常称为计时器或时钟中断。这个时钟以固定的频率发出中断,比如每秒100次中......
  • warning: /var/cache/yum/x86_64/7/mysql57-community/packages/mysql-community-comm
    问题描述在我正确地安装好mysql包之后,再安装mysql,就出现了这么一个问题:就去疯狂百度找解决问题的方法!!!问题解决经过查找资料,才发现,原来是有GPG验证检查,只需要禁止GPG验证检查就行啦!也就是在安装mysql的语句后面,加上这样一个语句:--nogpgcheck总起来就是:yum-yinstallmys......
  • Android 12 适配之 "Android:exported"
    Android12适配之"Android:exported"将build.gradle中的targetSDKVersion和compileSdkVersion改为31,对应Android12build项目报错AppstargetingAndroid12andhigherarerequiredtospecifyanexplicitvalueforandroid:exportedwhenthecorrespondingc......
  • rider下载安装 一款.Net跨平台开发神器 安装指南
    rider下载安装一款.Net跨平台开发神器安装指南原文链接:https://baijiahao.baidu.com/s?id=1763943888509043020&wfr=spider&for=pc下载破解补丁后,解压得到,如下图:将上面图示的补丁的所属文件夹/jetbra复制电脑某个位置,小编这里放置到了默认的下载目录下(不推荐,自己选个D盘目......