在上一章中Android本地視頻播放器開發--NDK編譯FFmpeg能夠擷取編譯出來的ffmpeg庫,接下來就是調用ffmpeg來實現解碼,這裡我們先解碼音頻,然後在播放音頻,同時為了適應性我會用不同的方法進行播放例如使用Android提供的AudioTrack,SDL、OpengAL,OpenSL ES,最終合入視頻播放器的是OpenSL ES,這樣可以減少CPU的利用率。接下來在這一章中,先簡單的介紹如何解碼音頻,在下一章中,實現音訊播放。
首先就是編寫調用ffmpeg的檔案這裡命名為:Decodec_Audio.c
#include <stdio.h>#include <stdlib.h>#include <android/log.h>#include "VideoPlayerDecode.h"#include "../ffmpeg/libavutil/avutil.h"#include "../ffmpeg/libavcodec/avcodec.h"#include "../ffmpeg/libavformat/avformat.h"#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "graduation", __VA_ARGS__))AVFormatContext *pFormatCtx = NULL;int audioStream, delay_time, videoFlag = 0;AVCodecContext *aCodecCtx;AVCodec *aCodec;AVFrame *aFrame;AVPacket packet;int frameFinished = 0;JNIEXPORT jint JNICALL Java_com_zhangjie_graduation_videopalyer_jni_VideoPlayerDecode_VideoPlayer(JNIEnv *env, jclass clz, jstring fileName){const char* local_title = (*env)->GetStringUTFChars(env, fileName, NULL);av_register_all();//註冊所有支援的檔案格式以及轉碼器/* *唯讀取檔案頭,並不會填充流資訊 */if(avformat_open_input(&pFormatCtx, local_title, NULL, NULL) != 0)return -1;/* *擷取檔案中的流資訊,此函數會讀取packet,並確定檔案中所有流資訊, *設定pFormatCtx->streams指向檔案中的流,但此函數並不會改變檔案指標, *讀取的packet會給後面的解碼進行處理。 */if(avformat_find_stream_info(pFormatCtx, NULL) < 0)return -1;/* *輸出檔案的資訊,也就是我們在使用ffmpeg時能夠看到的檔案詳細資料, *第二個參數指定輸出哪條流的資訊,-1代表ffmpeg自己選擇。最後一個參數用於 *指定dump的是不是輸出檔案,我們的dump是輸入檔案,因此一定要為0 */av_dump_format(pFormatCtx, -1, local_title, 0);int i = 0;for(i=0; i< pFormatCtx->nb_streams; i++){if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){audioStream = i;break;}}if(audioStream < 0)return -1;aCodecCtx = pFormatCtx->streams[audioStream]->codec;aCodec = avcodec_find_decoder(aCodecCtx->codec_id);if(avcodec_open2(aCodecCtx, aCodec, NULL) < 0)return -1;aFrame = avcodec_alloc_frame();if(aFrame == NULL)return -1;int ret;while(videoFlag != -1){if(av_read_frame(pFormatCtx, &packet) < 0)break;if(packet.stream_index == audioStream){ret = avcodec_decode_audio4(aCodecCtx, aFrame, &frameFinished, &packet);if(ret > 0 && frameFinished){int data_size = av_samples_get_buffer_size(aFrame->linesize,aCodecCtx->channels,aFrame->nb_samples,aCodecCtx->sample_fmt, 0);LOGI("audioDecodec :%d",data_size);}}usleep(50000);while(videoFlag != 0){if(videoFlag == 1)//暫停{sleep(1);}else if(videoFlag == -1) //停止{break;}}av_free_packet(&packet);}av_free(aFrame);avcodec_close(aCodecCtx);avformat_close_input(&pFormatCtx);(*env)->ReleaseStringUTFChars(env, fileName, local_title);}JNIEXPORT jint JNICALL Java_com_zhangjie_graduation_videopalyer_jni_VideoPlayerDecode_VideoPlayerPauseOrPlay (JNIEnv *env, jclass clz){ if(videoFlag == 1) { videoFlag = 0; }else if(videoFlag == 0){ videoFlag = 1; } return videoFlag;}JNIEXPORT jint JNICALL Java_com_zhangjie_graduation_videopalyer_jni_VideoPlayerDecode_VideoPlayerStop (JNIEnv *env, jclass clz){ videoFlag = -1;}
接下來就是編寫Android.mk:
################################################################# ffmpeg-prebuilt ###############################################################declare the prebuilt libraryinclude $(CLEAR_VARS)LOCAL_MODULE := ffmpeg-prebuiltLOCAL_SRC_FILES := ffmpeg/android/armv7-a/libffmpeg-neon.soLOCAL_EXPORT_C_INCLUDES := ffmpeg/android/armv7-a/includeLOCAL_EXPORT_LDLIBS := ffmpeg/android/armv7-a/libffmpeg-neon.soLOCAL_PRELINK_MODULE := trueinclude $(PREBUILT_SHARED_LIBRARY)########################################################## ffmpeg-test-neno.so ################################################################include $(CLEAR_VARS)TARGET_ARCH_ABI=armeabi-v7aLOCAL_ARM_MODE=armLOCAL_ARM_NEON=trueLOCAL_ALLOW_UNDEFINED_SYMBOLS=falseLOCAL_MODULE := ffmpeg-test-neonLOCAL_SRC_FILES := jniffmpeg/Decodec_Audio.cLOCAL_C_INCLUDES := $(LOCAL_PATH)/ffmpeg/android/armv7-a/include \ $(LOCAL_PATH)/ffmpeg \ $(LOCAL_PATH)/ffmpeg/libavutil \ $(LOCAL_PATH)/ffmpeg/libavcodec \ $(LOCAL_PATH)/ffmpeg/libavformat \ $(LOCAL_PATH)/ffmpeg/libavcodec \ $(LOCAL_PATH)/ffmpeg/libswscale \ $(LOCAL_PATH)/jniffmpeg \ $(LOCAL_PATH)LOCAL_SHARED_LIBRARY := ffmpeg-prebuiltLOCAL_LDLIBS := -llog -ljnigraphics -lz -lm $(LOCAL_PATH)/ffmpeg/android/armv7-a/libffmpeg-neon.soinclude $(BUILD_SHARED_LIBRARY)
然後在終端運行ndk-build,運行結果如下:
root@zhangjie:/Graduation/jni# ndk-buildInstall : libffmpeg-neon.so => libs/armeabi/libffmpeg-neon.soCompile arm : ffmpeg-test-neon <= Decodec_Audio.cSharedLibrary : libffmpeg-test-neon.soInstall : libffmpeg-test-neon.so => libs/armeabi/libffmpeg-test-neon.so
把編譯出來的
libffmpeg-test-neon.so
libffmpeg-neon.so
拷貝到之前android功能下的libs/armeabi目錄下面,點擊視頻,視頻檔案開始解碼音頻,當解碼成功,則列印出解碼音頻包的大小:
06-07 04:51:30.953: I/graduation(7014): audioDecodec :204806-07 04:51:31.000: I/graduation(7014): audioDecodec :204806-07 04:51:31.109: I/graduation(7014): audioDecodec :204806-07 04:51:31.156: I/graduation(7014): audioDecodec :204806-07 04:51:31.257: I/graduation(7014): audioDecodec :204806-07 04:51:31.304: I/graduation(7014): audioDecodec :204806-07 04:51:31.406: I/graduation(7014): audioDecodec :204806-07 04:51:31.460: I/graduation(7014): audioDecodec :204806-07 04:51:31.554: I/graduation(7014): audioDecodec :204806-07 04:51:31.609: I/graduation(7014): audioDecodec :204806-07 04:51:31.710: I/graduation(7014): audioDecodec :204806-07 04:51:31.757: I/graduation(7014): audioDecodec :204806-07 04:51:31.859: I/graduation(7014): audioDecodec :204806-07 04:51:31.914: I/graduation(7014): audioDecodec :204806-07 04:51:32.015: I/graduation(7014): audioDecodec :204806-07 04:51:32.062: I/graduation(7014): audioDecodec :204806-07 04:51:32.164: I/graduation(7014): audioDecodec :204806-07 04:51:32.210: I/graduation(7014): audioDecodec :204806-07 04:51:32.312: I/graduation(7014): audioDecodec :204806-07 04:51:32.367: I/graduation(7014): audioDecodec :204806-07 04:51:32.468: I/graduation(7014): audioDecodec :204806-07 04:51:32.515: I/graduation(7014): audioDecodec :204806-07 04:51:32.617: I/graduation(7014): audioDecodec :204806-07 04:51:32.671: I/graduation(7014): audioDecodec :204806-07 04:51:32.773: I/graduation(7014): audioDecodec :2048
在logcat裡面可以看到解碼音頻成功,下面一章我們將解碼出來的音頻進行播放。