Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android本地視頻播放器開發--ffmpeg解碼視頻文件中的音頻(1)

Android本地視頻播放器開發--ffmpeg解碼視頻文件中的音頻(1)

編輯:關於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> 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved