編輯:關於Android編程
在上一章中Android本地視頻播放器開發--NDK編譯FFmpeg能夠獲取編譯出來的ffmpeg庫,接下來就是調用ffmpeg來實現解碼,這裡我們先解碼音頻,然後在播放音頻,同時為了適應性我會用不同的方法進行播放例如使用Android提供的AudioTrack,SDL、OpengAL,OpenSL ES,最終合入視頻播放器的是OpenSL ES,這樣可以減少CPU的利用率。接下來在這一章中,先簡單的介紹如何解碼音頻,在下一章中,實現音頻的播放。
首先就是編寫調用ffmpeg的文件這裡命名為:Decodec_Audio.c
[cpp]
#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;
}
#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:
[html]
#######################################################
########## 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/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 -ljnigraphics -lz -lm $(LOCAL_PATH)/ffmpeg/android/armv7-a/libffmpeg-neon.so
include $(BUILD_SHARED_LIBRARY)
#######################################################
########## 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/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 -ljnigraphics -lz -lm $(LOCAL_PATH)/ffmpeg/android/armv7-a/libffmpeg-neon.so
include $(BUILD_SHARED_LIBRARY)
然後在終端運行ndk-build,運行結果如下:
[cpp]
root@zhangjie:/Graduation/jni# ndk-build
Install : libffmpeg-neon.so => libs/armeabi/libffmpeg-neon.so
Compile arm : ffmpeg-test-neon <= Decodec_Audio.c
SharedLibrary : libffmpeg-test-neon.so
Install : libffmpeg-test-neon.so => libs/armeabi/libffmpeg-test-neon.so
root@zhangjie:/Graduation/jni# ndk-build
Install : libffmpeg-neon.so => libs/armeabi/libffmpeg-neon.so
Compile arm : ffmpeg-test-neon <= Decodec_Audio.c
SharedLibrary : libffmpeg-test-neon.so
Install : libffmpeg-test-neon.so => libs/armeabi/libffmpeg-test-neon.so
把編譯出來的[cpp]
libffmpeg-test-neon.so<PRE style="FONT-SIZE: 14px" class=cpp name="code">libffmpeg-neon.so</PRE><P></P>
<PRE></PRE>
<P></P>
<P><SPAN style="FONT-SIZE: 14px">拷貝到之前android功能下的libs/armeabi目錄下面,點擊視頻,視頻文件開始解碼音頻,當解碼成功,則打印出解碼音頻包的大小:</SPAN></P>
<P><SPAN style="FONT-SIZE: 14px"><IMG alt="" src=""><BR>
</SPAN></P>
<P><SPAN style="FONT-SIZE: 14px"></SPAN><PRE class=cpp name="code">06-07 04:51:30.953: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.000: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.109: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.156: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.257: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.304: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.406: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.460: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.554: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.609: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.710: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.757: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.859: I/graduation(7014): audioDecodec :2048
06-07 04:51:31.914: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.015: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.062: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.164: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.210: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.312: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.367: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.468: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.515: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.617: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.671: I/graduation(7014): audioDecodec :2048
06-07 04:51:32.773: I/graduation(7014): audioDecodec :2048</PRE><BR>
在logcat裡面可以看到解碼音頻成功,下面一章我們將解碼出來的音頻進行播放。<P></P>
這些天,項目裡加了一個功能效果,場景是: 假如有一個家居圖片,圖片裡,有各樣的家居用品: 桌子,毛巾,花瓶等等,需要在指定的商品處添加標記,方便用戶直接看到商品,點擊該標
檢查更新並下載更新可以說是一個app必備的功能了.既然是必備功能,往往需要考慮很多東西,如下:1,更新方式:一個軟件有更新了,如果是上線了新的功能或修復了某些bug,或者
很多時候Android常用的控件不能滿足我們的需求,那麼我們就需要自定義一個控件了。今天做了一個自定義控件的實例,來分享下。首先定義一個layout實現按鈕內部布局:&n
實現方式實現的方式有很多種 這裡總結最常見的幾種方式,以後再添加其他的。viewPager + RadioGroupviewPager + FragmentT