如何从托管byte []缓冲区获取字节**

时间:2014-04-14 10:43:20

标签: c# ffmpeg pinvoke

我一直在使用FFmpeg.AutoGen https://github.com/Ruslan-B/FFmpeg.AutoGen包装器来解码我的H264视频,并取得了巨大的成功,现在必须添加AAC音频解码(之前我正在使用G711和NAudio)。 / p>

我使用avcodec_decode_audio4进行AAC流解码,但输出缓冲区或帧是浮点格式FLT,我需要它在S16中。为此,我发现使用swr_convert和FFmpeg的非托管示例.AutoGen确实具有此函数P / Invoked as;

[DllImport(SWRESAMPLE_LIBRARY, EntryPoint="swr_convert", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int swr_convert(SwrContext* s, byte** @out, int out_count, byte** @in, int in_count);

我的麻烦在于我无法找到将我的托管字节[]转换/修复/转换为字节**的成功方法,以将其作为目标缓冲区提供。

以前有人这样做吗?

我的非工作代码......

packet.ResetBuffer(m_avFrame->linesize[0]*2);

fixed (byte* pData = packet.Payload)
{
    byte** src = &m_avFrame->data_0;
    //byte** dst = *pData;
    IntPtr d = new IntPtr(pData);

    FFmpegInvoke.swr_convert(m_pConvertContext, (byte**)d.ToPointer(), packet.Length, src, (int)m_avFrame->linesize[0]);
}

感谢您的帮助。

干杯

戴夫

2 个答案:

答案 0 :(得分:2)

您要调用的功能在此处记录:http://www.ffmpeg.org/doxygen/2.0/swresample_8c.html#a81af226d8969df314222218c56396f6a

out_arg参数声明如下:

uint8_t* out_arg[SWR_CH_MAX]

这是一个长度为SWR_CH_MAX的字节数组数组。您的翻译会将其呈现为byte**,因此会强制您使用不安全的代码。我个人认为我会避免这种情况。我会声明这样的参数:

[MarshalAs(UnmanagedType.LPArray)]
IntPtr[] out_arg

像这样声明数组:

IntPtr[] out_arg = new IntPtr[channelCount];

我猜测CH中的SWR_CH_MAX是渠道的缩写。

然后你需要为输出缓冲区分配内存。我不确定你想怎么做。您可以为每个通道分配一个字节数组并固定这些数组以获取指向传递给本机代码的指针。这将是我的首选方法,因为在返回时,您的通道处于良好的托管阵列中。另一种方式是拨打Marshal.AllocHGlobal

输入缓冲区需要以相同的方式处理。

我不会使用您当前使用的自动pinvoke翻译。他似乎一心要强迫你使用指针和不安全的代码。没有大量的帮助。我会手工翻译。

我很抱歉不提供更具体的细节,但它有点难,因为您的问题不包含有关您的代码示例中使用的类型的任何信息。我希望一般的建议是有用的。

答案 1 :(得分:1)

感谢@ david-heffernan回答我已经成功完成了以下工作,我发布了一个答案,因为管理使用FFmpeg的例子非常少见。

fixed (byte* pData = packet.Payload)
{
    IntPtr[] in_buffs = new IntPtr[2];
    in_buffs[0] = new IntPtr(m_avFrame->data_0);
    in_buffs[1] = new IntPtr(m_avFrame->data_1);
    IntPtr[] out_buffs = new IntPtr[1];
    out_buffs[0] = new IntPtr(pData);

    FFmpegInvoke.swr_convert(m_pConvertContext, out_buffs, m_avFrame->nb_samples, in_buffs, m_avFrame->nb_samples);
}

在解码AAC音频缓冲区的完整环境中......

    protected override void DecodePacket(MediaPacket packet)
    {
        int frameFinished = 0;


        AVPacket avPacket = new AVPacket();
        FFmpegInvoke.av_init_packet(ref avPacket);
        byte[] payload = packet.Payload;
        fixed (byte* pData = payload)
        {
            avPacket.data = pData;
            avPacket.size = packet.Length;
            if (packet.KeyFrame)
            {
                avPacket.flags |= FFmpegInvoke.AV_PKT_FLAG_KEY;
            }

            int in_len = packet.Length;


            int count = FFmpegInvoke.avcodec_decode_audio4(CodecContext, m_avFrame, out frameFinished, &avPacket);

            if (count != packet.Length)
            {
            }

            if (count < 0)
            {
                throw new Exception("Can't decode frame!");
            }
        }
        FFmpegInvoke.av_free_packet(ref avPacket);

        if (frameFinished > 0)
        {
            if (!mConverstionContextInitialised)
            {
                InitialiseConverstionContext();
            }

            packet.ResetBuffer(m_avFrame->nb_samples*4); // need to find a better way of getting the out buff size

            fixed (byte* pData = packet.Payload)
            {
                IntPtr[] in_buffs = new IntPtr[2];
                in_buffs[0] = new IntPtr(m_avFrame->data_0);
                in_buffs[1] = new IntPtr(m_avFrame->data_1);
                IntPtr[] out_buffs = new IntPtr[1];
                out_buffs[0] = new IntPtr(pData);

                FFmpegInvoke.swr_convert(m_pConvertContext, out_buffs, m_avFrame->nb_samples, in_buffs, m_avFrame->nb_samples);
            }

            packet.Type = PacketType.Decoded;

            if (mFlushRequest)
            {
                //mRenderQueue.Clear();
                packet.Flush = true;
                mFlushRequest = false;
            }

            mFirstFrame = true;
        }
    }