安卓调用ffmpeg

本文介绍了如何在Android环境中使用ffmpeg库。首先在Ubuntu上编译ffmpeg,然后将编译后的库应用于Android项目。接着,创建NDK的头文件和源文件,并编写Java的native函数。通过javah生成头文件,再编写Android.mk和Application.mk文件进行编译。最后,测试编译结果确保ffmpeg在Android设备上的功能正常。

ffmpeg是个linux下很受欢迎的解码库,可惜对于安卓而言他并不是最好的,这里介绍了如何在安卓中使用

 

1.之前在ubuntu编译安装ffmpeg,试了下,感觉不错,ffmpeg的编译也是十分简单的,推荐在ubuntu中编译,我是在虚拟机编译后直接拿过来在win7使用的(这是由于笔记的配置较低,在虚拟机跑eclise等调试比较耗内存)

 

2.现在我们构建ndk的头文件和源文件

编写Java需要用的native函数

package com.zhku.demo2;

import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		if (savedInstanceState == null) {
			getSupportFragmentManager().beginTransaction()
					.add(R.id.container, new PlaceholderFragment()).commit();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	/**
	 * A placeholder fragment containing a simple view.
	 */
	public static class PlaceholderFragment extends Fragment {
		private Button stop, pauseOrPlay, start;

		public PlaceholderFragment() {
		}

		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			View rootView = inflater.inflate(R.layout.fragment_main, container,false);
			pauseOrPlay = (Button) rootView.findViewById(R.id.PauseOrPlay);
			stop = (Button) rootView.findViewById(R.id.Stop);
			start = (Button) rootView.findViewById(R.id.Start);
			
			start.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					nativeInit();	
					String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/galleon.avi";					
					VideoPlayer(path);
				}
			});
			pauseOrPlay.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					VideoPlayerPauseOrPlay();
				}
			});
			stop.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					// TODO Auto-generated method stub
					VideoPlayerStop();
				}
			});
			
			nativeInit();
			
			return rootView;
		}

		/**
		 * 
		 * @param message
		 */
		private void onNativeMessage(final String message) {
			Log.i(MainActivity.class.toString(), "-->>" + message);
		}

		/**
		 * 
		 * @param fileName
		 * @return
		 */
		private native int VideoPlayer(String fileName);

		/**
		 * 
		 * @return
		 */
		private native int VideoPlayerPauseOrPlay();

		/**
		 * Initializes the native code.
		 */
		private native void nativeInit();

		/**
		 * 
		 */
		private native void VideoPlayerStop();

		static {
			System.loadLibrary("avutil-54");
			System.loadLibrary("swresample-1");
			System.loadLibrary("avcodec-56");
			System.loadLibrary("swscale-3");
			System.loadLibrary("avformat-56");
			System.loadLibrary("ffmpeg1");
		}
	}

}


接着使用javah生成头文件,具体的步骤已经之前记录在博客里了

 

编写Android.mk和Application.mk,Application.mk不是必要的,只是我们在ndk使用C++时可能会出现检查错误

 

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := ffmpeg1

LOCAL_SRC_FILES := com_zhku_demo2_MainActivity.cpp \
		com_zhku_demo2_MainActivity_PlaceholderFragment.cpp

LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid
LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil libwsresample

include $(BUILD_SHARED_LIBRARY)
$(call import-module,ffmpeg-2.8/android/arm)

Application.mk

APP_CFLAGS += -Wno-error=format-security


 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <android/log.h>

extern "C"
{
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}


/* Header for class com_zhku_demo2_MainActivity_PlaceholderFragment */

#ifndef _Included_com_zhku_demo2_MainActivity_PlaceholderFragment
#define _Included_com_zhku_demo2_MainActivity_PlaceholderFragment
#ifdef __cplusplus
extern "C" {
#endif
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_INITIALIZING
#define com_zhku_demo2_MainActivity_PlaceholderFragment_INITIALIZING 0L
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_CREATED
#define com_zhku_demo2_MainActivity_PlaceholderFragment_CREATED 1L
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_ACTIVITY_CREATED
#define com_zhku_demo2_MainActivity_PlaceholderFragment_ACTIVITY_CREATED 2L
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_STOPPED
#define com_zhku_demo2_MainActivity_PlaceholderFragment_STOPPED 3L
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_STARTED
#define com_zhku_demo2_MainActivity_PlaceholderFragment_STARTED 4L
#undef com_zhku_demo2_MainActivity_PlaceholderFragment_RESUMED
#define com_zhku_demo2_MainActivity_PlaceholderFragment_RESUMED 5L

#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;
int  err_code;
static jmethodID gOnNativeMessage = NULL;
char msg[1024];

void LogMessageFromAndroid(JNIEnv*,const char *,jobject);

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayer
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayer
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayerPauseOrPlay
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayerPauseOrPlay
  (JNIEnv *, jobject);

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayerStop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayerStop
  (JNIEnv *, jobject);

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    nativeInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_nativeInit
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


 

#include "com_zhku_demo2_MainActivity_PlaceholderFragment.h"

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    nativeInit
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_nativeInit
  (JNIEnv *env, jobject obj)
  {
      if (NULL == gOnNativeMessage)
      {
        // Get the class from the object
        jclass clazz = env->GetObjectClass(obj);
        // Get the method id for the callback
        gOnNativeMessage = env->GetMethodID(clazz,"onNativeMessage","(Ljava/lang/String;)V");
        // If method could not be found
        if (NULL == gOnNativeMessage)
        {
          // Get the exception class
          jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
          // Throw exception
          env->ThrowNew(exceptionClazz, "Unable to find method");
        }
      }
      LogMessageFromAndroid(env,"nativeInit finish",obj);
  }
/**
  * Method:    LogMessageFromAndroid
  * Signature: VOID
  *
  */
void LogMessageFromAndroid(JNIEnv *env,const char *format,jobject obj)
{
  if(NULL != gOnNativeMessage)
  {
      // Prepare message
      char message[26];
      sprintf(message, format);
      // Message from the C string
      jstring messageString = env->NewStringUTF(message);
      // Call the on native message method
      env->CallVoidMethod(obj, gOnNativeMessage, messageString);
  }
}
/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayer
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayer
  (JNIEnv *env, jobject obj, jstring str)
  {
    
  	const char* fileName = env->GetStringUTFChars(str, NULL);
  	av_register_all();
    //LogMessageFromAndroid(env,fileName,obj);

  	if((err_code = avformat_open_input(&pFormatCtx, "/storage/sdcard0/sample.mov",NULL, NULL)) != 0)
    {
        // 在安卓中经常出现无法打开文件的情况,所以可以使用
        // av_strerror打印日志
        av_strerror(err_code, msg, 1024);
        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, msg); 
        return -1;
    }
    LogMessageFromAndroid(env,"VideoPlayer",obj);
	  if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        jclass exceptionClazz = env->FindClass("java/lang/RuntimeException");
        env->ThrowNew(exceptionClazz, "Unable to avformat_find_stream_info");     
        return -1;
    }

    av_dump_format(pFormatCtx, -1, fileName, 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(str, fileName);  

  }

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayerPauseOrPlay
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayerPauseOrPlay
  (JNIEnv *, jobject)
  {
      if(videoFlag == 1)
      {
        videoFlag = 0;
      }else if(videoFlag == 0)
      {
         videoFlag = 1;
      }
      return  videoFlag;
  }

/*
 * Class:     com_zhku_demo2_MainActivity_PlaceholderFragment
 * Method:    VideoPlayerStop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayerStop
  (JNIEnv *, jobject)
  {
      videoFlag = -1;  
  }


为了方便我编写了一个直接调用java函数的代码,用于直接输出log,当然还有LOGI

 

接着编译

F:\develop\android\adt-bundle-windows-x86-20140321\workspace\JNI\Demo2\jni>ndk-build APP_PLATFORM=android-9
[armeabi] Compile++ thumb: ffmpeg1 <= com_zhku_demo2_MainActivity_PlaceholderFragment.cpp
F:/develop/android/adt-bundle-windows-x86-20140321/workspace/JNI/Demo2//jni/com_zhku_demo2_MainActivity_PlaceholderFragm
ent.cpp: In function 'void LogMessageFromAndroid(JNIEnv*, char const*, jobject)':
F:/develop/android/adt-bundle-windows-x86-20140321/workspace/JNI/Demo2//jni/com_zhku_demo2_MainActivity_PlaceholderFragm
ent.cpp:39:30: warning: format not a string literal and no format arguments [-Wformat-security]
F:/develop/android/adt-bundle-windows-x86-20140321/workspace/JNI/Demo2//jni/com_zhku_demo2_MainActivity_PlaceholderFragm
ent.cpp: In function 'jint Java_com_zhku_demo2_MainActivity_00024PlaceholderFragment_VideoPlayer(JNIEnv*, jobject, jstri
ng)':
F:/develop/android/adt-bundle-windows-x86-20140321/workspace/JNI/Demo2//jni/com_zhku_demo2_MainActivity_PlaceholderFragm
ent.cpp:89:14: warning: 'AVFrame* avcodec_alloc_frame()' is deprecated (declared at D:/ProgramFiles/android-ndk-r10/sour
ces/ffmpeg-2.8/android/arm/include/libavcodec/avcodec.h:3927) [-Wdeprecated-declarations]
F:/develop/android/adt-bundle-windows-x86-20140321/workspace/JNI/Demo2//jni/com_zhku_demo2_MainActivity_PlaceholderFragm
ent.cpp:89:34: warning: 'AVFrame* avcodec_alloc_frame()' is deprecated (declared at D:/ProgramFiles/android-ndk-r10/sour
ces/ffmpeg-2.8/android/arm/include/libavcodec/avcodec.h:3927) [-Wdeprecated-declarations]
[armeabi] SharedLibrary  : libffmpeg1.so
[armeabi] Install        : libffmpeg1.so => libs/armeabi/libffmpeg1.so
[armeabi] Install        : libavformat-56.so => libs/armeabi/libavformat-56.so
[armeabi] Install        : libavcodec-56.so => libs/armeabi/libavcodec-56.so
[armeabi] Install        : libswscale-3.so => libs/armeabi/libswscale-3.so
[armeabi] Install        : libavutil-54.so => libs/armeabi/libavutil-54.so
[armeabi] Install        : libswresample-1.so => libs/armeabi/libswresample-1.so

F:\develop\android\adt-bundle-windows-x86-20140321\workspace\JNI\Demo2\jni>



测试

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值