ffmpeg解码视频文件中的音频(2)

在Android本地视频播放器开发–ffmpeg解码视频文件中的音频(1)中我们从视频文件中解码出音频,这一章中将使用OpenSL ES来播放解码的音频数据,首先关于OpenSL ES这里暂不介绍,可以查看官网以及NDK中samples下面的native-audio里面的文件,这里我也是扣取了其中的代码,,我们播放音频的部分在上一章的基础上进行添加的,代码如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <android/log.h>// for native audio#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.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;intaudioStream, delay_time, videoFlag = 0;AVCodecContext *aCodecCtx;AVCodec*aCodec;AVFrame*aFrame;AVPacketpacket;int frameFinished = 0;// engine interfacesstatic SLObjectItf engineObject = NULL;static SLEngineItf engineEngine;// output mix interfacesstatic SLObjectItf outputMixObject = NULL;static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;// buffer queue player interfacesstatic SLObjectItf bqPlayerObject = NULL;static SLPlayItf bqPlayerPlay;static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;static SLEffectSendItf bqPlayerEffectSend;static SLMuteSoloItf bqPlayerMuteSolo;static SLVolumeItf bqPlayerVolume;// aux effect on the output mix, used by the buffer queue playerstatic const SLEnvironmentalReverbSettings reverbSettings =SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;// file descriptor player interfacesstatic SLObjectItf fdPlayerObject = NULL;static SLPlayItf fdPlayerPlay;static SLSeekItf fdPlayerSeek;static SLMuteSoloItf fdPlayerMuteSolo;static SLVolumeItf fdPlayerVolume;// pointer and size of the next player buffer to enqueue, and number of remaining buffersstatic short *nextBuffer;static unsigned nextSize;static int nextCount;// this callback handler is called every time a buffer finishes playingvoid bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context){assert(bq == bqPlayerBufferQueue);assert(NULL == context);// for streaming playback, replace this test by logic to find and fill the next bufferif (–nextCount > 0 && NULL != nextBuffer && 0 != nextSize) {SLresult result;// enqueue another bufferresult = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,// which for this code example would indicate a programming errorassert(SL_RESULT_SUCCESS == result);}}void createEngine(JNIEnv* env, jclass clazz){SLresult result;// create engineresult = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);assert(SL_RESULT_SUCCESS == result);// realize the engineresult = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);// get the engine interface, which is needed in order to create other objectsresult = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);// create output mix, with environmental reverb specified as a non-required interfaceconst SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean req[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);assert(SL_RESULT_SUCCESS == result);// realize the output mixresult = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);// get the environmental reverb interface// this could fail if the environmental reverb effect is not available,// either because the feature is not present, excessive CPU load, or// the required MODIFY_AUDIO_SETTINGS permission was not requested and grantedresult = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);}// ignore unsuccessful result codes for environmental reverb, as it is optional for this example}void createBufferQueueAudioPlayer(JNIEnv* env, jclass clazz, int rate, int channel,int bitsPerSample){SLresult result;// configure audio sourceSLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};// SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_16,//SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,//SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN}; SLDataFormat_PCM format_pcm; format_pcm.formatType = SL_DATAFORMAT_PCM;format_pcm.numChannels = channel;format_pcm.samplesPerSec = rate * 1000; format_pcm.bitsPerSample = bitsPerSample; format_pcm.containerSize = 16;if(channel == 2)format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;elseformat_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;SLDataSource audioSrc = {&loc_bufq, &format_pcm};// configure audio sinkSLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&loc_outmix, NULL};// create audio playerconst SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND,/*SL_IID_MUTESOLO,*/ SL_IID_VOLUME};const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,/*SL_BOOLEAN_TRUE,*/ SL_BOOLEAN_TRUE};result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,3, ids, req);assert(SL_RESULT_SUCCESS == result);// realize the playerresult = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);// get the play interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);assert(SL_RESULT_SUCCESS == result);// get the buffer queue interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,&bqPlayerBufferQueue);assert(SL_RESULT_SUCCESS == result);// register callback on the buffer queueresult = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);assert(SL_RESULT_SUCCESS == result);// get the effect send interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,&bqPlayerEffectSend);assert(SL_RESULT_SUCCESS == result);#if 0 // mute/solo is not supported for sources that are known to be mono, as this is// get the mute/solo interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);assert(SL_RESULT_SUCCESS == result);#endif// get the volume interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);assert(SL_RESULT_SUCCESS == result);// set the player’s state to playingresult = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);assert(SL_RESULT_SUCCESS == result);}void AudioWrite(const void*buffer, int size){(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, size);}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;createEngine(env, clz);int flag_start = 0;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){if(flag_start == 0){flag_start = 1;createBufferQueueAudioPlayer(env, clz, aCodecCtx->sample_rate, aCodecCtx->channels, SL_PCMSAMPLEFORMAT_FIXED_16);}int data_size = av_samples_get_buffer_size(aFrame->linesize,aCodecCtx->channels,aFrame->nb_samples,aCodecCtx->sample_fmt, 1);LOGI("audioDecodec :%d : %d, :%d :%d",data_size,aCodecCtx->channels,aFrame->nb_samples,aCodecCtx->sample_rate);(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, aFrame->data[0], data_size);}}usleep(5000);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中添加OpenSL ES的库支持,代码如下:LOCAL_PATH := $(call my-dir)#################################################################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-neon#LOCAL_SRC_FILES := jniffmpeg/VideoPlayerDecode.cLOCAL_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 -lGLESv2 -ljnigraphics -lz -lm $(LOCAL_PATH)/ffmpeg/android/armv7-a/libffmpeg-neon.soLOCAL_LDLIBS += -lOpenSLES include $(BUILD_SHARED_LIBRARY)由于OpenSLES最低版本需要9所以要在Application.mk中添加平台# The ARMv7 is significanly faster due to the use of the hardware FPUAPP_ABI := armeabi APP_PLATFORM := android-9APP_STL := stlport_staticAPP_CPPFLAGS += -fno-rtti#APP_ABI := armeabi最后在终端运行ndk-build,就会将代码添加到ffmpeg-test-neon.so这个库中看了哪些风景,遇到哪些人。尽管同学说,

ffmpeg解码视频文件中的音频(2)

相关文章:

你感兴趣的文章:

标签云: