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)

  • 相关阅读:
    hdu 4002 Find the maximum
    hdu 2837 坑题。
    hdu 3123
    zoj Treasure Hunt IV
    hdu 2053 Switch Game 水题一枚,鉴定完毕
    poj 1430 Binary Stirling Numbers
    hdu 3037 Saving Beans
    hdu 3944 dp?
    南阳oj 求N!的二进制表示最低位的1的位置(从右向左数)。
    fzu 2171 防守阵地 II
  • 原文地址:https://www.cnblogs.com/renhui/p/9177025.html
Copyright © 2011-2022 走看看