1. 程式人生 > >FFMPEG 解碼音訊

FFMPEG 解碼音訊

目的

通過FFMPEG解碼音訊的碼流,得到PCM的音訊取樣資料並用AudioTraker播放

步驟

1.註冊所有元件

av_register_all();

2.拿到封裝格式上下文

AVFormatContext *avFormatContext = avformat_alloc_context();

3 開啟檔案

    if (avformat_open_input(&avFormatContext, src_cstr, NULL, NULL) < 0) {
        LOGE("%s", "開啟檔案失敗");
        return; 
    }

4.查詢流資訊

avformat_find_stream_info(avFormatContext, NULL)

5.拿到解碼器

avcodec_find_decoder(avCodecCtx->codec_id)

6.開啟解碼器

avcodec_open2(avCodecCtx, aVCodec, NULL)

7.讀取一幀一幀的壓縮音訊資料

av_read_frame(avFormatContext, aVPacket)

8.開始解碼

vcodec_decode_audio4(avCodecCtx, aVFrame, &got_frame_ptr,aVPacket)

9.解碼得到的Frame資料,轉成PCM

swr_convert(swrCtx, &outBuffer, MAX_AUDIO_FRME_SIZE,
aVFrame->data, aVFrame->nb_samples)

10.呼叫java端的方法,獲取到AudioTraker物件

//java程式碼
public AudioTrack createAudio() {

    int sampleRateInHz = 44100;
    int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    int minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz,
            channelConfig, audioFormat);

    AudioTrack audio = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定流的型別
            sampleRateInHz, // 設定音訊資料的取樣率 32k,如果是44.1k就是44100
            channelConfig, // 設定輸出聲道為雙聲道立體聲,而CHANNEL_OUT_MONO型別是單聲道
            audioFormat, // 設定音訊資料塊是8位還是16位,這裡設定為16位。好像現在絕大多數的音訊都是16位的了
            minBufferSize, AudioTrack.MODE_STREAM // 設定模式型別,在這裡設定為流型別,另外一種MODE_STATIC貌似沒有什麼效果
    );
    // audio.play(); // 啟動音訊裝置,下面就可以真正開始音訊資料的播放了
    return audio;
}

// jni呼叫createAudio
jclass player_class = (*env)->GetObjectClass(env, jthis);
jmethodID createAudioMId = (*env)->GetMethodID(env, player_class,
        "createAudio", "()Landroid/media/AudioTrack;");

//JNIEnv*, jobject, jmethodID, ...
jobject audioTraker = (*env)->CallObjectMethod(env, jthis, createAudioMId);

11.呼叫AudioTrack.paly(),write()播放解碼後的音訊資料

javap反編譯可以得到方法簽名

// 呼叫play
jclass audio_class = (*env)->GetObjectClass(env, audioTraker);
jmethodID playMId = (*env)->GetMethodID(env, audio_class, "play", "()V");
(*env)->CallVoidMethod(env, audioTraker, playMId);
jmethodID writeMId = (*env)->GetMethodID(env, audio_class, "write",
        "([BII)I");

        .....

// while迴圈呼叫write(byte[] audioData, int offsetInBytes, int sizeInBytes)
//這裡需要byte[],所以構造byteArray
jbyteArray byteArray = (*env)->NewByteArray(env, bufferSize);
jbyte* byteElement = (*env)->GetByteArrayElements(env,
        byteArray, NULL);

//out_buffer的資料複製到sampe_bytep
memcpy(byteElement, outBuffer, bufferSize);
// 同步byteElement資料到byteArray 
(*env)->ReleaseByteArrayElements(env, byteArray, byteElement,
        0);

//呼叫AudioTraker的write()方法
(*env)->CallIntMethod(env, audioTraker,writeMId,byteArray,
        0, bufferSize);
//在while迴圈裡面不斷建立物件,需要釋放區域性引用
(*env)->DeleteLocalRef(env, byteArray);

經過以上步驟可以實現檔案的音訊解碼!

程式碼

/**
 * 解碼音訊檔案並用AudioTraker進行播放
 */JNIEXPORT void JNICALL Java_com_example_ffmpeg_AudioUtil_convertAudio(
        JNIEnv *env, jobject jthis, jstring src_jstr, jstring dst_jstr) {

    const char* src_cstr = (*env)->GetStringUTFChars(env, src_jstr, NULL);
    const char* dst_cstr = (*env)->GetStringUTFChars(env, dst_jstr, NULL);

    //註冊所有元件
    av_register_all();
    //拿到封裝格式上下文
    AVFormatContext *avFormatContext = avformat_alloc_context();
    // 開啟檔案
    if (avformat_open_input(&avFormatContext, src_cstr, NULL, NULL) < 0) {

        LOGE("%s", "開啟檔案失敗");
        return;
    }

    if (avformat_find_stream_info(avFormatContext, NULL) < 0) {

        LOGE("%s", "獲取流資訊失敗");
        return;
    }
    // 拿到解碼器
    int index = -1;
    int i = 0;
    AVCodecContext * avCodecCtx;

    for (; i < avFormatContext->nb_streams; i++) {

        avCodecCtx = avFormatContext->streams[i]->codec;
        if (avCodecCtx->codec_type == AVMEDIA_TYPE_AUDIO) {

            index = i;
            break;
        }
    }

    if (index == -1) {

        LOGE("%s", "沒有找到索引");
        return;
    }

    AVCodec *aVCodec = avcodec_find_decoder(avCodecCtx->codec_id);
    if (aVCodec == NULL) {

        LOGE("%s", "沒有找到解碼器");
        return;
    }
    // 開啟解碼器
    if (avcodec_open2(avCodecCtx, aVCodec, NULL) < 0) {

        LOGE("%s", "開啟解碼器失敗");
        return;
    }

    // 拿到資料幀,開始解碼
    AVFrame *aVFrame = av_frame_alloc();
    int got_frame_ptr;
    AVPacket *aVPacket = (AVPacket*) av_malloc(sizeof(AVPacket));

    struct SwrContext *swrCtx = swr_alloc();
    // 輸出的緩衝區大小
    uint8_t *outBuffer = (uint8_t *) av_malloc(MAX_AUDIO_FRME_SIZE);

    // 輸出聲道佈局
    int64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    int out_sample_rate = 44100;
    // 輸入的聲道佈局
    int64_t in_ch_layout = avCodecCtx->channel_layout;
    enum AVSampleFormat in_sample_fmt = avCodecCtx->sample_fmt;
    int in_sample_rate = avCodecCtx->sample_rate;
    int nb_channels = av_get_channel_layout_nb_channels(out_ch_layout);
    // 設定轉換的引數選項
    swr_alloc_set_opts(swrCtx, out_ch_layout, out_sample_fmt, out_sample_rate,
            in_ch_layout, in_sample_fmt, in_sample_rate, 0, NULL);
    swr_init(swrCtx);
    int lenCount = 0;

    LOGE("%s", "HAAHAHAHH");
    // 呼叫createAudio
    jclass player_class = (*env)->GetObjectClass(env, jthis);
    jmethodID createAudioMId = (*env)->GetMethodID(env, player_class,
            "createAudio", "()Landroid/media/AudioTrack;");

    LOGE("createAudioMId%d", createAudioMId);
    //JNIEnv*, jobject, jmethodID, ...
    jobject audioTraker = (*env)->CallObjectMethod(env, jthis, createAudioMId);
    LOGE("audioTraker%d", audioTraker);
    // 呼叫play
    jclass audio_class = (*env)->GetObjectClass(env, audioTraker);
    jmethodID playMId = (*env)->GetMethodID(env, audio_class, "play", "()V");
    (*env)->CallVoidMethod(env, audioTraker, playMId);
    jmethodID writeMId = (*env)->GetMethodID(env, audio_class, "write",
            "([BII)I");
    while (av_read_frame(avFormatContext, aVPacket) >= 0) {
        // 注意,這裡我們只需要解音訊的壓縮資料
        if (aVPacket->stream_index == index) {

            int len = avcodec_decode_audio4(avCodecCtx, aVFrame, &got_frame_ptr,
                    aVPacket);
            LOGE("解碼%d幀", lenCount++);
            if (len < 0) {

                LOGE("%s", "解碼完成");
            }
            // frame-->16bit PCM資料,寫入檔案
            if (got_frame_ptr) {

                // number of samples output per channel
                swr_convert(swrCtx, &outBuffer, MAX_AUDIO_FRME_SIZE,
                        aVFrame->data, aVFrame->nb_samples);
                int bufferSize = av_samples_get_buffer_size(NULL, nb_channels,
                        aVFrame->nb_samples, out_sample_fmt, 1);

                // 呼叫write
                //byte[] audioData, int offsetInBytes, int sizeInBytes
                jbyteArray byteArray = (*env)->NewByteArray(env, bufferSize);
                jbyte* byteElement = (*env)->GetByteArrayElements(env,
                        byteArray, NULL);

                //out_buffer的資料複製到sampe_bytep
                memcpy(byteElement, outBuffer, bufferSize);
                (*env)->ReleaseByteArrayElements(env, byteArray, byteElement,
                        0);
//              (*env)->CallVoidMethod(env, audioTraker, writeMId, byteElement,
//                                      0, bufferSize);

                (*env)->CallIntMethod(env, audioTraker,writeMId,byteArray,
                        0, bufferSize);
                //釋放區域性引用
                (*env)->DeleteLocalRef(env, byteArray);
                usleep(1000 * 16);
            }
        }
        av_free_packet(aVPacket);
    }

    swr_free(&swrCtx);
    av_frame_free(&aVFrame);
    av_free(outBuffer);
    avformat_free_context(avFormatContext);
    avcodec_free_context(&avCodecCtx);
    (*env)->ReleaseStringUTFChars(env, src_jstr, src_cstr);
    (*env)->ReleaseStringUTFChars(env, dst_jstr, dst_cstr);
}

相關推薦

使用FFMpeg 解碼音訊檔案

本篇文章將介紹使用FFMpeg解碼音訊檔案為PCM的資料。 使用FFMpeg獲取想要的音訊資料的步驟如下: 解封裝(MP3檔案)->解碼(MP3編碼)->PCM資料重取樣 1. 解封裝 使用FFMpeg解封裝的步驟如下: 使用函式 av_re

FFMPEG 解碼音訊

目的 通過FFMPEG解碼音訊的碼流,得到PCM的音訊取樣資料並用AudioTraker播放 步驟 1.註冊所有元件 av_register_all(); 2.拿到封裝格式上下文 AVFormatContext *avFormatContext =

使用ffmpeg解碼音訊檔案到PCM格式

最近忙於使用ffmpeg播放音樂檔案的專案,現將開發經驗總結如下: 一、解碼音樂檔案的大致流程如下: 1,開啟音樂檔案,呼叫av_open_input_file() 2,查詢audio stream,呼叫av_find_stream_info() 3,查詢對應的decode

FFMPEG學習筆記---SDL+FFmpeg解碼音訊資料

音訊解析流程基本跟視訊差不太多,都是藉助FFMpeg開啟檔案,獲取檔案相關資訊,找到音視訊流,開啟解碼器,進行資料讀取,其中有時會用到轉換函式,將圖片格式或者音訊格式轉換為我們想要的或者裝置可以識別的格式,然後進行讀取播放即可;下面是程式碼:#include <stdi

ffmpeg解碼音訊的兩種方式(二)根據同步位元組解析音訊

根據adts同步頭提取aac音訊單幀: #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <string.h> extern "C" { #includ

4.基於FFMPEG音訊解碼為PCM

繼續FFMPEG學習之路,前面瞭解了將PCM編碼為AAC檔案,接下來則需要了解一下解碼方面,將MP3/AAC等音訊格式解碼為PCM資料,記錄一下過程。。。 1)解碼流程 整個解碼流程採用虛擬碼大致如下: 初始化複用器和解複用器—>獲取輸入檔案的一些資訊—->查詢解碼器

FFMPEG解碼海思音訊資料

解碼流程: 1、  讀取海思g726音訊資料,海思g726音訊會多4個位元組的海思頭資訊。 2、選擇ffmpeg g726編碼器進行解碼。ffmpeg g726解碼器包括:AV_CODEC_ID_ADPCM_G726、AV_CODEC_ID_ADPCM_G726LE。如果海思

[總結]FFMPEG音訊解碼零基礎學習方法

郵箱:[email protected] 技術交流:QQ:931120780,註明csdn交流,白天較少回覆請留言。 部落格內錯誤之處,請您留言或郵件指明,不勝感激。近期發現一些錯誤,發現會及時修正。

android ffmpeg+opensl 音訊解碼播放、暫停、進度seek、時間、上/下一首

類似文章太多,但是大多程式碼都有記憶體溢位的問題,而且都缺少c層呼叫java層的例子,實際上有了參考博文後,還是有很多坑需要自己填。不過,看了很多博主和帖子後還是能夠解決一些問題,但是有些問題,根本找不到,所以我把音訊解碼播放還有控制部分做了比較詳細的例子。

mt7628/mt7620實現alsa架構通過ffmpeg解碼並播放音訊

//by Sven之前在評估用MT7628做一個音樂播放器,最初使用ffmpeg+sdl但過程曲折離奇,費了一番折騰最後發現mt7628的效能根本無法支撐ffmpeg的資源訴求,播放出來的聲音一卡一卡的,解碼速度跟不上。 無奈使用了另一替代方案libmad+libao,此方案

03 ffmpeg 解碼SDK調用 H264轉YUV420

03 ffmpeg 解碼sdk調用 h264轉yuv420制作一個H264文件[root@localhost ~]# cd /home/ [root@localhost home]# wget http://sh.yinyuetai.com/uploads/videos/common/0E3E014EBF34

FFmpeg 解碼流程

input 解碼 結構 ive nal existing format receive free 當前是用的是3.4版本的FFmpegav_register_all avformat_open_input avformat_find_stream_inf

Android 音視頻深入 九 FFmpeg解碼視頻生成yuv文件(附源碼下載)

ava vco av_free html codec ati type free 索引 項目地址,求star https://github.com/979451341/Audio-and-video-learning-materials/tree/master/FFmpeg

FFmpeg音訊處理詳解

一、基本概念 1. 音訊簡介 數碼音訊系統是通過將聲波波形轉換成一連串的二進位制資料來再現原始聲音的, 實現這個步驟使用的裝置是模/數轉換器(A/D)它以每秒上萬次的速率對聲波進行取樣, 每一次取樣都記錄下了原始模擬聲波在某一時刻的狀態,稱之為樣本。 將一串的樣本連線起來,就可以

speech_recognition實現錄音ffmpeg實現音訊檔案轉換,並用百度語音的sdk實現語音識別

專案說明: 在windows平臺下,使用speech_recognition記錄音訊,並轉換為16k的wav, 之後利用ffmpeg將wav轉化為pcm檔案,上傳到百度語音端,返回語音資訊,並利用pyttsx3添加了簡單的互動功能。 需求模組: speech_recognit

C# 使用 ffmpeg 進行音訊轉碼

先放一下 ffmpeg 的官方文件以及下載地址: 官方文件:http://ffmpeg.org/ffmpeg.html 下載地址:http://ffmpeg.org/download.html 用 ffmpeg 進行轉碼很簡單,全部都用預設引數的話用下面這句就行: ff

FFmpeg解碼MP4檔案為h264和YUV檔案

#include <iostream> #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #in

wavesurfer fluent-ffmpeg提取音訊

我試圖得到一個視訊音訊檔案wavesurfer所以可以顯示波。 我用fluent-ffmpeg表達應用。 我已經得到了處理 app.get('/audio/:file', (req, res) => ffmpeg.audio(req, res)) const path = req.params.f

FFMPEG實現音訊重取樣

技術在於交流、溝通,轉載請註明出處並保持作品的完整性。 原文:https://blog.csdn.net/hiwubihe/article/details/81259134 [音訊編解碼系列文章] 音訊編解碼基礎 FFMPEG實現音訊重取樣 FFMPEG實現PCM編

FFMPEG解碼mp3到pcm

int Mp3Decoder::prepare() { int ret=0; //1 開啟檔案獲取fmt_ctx(用於解封裝) if(avformat_open_input(&fmt_ctx,src_filename,NULL,NULL)<0){