您的位置:首页 > 移动开发 > Android开发

Android本地视频播放器开发--ffmpeg解码视频文件中的音频(2)

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

[cpp] view
plaincopy

#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__))



***FormatContext *pFormatCtx = NULL;

int audioStream, delay_time, videoFlag = 0;

***CodecContext *aCodecCtx;

***Codec *aCodec;

***Frame *aFrame;

***Packet packet;

int frameFinished = 0;



// engine interfaces

static SLObjectItf engineObject = NULL;

static SLEngineItf engineEngine;



// output mix interfaces

static SLObjectItf outputMixObject = NULL;

static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;



// buffer queue player interfaces

static 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 player

static const SLEnvironmentalReverbSettings reverbSettings =

SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;



// file descriptor player interfaces

static 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 buffers

static short *nextBuffer;

static unsigned nextSize;

static int nextCount;



// this callback handler is called every time a buffer finishes playing

void 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 buffer

if (--nextCount > 0 && NULL != nextBuffer && 0 != nextSize) {

SLresult result;

// enqueue another buffer

result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);

// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,

// which for this code example would indicate a programming error

assert(SL_RESULT_SUCCESS == result);

}

}





void createEngine(JNIEnv* env, jclass clazz)

{

SLresult result;



// create engine

result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);

assert(SL_RESULT_SUCCESS == result);



// realize the engine

result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);

assert(SL_RESULT_SUCCESS == result);



// get the engine interface, which is needed in order to create other objects

result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

assert(SL_RESULT_SUCCESS == result);



// create output mix, with environmental reverb specified as a non-required interface

const 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 mix

result = (*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 granted

result = (*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 source

SLDataLocator_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;

else

format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;

format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;

SLDataSource audioSrc = {&loc_bufq, &format_pcm};



// configure audio sink

SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};

SLDataSink audioSnk = {&loc_outmix, NULL};



// create audio player

const 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 player

result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);

assert(SL_RESULT_SUCCESS == result);



// get the play interface

result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);

assert(SL_RESULT_SUCCESS == result);



// get the buffer queue interface

result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,

&bqPlayerBufferQueue);

assert(SL_RESULT_SUCCESS == result);



// register callback on the buffer queue

result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);

assert(SL_RESULT_SUCCESS == result);



// get the effect send interface

result = (*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 interface

result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);

assert(SL_RESULT_SUCCESS == result);

#endif



// get the volume interface

result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);

assert(SL_RESULT_SUCCESS == result);



// set the player's state to playing

result = (*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 == ***MEDIA_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的库支持,代码如下:

[cpp] view
plaincopy

LOCAL_PATH := $(call my-dir)

#######################################################

########## ffmpeg-prebuilt #######

#######################################################

#declare the prebuilt library

include $(CLEAR_VARS)

LOCAL_MODULE := ffmpeg-prebuilt

LOCAL_SRC_FILES := ffmpeg/android/armv7-a/libffmpeg-neon.so

LOCAL_EXPORT_C_INCLUDES := ffmpeg/android/armv7-a/include

LOCAL_EXPORT_LDLIBS := ffmpeg/android/armv7-a/libffmpeg-neon.so

LOCAL_PRELINK_MODULE := true

include $(PREBUILT_SHARED_LIBRARY)



########################################################

## ffmpeg-test-neno.so ########

########################################################

include $(CLEAR_VARS)

TARGET_ARCH_ABI=armeabi-v7a

LOCAL_ARM_MODE=arm

LOCAL_ARM_NEON=true

LOCAL_ALLOW_UNDEFINED_SYMBOLS=false

LOCAL_MODULE := ffmpeg-test-neon

#LOCAL_SRC_FILES := jniffmpeg/VideoPlayerDecode.c

LOCAL_SRC_FILES := jniffmpeg/Decodec_Audio.c



LOCAL_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-prebuilt

LOCAL_LDLIBS := -llog -lGLESv2 -ljnigraphics -lz -lm $(LOCAL_PATH)/ffmpeg/android/armv7-a/libffmpeg-neon.so

LOCAL_LDLIBS += -lOpenSLES

include $(BUILD_SHARED_LIBRARY)

由于OpenSLES最低版本需要9所以要在Application.mk中添加平台

[cpp] view
plaincopy

# The ARMv7 is significanly faster due to the use of the hardware FPU

APP_ABI := armeabi

APP_PLATFORM := android-9

APP_STL := stlport_static

APP_CPPFLAGS += -fno-rtti

#APP_ABI := armeabi

最后在终端运行ndk-build,就会将代码添加到

[cpp] view
plaincopy

ffmpeg-test-neon.so这个库中

最后在Android端调用

[cpp] view
plaincopy

VideoPlayer这个函数就会自动播放视频的声音,测试发现虽然声音正常但是有杂音,可能采样率设置的不对,获取其他的配置有问题,下一章着重解决这个问题,同时使用队列

出在:

[cpp] view
plaincopy

VideoPlayer这个函数就会自动播放视频的声音,测试发现虽然声音正常但是有杂音,可能采样率设置的不对,获取其他的配置有问题,下一章着重解决这个问题,同时使用队列
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐