You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android平台如何将Easy Movie Texture插件音频输出至Unity AudioSource以调用GetSpectrumData

如何将Easy Movie Texture的Android音频输出到Unity AudioSource

我之前在项目里刚好遇到过类似的需求——用Easy Movie Texture在Android上播视频,还要把音频导进Unity的AudioSource来调用GetSpectrumData。其实核心思路是把插件原生层解码出的音频PCM数据传递到Unity端,再喂给AudioClip/Source,下面是具体的实现步骤:

1. 确认插件的音频回调/可修改性

首先得明确:Easy Movie Texture在Android上是通过原生代码(Java)处理音频解码和输出的,默认可能直接把音频输出到Android系统的AudioTrack,所以我们需要拦截这些PCM音频数据并传递给Unity。

如果插件允许修改原生代码,那我们可以直接在音频输出前加入数据传递逻辑;如果插件提供了现成的音频回调API(比如部分版本有OnAudioDataAvailable事件),那直接用现成接口更省心。

2. 原生层(Android Java)修改:传递PCM数据到Unity

假设我们需要修改插件的Java代码,找到处理音频帧的地方(比如解码后的音频数据回调),把PCM数据转换成可传递的格式(比如Base64字符串,因为UnitySendMessage直接传数组有局限),然后发送给Unity:

import android.util.Base64;
import com.unity3d.player.UnityPlayer;

// 假设这是插件中处理音频数据的方法
public void onAudioFrameAvailable(byte[] audioData, int sampleRate, int channels) {
    // 将PCM字节数组转成Base64字符串,方便Unity接收
    String base64Audio = Base64.encodeToString(audioData, Base64.DEFAULT);
    // 把采样率、声道数和数据拼接成一个字符串发送
    String message = base64Audio + "," + sampleRate + "," + channels;
    // 发送给Unity中名为"AudioReceiver"的物体上的指定方法
    UnityPlayer.UnitySendMessage("AudioReceiver", "ReceiveAudioData", message);
}

3. Unity端:接收数据并填充到AudioSource

在Unity中创建一个空物体命名为AudioReceiver,挂载以下脚本,用来接收原生层传递的音频数据,动态生成AudioClip并喂给AudioSource:

using UnityEngine;
using System;

public class AudioDataReceiver : MonoBehaviour
{
    private AudioClip _dynamicAudioClip;
    private AudioSource _audioSource;
    private int _currentSamplePosition = 0;
    private bool _isClipInitialized = false;

    void Start()
    {
        _audioSource = GetComponent<AudioSource>();
        _audioSource.playOnAwake = false;
    }

    // 原生层调用的方法,接收音频数据
    public void ReceiveAudioData(string rawData)
    {
        // 拆分数据:Base64字符串、采样率、声道数
        string[] dataParts = rawData.Split(',');
        byte[] pcmBytes = Convert.FromBase64String(dataParts[0]);
        int sampleRate = int.Parse(dataParts[1]);
        int channels = int.Parse(dataParts[2]);

        // 首次收到数据时初始化AudioClip
        if (!_isClipInitialized)
        {
            // 创建动态AudioClip:参数依次是名称、总采样数(设为10秒缓冲)、声道数、采样率、是否流式
            _dynamicAudioClip = AudioClip.Create("MovieAudio", sampleRate * 10, channels, sampleRate, true);
            _audioSource.clip = _dynamicAudioClip;
            _audioSource.Play();
            _isClipInitialized = true;
        }

        // 将16位PCM字节数组转成Unity AudioClip需要的float数组(范围-1到1)
        float[] floatSamples = ConvertPcmBytesToFloat(pcmBytes);

        // 把数据填充到AudioClip,注意不要超过缓冲长度
        if (_currentSamplePosition + floatSamples.Length <= _dynamicAudioClip.samples)
        {
            _dynamicAudioClip.SetData(floatSamples, _currentSamplePosition);
            _currentSamplePosition += floatSamples.Length;
        }
        else
        {
            // 处理缓冲溢出的情况,比如重置位置或者扩展缓冲
            _currentSamplePosition = 0;
            _dynamicAudioClip.SetData(floatSamples, _currentSamplePosition);
            _currentSamplePosition += floatSamples.Length;
        }
    }

    // 16位PCM字节数组转float数组的工具方法
    private float[] ConvertPcmBytesToFloat(byte[] pcmBytes)
    {
        float[] floatSamples = new float[pcmBytes.Length / 2];
        for (int i = 0; i < floatSamples.Length; i++)
        {
            // 16位PCM是小端格式,转成short再归一化到-1~1
            short sample = BitConverter.ToInt16(pcmBytes, i * 2);
            floatSamples[i] = sample / 32768f;
        }
        return floatSamples;
    }

    // 测试GetSpectrumData的示例
    public float[] spectrumData = new float[256];
    void Update()
    {
        if (_isClipInitialized && _audioSource.isPlaying)
        {
            _audioSource.GetSpectrumData(spectrumData, 0, FFTWindow.Blackman);
            // 这里可以用spectrumData做频谱分析,比如可视化
        }
    }
}

4. 关键注意事项

  • 音画同步:要确保音频数据的传递和视频播放的时间对齐,部分插件会提供视频帧的时间戳,可以用这个来校准音频的播放位置。
  • PCM格式匹配:要确认原生层输出的PCM数据格式(位深度、采样率、声道数)和Unity端AudioClip的设置一致,不然会出现杂音或无声。
  • 性能优化:如果视频码率高,频繁传递大体积的音频数据可能会有性能问题,可以考虑批量传递数据,或者在原生层做一定的缓存后再发送。
  • 插件版本兼容:不同版本的Easy Movie Texture可能原生代码结构不同,要根据实际插件的代码结构找到音频处理的入口点。

内容的提问来源于stack exchange,提问作者Manian Rezaee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:33:18