zoukankan      html  css  js  c++  java
  • 音视频编解码——LAME

    一、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 != 0) {
            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 == 0) {
            // MPEG2: num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
            return (jint) ceil(samples * p + 4 * 576 * p + 256);
        } else if (version == 1) {
            // MPEG1: num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
            return (jint) ceil(samples * p + 4 * 1152 * p + 512);
        } else {
            return (jint) ceil((1.25 * samples + 7200));
        }
    }
    
    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, 0);
        env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, 0);
    
        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, 0);
        env->ReleaseShortArrayElements(bufR_, bufR, 0);
        env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, 0);
    
        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, 0);
    
        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)

  • 相关阅读:
    MySql相关
    RabbmitMQ 的配置及开启MQTT服务
    cmake 编译.so or .a文件很大问题
    模型上移动端遇到简单的问题:No variants found for 'app'
    第一次用go mod模式创建beego1.12的步骤
    python中RabbitMQ的使用hello world简单模式
    Ubuntu18.04 20.04安装rabbitMQ
    Django的mysqlclient报错
    《软件需求》读书笔记六
    《软件需求》读书笔记五
  • 原文地址:https://www.cnblogs.com/renhui/p/9177025.html
Copyright © 2011-2022 走看看