Android使用ffmpeg导出pcm裸数据

本文介绍了如何在Android Studio 3.1.2中搭建C++项目,利用ffmpeg库导出pcm裸数据。从创建C++项目、配置CMakeLists.txt到编写Java层和Native代码,详细阐述了ffmpeg解码mp3文件并输出pcm数据的关键步骤。内容包括获取文件信息、初始化上下文、解码过程以及数据转换。源码可在GitHub找到。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实之前很长一段时间都在研究音视频的知识,只是没有总结知识。后来太忙导致以前学的都忘了好多,最近买了音视频开发的书来系统学习,但是里头的部分代码是eclipse的,现在基本上不用eclipse开发了,所以我还是选择在Android Studio中进行实践,在此记录一下学习中的要点。代码均是参考https://github.com/zhanxiaokai,也就是该书的源码仓库。本文所用环境为Android Studio3.1.2,目前的最新稳定版。本文不会从编译开始讲起,只会提一些自己认为的关键代码,以及项目构建时候的一些坑,读者需要有一定ndk开发知识。

搭建Android Studio的c++项目

新建项目的时候选择包含c++项目
这里写图片描述
最后选上支持c++11特性、异常以及运行时类型识别
这里写图片描述
接下来我们在build.gralde中添加如下代码

apply plugin: 'com.android.application'

ext {
    boolean isWindow = isWindowsOS();
    if(isWindow){
        native_sdk_path = rootDir.getAbsolutePath().replace('\\','/') + "/app"
    }else{
        native_sdk_path = file(rootDir.getAbsolutePath() + "/app")
    }
}



/** 判断是否为Windows操作系统 */
boolean isWindowsOS(){
    boolean isWindowsOS = false;
    String osName = System.getProperty("os.name");
    println("os.name=" + osName);
    if(osName == null || "".equals(osName)) {
        return false;
    }
    if(osName.toLowerCase().indexOf("windows") > -1){
        isWindowsOS = true;
    }
    return isWindowsOS;
}
android {
    ...
    sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
        ndk {
            abiFilters 'armeabi-v7a'
        }
  buildTypes {
        debug{

            debuggable true
            jniDebuggable true

            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_PLATFORM=android-16', '-DANDROID_TOOLCHAIN=clang',
                            '-DANDROID_ARM_NEON=TRUE', '-DANDROID_STL=gnustl_static',
                            "-DPATH_TO_MEDIACORE:STRING=${native_sdk_path}"
                    cFlags '-O3', '-DGL_GLEXT_PROTOTYPES', '-DEGL_EGLEXT_PROTOTYPES', '-fsigned-char', "-I${native_sdk_path}", '-Wformat','-mfpu=neon', '-mfloat-abi=softfp -frtti' // full optimization, char data type is signed
                    // 编译优化,设置函式是否能被 inline 的伪指令长度
                    cppFlags '-O3', '-fexceptions', '-fsigned-char',
                            "-frtti -std=c++11", '-Wformat'


                }
            }
        }

        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_PLATFORM=android-16', '-DANDROID_TOOLCHAIN=clang',
                            '-DANDROID_ARM_NEON=TRUE', '-DANDROID_STL=gnustl_static',
                            '-DCMAKE_BUILD_TYPE=RelWithDebInfo',
                            "-DPATH_TO_MEDIACORE:STRING=${native_sdk_path}"
                    cFlags '-O3', '-DGL_GLEXT_PROTOTYPES', '-DEGL_EGLEXT_PROTOTYPES', '-fsigned-char', "-I${native_sdk_path}", '-Wformat','-mfpu=neon', '-mfloat-abi=softfp -frtti' // full optimization, char data type is signed
                    // 编译优化,设置函式是否能被 inline 的伪指令长度
                    cppFlags '-O3', '-fexceptions', '-fsigned-char',
                            "-frtti -std=c++11", '-Wformat'

                }
            }
        }

    }
      externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

简单说一下配置,这里我们需要将根app目录添加到cmake的cache中方便获取,arguments 中的-D就是在cmake构建的时候生成的CMakeCache.txt中定义的变量,可以直接在camke脚本中获取。这里的比如-DANDROID_PLATFORM定义是内部特殊属性,可以参考官网https://developer.android.com/ndk/guides/cmake。然后下面是一些c和cxx的编译参数,大部分是一些优化选项,具体可以参考我的这篇文章https://blog.youkuaiyun.com/a568478312/article/details/79195218。接着我们指

### 如何使用 FFmpeg 提取 PCM 裸流 要通过 FFmpeg 提取 PCM 原始数据流(raw stream),可以通过指定输出格式为 `s16le` 或其他适合的 PCM 编码来实现。以下是具体方法: #### 使用命令行提取 PCM Raw Stream 假设输入文件是一个 WAV 文件,可以运行以下命令将其转换为 PCM raw 数据流: ```bash ./ffmpeg -i input.wav -f s16le -acodec pcm_s16le output.pcm ``` - `-f s16le`: 指定输出格式为 16 位小端序 PCM 格式[^1]。 - `-acodec pcm_s16le`: 显式设置音频编解码器为 PCM S16LE。 此命令将从 `input.wav` 中提取音频并保存为未压缩的 PCM 数据到 `output.pcm` 文件中。 #### 处理不同采样率和声道数的情况 如果需要调整采样率或声道数量,可以在命令中加入额外参数。例如,将音频重新采样为 8kHz 单声道: ```bash ./ffmpeg -i input.wav -ar 8000 -ac 1 -f s16le -acodec pcm_s16le output.pcm ``` - `-ar 8000`: 设置输出采样率为 8 kHz。 - `-ac 1`: 将音频转换为单声道。 这些选项允许灵活控制输出 PCM 的特性以满足特定需求[^1]。 #### 示例代码片段 下面展示了一个简单的 Python 脚本调用 FFmpeg 来提取 PCM 流的例子: ```python import subprocess def extract_pcm(input_file, output_file): command = [ 'ffmpeg', '-i', input_file, '-f', 's16le', '-acodec', 'pcm_s16le', output_file ] result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode != 0: raise Exception(f"Error extracting PCM: {result.stderr.decode('utf-8')}") extract_pcm('input.wav', 'output.pcm') ``` 该脚本利用子进程执行 FFmpeg 命令,并捕获可能发生的错误以便处理异常情况。 --- #### 注意事项 在某些情况下,可能会遇到不支持的目标样本格式问题。此时可查询编码器所支持的具体格式列表并通过编程接口动态适配。例如,在 C/C++ 程序中可通过如下方式访问支持的样本格式: ```cpp pCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (pCodec && pCodec->sample_fmts) { printf("Supported sample formats:"); for (int i = 0; pCodec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i) { printf(" %s", av_get_sample_fmt_name(pCodec->sample_fmts[i])); } } ``` 这有助于确认目标编码器是否兼容当前配置下的样本格式[^2]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值