一、LAME简介

LAME是目前非常优秀的一种MP3编码引擎,在业界,转码成Mp3格式的音频文件时,最常用的就是LAME库。当达到320Kbit/s时,LAME编码出来的音频质量几乎可以和CD的音质相媲美,并且还能保证整个音频文件的体积非常小,因此若要在移动端平台上编码MP3文件,使用LAME便成为唯一的选择。

二、使用场景

操作系统:Android。

场景:1.录音时保存Mp3格式的文件      2. 将wav无损音频文件转码成mp3这种体积相对较小的音频文件。     3.可以将获取到的音频流进行录制保存为mp3格式。

附:如何录制wav文件,在之前的博客里面我们讲过:Android 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件

三、开发准备

LAME的源码是托管到sourceforge.net上的,我们开发一个基于LAME的项目,就不得不下载其源码用于编译。

LAME主页:http://lame.sourceforge.net/

LAME下载:http://sourceforge.net/projects/lame/files/lame/3.99/

如果需要集成到Android系统上,就需要开发者具备一些NDK开发的能力。

四、开发过程

定义在java类中定义native方法:

    private static native long nInit(int inSampleRate, int inChannels, int outSampleRate, int outBitrate, int model, int quality);

    private static native int nGetVersion(long lamePtr);

    private static native int mGetMp3bufferSize(long lamePtr);

    private static native int mGetMp3bufferSizeWithSamples(long lamePtr, int samples);

    private static native int nEncodeShortInterleaved(long lamePtr, short[] bufLR, int samples, byte[] outMp3buf);

    private static native int nEncodeShort(long lamePtr, short[] bufL, short[] bufR, int samples, byte[] outMp3buf);

    private static native int nFlush(long lamePtr, byte[] outBuf);

    private static native void nClose(long lamePtr);

生成相应的.h的头文件,并实现该头文件,完成整体逻辑的编写。

#include <jni.h>
#include <cwchar>
#include <math.h>
#include "com_renhui_lame_Lame.h"
#include "libmp3lame/lame.h" extern "C" JNIEXPORT jlong JNICALL Java_com_renhui_lame_Lame_nInit(JNIEnv *env, jclass type, jint inSampleRate, jint inChannels, jint outSampleRate, jint outBitrate, jint model, jint quality) { lame_global_flags *lameFlags;
lameFlags = lame_init();
lame_set_in_samplerate(lameFlags, inSampleRate);
lame_set_num_channels(lameFlags, inChannels);
lame_set_out_samplerate(lameFlags, outSampleRate);
lame_set_brate(lameFlags, outBitrate);
lame_set_mode(lameFlags, (MPEG_mode) model);
lame_set_quality(lameFlags, quality);
int code = lame_init_params(lameFlags);
if (code != ) {
lame_close(lameFlags);
return code;
}
return (long) lameFlags;
} JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_nGetVersion(JNIEnv *env, jclass type, jlong lamePtr) {
lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr;
return lame_get_version(lameFlags);
} JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_mGetMp3bufferSize(JNIEnv *env, jclass type, jlong lamePtr) {
lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr;
return lame_get_size_mp3buffer(lameFlags);
} JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_mGetMp3bufferSizeWithSamples(JNIEnv *env, jclass type, jlong lamePtr, jint samples) { lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr; int version = lame_get_version(lameFlags);
int bitrate = lame_get_brate(lameFlags);
int sampleRate = lame_get_out_samplerate(lameFlags); float p = (bitrate / 8.0f) / sampleRate; if (version == ) {
// MPEG2: num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
return (jint) ceil(samples * p + * * p + );
} else if (version == ) {
// MPEG1: num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
return (jint) ceil(samples * p + * * p + );
} else {
return (jint) ceil((1.25 * samples + ));
}
} JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_nEncodeShortInterleaved(JNIEnv *env, jclass type, jlong lamePtr,
jshortArray bufLR_, jint samples,
jbyteArray outMp3buf_) { lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr; jshort *bufLR = env->GetShortArrayElements(bufLR_, NULL);
jbyte *outMp3buf = env->GetByteArrayElements(outMp3buf_, NULL); const jsize outMp3bufSize = env->GetArrayLength(outMp3buf_);
int result = lame_encode_buffer_interleaved(lameFlags, bufLR, samples,
(u_char *) outMp3buf, outMp3bufSize); env->ReleaseShortArrayElements(bufLR_, bufLR, );
env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, ); return result;
} JNIEXPORT jint JNICALL
Java_com_renhui_lame_Lame_nEncodeShort(JNIEnv *env, jclass type, jlong lamePtr, jshortArray bufL_,
jshortArray bufR_, jint samples, jbyteArray outMp3buf_) {
lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr; jshort *bufL = env->GetShortArrayElements(bufL_, NULL);
jshort *bufR = env->GetShortArrayElements(bufR_, NULL);
jbyte *outMp3buf = env->GetByteArrayElements(outMp3buf_, NULL); const jsize outMp3bufSize = env->GetArrayLength(outMp3buf_);
int result = lame_encode_buffer(lameFlags, bufL, bufR, samples,
(u_char *) outMp3buf, outMp3bufSize); env->ReleaseShortArrayElements(bufL_, bufL, );
env->ReleaseShortArrayElements(bufR_, bufR, );
env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, ); return result;
} JNIEXPORT jint JNICALL
Java_com_renhui_lame_Lame_nFlush(JNIEnv *env, jclass type, jlong lamePtr, jbyteArray outBuf_) { lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr; jbyte *outBuf = env->GetByteArrayElements(outBuf_, NULL); const jsize outBufSize = env->GetArrayLength(outBuf_);
int result = lame_encode_flush(lameFlags, (u_char *) outBuf, outBufSize); env->ReleaseByteArrayElements(outBuf_, outBuf, ); return result;
} JNIEXPORT void JNICALL
Java_com_renhui_lame_Lame_nClose(JNIEnv *env, jclass type, jlong lamePtr) {
lame_global_flags *lameFlags;
lameFlags = (lame_global_flags *) lamePtr;
lame_close(lameFlags);
}

编写Android.mk和Application.mk,为ndk-build打包做准备。

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE :=mp3lame
LAME_LIBMP3_DIR :=libmp3lame
LOCAL_SRC_FILES :=\
$(LAME_LIBMP3_DIR)/bitstream.c \
$(LAME_LIBMP3_DIR)/fft.c \
$(LAME_LIBMP3_DIR)/id3tag.c \
$(LAME_LIBMP3_DIR)/mpglib_interface.c \
$(LAME_LIBMP3_DIR)/presets.c \
$(LAME_LIBMP3_DIR)/quantize.c \
$(LAME_LIBMP3_DIR)/reservoir.c \
$(LAME_LIBMP3_DIR)/tables.c \
$(LAME_LIBMP3_DIR)/util.c \
$(LAME_LIBMP3_DIR)/VbrTag.c \
$(LAME_LIBMP3_DIR)/encoder.c \
$(LAME_LIBMP3_DIR)/gain_analysis.c \
$(LAME_LIBMP3_DIR)/lame.c \
$(LAME_LIBMP3_DIR)/newmdct.c \
$(LAME_LIBMP3_DIR)/psymodel.c \
$(LAME_LIBMP3_DIR)/quantize_pvt.c \
$(LAME_LIBMP3_DIR)/set_get.c \
$(LAME_LIBMP3_DIR)/takehiro.c \
$(LAME_LIBMP3_DIR)/vbrquantize.c \
$(LAME_LIBMP3_DIR)/version.c \
com_renhui_lame_Lame.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mp3lame
LOCAL_LDLIBS := -llog -lz
include $(BUILD_SHARED_LIBRARY)

Application.mk:

APP_ABI := all
#APP_ABI := armeabi armeabi-v7a x86 # APP_ABI :=armeabi
APP_PLATFORM := android-14

附:有关编译语法,整理了一篇文章,供大家参考:Android NDK学习(二):编译脚本语法Android.mk和Application.mk

五、思维拓展

1. 录音为Mp3格式

https://github.com/renhui/LameAndroid-master

2. 结合NBPlayer,将Player的PCM数据保存到Mp3文件里

推荐资料:

Android录制音频并使用Lame转成mp3

LameMp3开发问题解决方案锦集(安卓ndk)

音视频编解码——LAME的更多相关文章

  1. [转帖]AVS音视频编解码技术了解

    AVS高清立体视频编码器 电视技术在经历了从黑白到彩色.从模拟到数字的技术变革之后正在酝酿另一场技术革命,从单纯观看二维场景的平面电视跨越到展现三维场景的立体电视3DTV.3DTV系统的核心问题之一是 ...

  2. 音视频编解码技术(一):MPEG-4/H.264 AVC 编解码标准

    一.H264 概述 H.264,通常也被称之为H.264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 1. H.264视频编解码的意义 H.264的出现就是为了创 ...

  3. 集显也能硬件编码:Intel SDK && 各种音视频编解码学习详解

    http://blog.sina.com.cn/s/blog_4155bb1d0100soq9.html INTEL MEDIA SDK是INTEL推出的基于其内建显示核心的编解码技术,我们在播放高清 ...

  4. FFmpeg音视频编解码实践总结

    PS:由于目前开发RTSP服务器传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需 ...

  5. 【miscellaneous】各种音视频编解码学习详解

    编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...

  6. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...

  7. 各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放 license收费等 ...

  8. 音视频编解码问题:javaCV如何快速进行音频预处理和解复用编解码(基于javaCV-FFMPEG)

    前言: 前面我用了很多章实现了javaCV的基本操作,包括:音视频捕捉(摄像头视频捕捉和话筒音频捕捉),推流(本地音视频或者摄像头话筒混合推流到服务器),转流(rtsp->rtmp),收流(录制 ...

  9. 基于ffmpeg的简单音视频编解码的例子

    近日需要做一个视频转码服务器,对我这样一个在该领域的新手来说却是够我折腾一番,在别人的建议下开始研究开源ffmpeg项目,下面是在代码中看到的一 段例子代码,对我的学习非常有帮助.该例子代码包含音频的 ...

随机推荐

  1. Sublime Text 执行后只有运行时间,没有执行结果!解决方法!

    问题:build完python代码,只输出:[Finished in 0.2s] 分析:操作回忆:开始是可以看到运行结果的,后来在Tools>>Build with 选择了"Sy ...

  2. vue-cli 2.x脚手架build目录中的webpack.base.conf.js配置文件

    此文章用来解释vue-cli脚手架build目录中的webpack.base.conf.js配置文件,适用于vue-cli 2.x版本 此配置文件是vue开发环境的wepack相关配置文件,主要用来处 ...

  3. C#smtp邮件消息提醒的一些bug总结

    软件通过检测公众号的某些链接是否异常发邮件提醒.. 1:网上找了些方法有说ssl  system.net.mail 不支持 ,这种说法是有问题的,其方法内设置的验证账号写成了邮箱的地址而不是账号也就是 ...

  4. MongoDB及Mongoose的记录

    MongoDB是一种NoSQL的文档型数据库,其存储的文档类型都是JSON对象. 在node.js中由于代码都是异步执行,且nosql也没有“事物”这一定义,所以日常使用中很难保证数据库操作的原子性. ...

  5. jquery取每个li元素下的第三个a元素

    $('li').find('a:eq(2)');

  6. vue-cli 第二章 (常见问题修正)

    一.编译打包多出 *.map 文件处理   当执行 npm run build 后根目录下会编译出一个 dist 的文件夹,如下:     其中 css 和 js  文件夹下会多出一些 *.map 的 ...

  7. XBee 802.15.4/Digimesh FAQs:如何为2.4G模块选择合适的信道

    XBee 802.15.4模块和XBee Digimesh模块在硬件上完全相同,只是出厂带有不同固件,如果测试需要,这两个固件可以都可以互换烧入模块中. 如何为2.4G模块选择合适的信道 IEEE 8 ...

  8. 欧拉函数-gcd-快速幂(牛客寒假算法基础集训营1-D-小a与黄金街道)

    题目描述: 链接:https://ac.nowcoder.com/acm/contest/317/D来源:牛客网小a和小b来到了一条布满了黄金的街道上.它们想要带几块黄金回去,然而这里的城管担心他们拿 ...

  9. Java集合不能存放基本数据类型

    Java集合不能存放基本数据类型,只能存放对象的引用. 每个集合元素都是一个引用变量,实际内容都存放在堆内或方法区里面, 但是基本数据类型是在栈内存上分配空间的,栈上的数据随时会被收回. 如何解决? ...

  10. Codeforces Round #548 (Div. 2) E 二分图匹配(新坑) or 网络流 + 反向处理

    https://codeforces.com/contest/1139/problem/E 题意 有n个学生,m个社团,每个学生有一个\(p_i\)值,然后每个学生属于\(c_i\)社团, 有d天,每 ...