=====================================================

最简单的基于FFmpeg的移动端例子系列文章列表:

最简单的基于FFmpeg的移动端例子:Android HelloWorld

最简单的基于FFmpeg的移动端例子:Android 视频解码器

最简单的基于FFmpeg的移动端例子:Android 视频解码器-单个库版

最简单的基于FFmpeg的移动端例子:Android 推流器

最简单的基于FFmpeg的移动端例子:Android 视频转码器

最简单的基于FFmpeg的移动端例子附件:Android 自带播放器

最简单的基于FFmpeg的移动端例子附件:SDL Android HelloWorld

最简单的基于FFmpeg的移动端例子:IOS HelloWorld

最简单的基于FFmpeg的移动端例子:IOS 视频解码器

最简单的基于FFmpeg的移动端例子:IOS 推流器

最简单的基于FFmpeg的移动端例子:IOS 视频转码器

最简单的基于FFmpeg的移动端例子附件:IOS自带播放器

最简单的基于FFmpeg的移动端例子:Windows Phone HelloWorld

=====================================================


本文记录一个安卓平台下基于FFmpeg的视频解码器。该视频解码器C语言的源代码来自于《最简单的基于FFMPEG+SDL的视频播放器》。相关的概念就不再重复记录了。



源代码

项目的目录结构如图所示。Java源代码位于src目录,而C代码位于jni目录。
Android程序Java端代码位于src\com\leixiaohua1020\sffmpegandroiddecoder\MainActivity.java,如下所示。
/** * 最简单的基于FFmpeg的视频解码器-安卓 * Simplest FFmpeg Android Decoder *  * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 *  * 本程序是安卓平台下最简单的基于FFmpeg的视频解码器。它可以将输入的视频数据解码成YUV像素数据。 *  * This software is the simplest decoder based on FFmpeg in Android. It can decode video stream * to raw YUV data. *  */package com.leixiaohua1020.sffmpegandroiddecoder;import android.os.Bundle;import android.os.Environment;import android.app.Activity;import android.text.Editable;import android.util.Log;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Button startButton = (Button) this.findViewById(R.id.button_start);final EditText urlEdittext_input= (EditText) this.findViewById(R.id.input_url);final EditText urlEdittext_output= (EditText) this.findViewById(R.id.output_url);startButton.setOnClickListener(new OnClickListener() {public void onClick(View arg0){String folderurl=Environment.getExternalStorageDirectory().getPath();String urltext_input=urlEdittext_input.getText().toString();        String inputurl=folderurl+"/"+urltext_input;                String urltext_output=urlEdittext_output.getText().toString();        String outputurl=folderurl+"/"+urltext_output;                Log.i("inputurl",inputurl);        Log.i("outputurl",outputurl);            decode(inputurl,outputurl);        }});    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.main, menu);        return true;    }        //JNI    public native int decode(String inputurl, String outputurl);        static{    System.loadLibrary("avutil-54");    System.loadLibrary("swresample-1");    System.loadLibrary("avcodec-56");    System.loadLibrary("avformat-56");    System.loadLibrary("swscale-3");    System.loadLibrary("postproc-53");    System.loadLibrary("avfilter-5");    System.loadLibrary("avdevice-56");    System.loadLibrary("sffdecoder");    }}

C语言端源代码位于jni/simplest_ffmpeg_decoder.c,如下所示。
/** * 最简单的基于FFmpeg的视频解码器-安卓 * Simplest FFmpeg Android Decoder * * 雷霄骅 Lei Xiaohua * leixiaohua1020@126.com * 中国传媒大学/数字电视技术 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程序是安卓平台下最简单的基于FFmpeg的视频解码器。它可以将输入的视频数据解码成YUV像素数据。 * * This software is the simplest decoder based on FFmpeg in Android. It can decode video stream * to raw YUV data. * */#include <stdio.h>#include <time.h> #include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswscale/swscale.h"#include "libavutil/log.h"#ifdef ANDROID#include <jni.h>#include <android/log.h>#define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)#define LOGI(format, ...)  __android_log_print(ANDROID_LOG_INFO,  "(=_=)", format, ##__VA_ARGS__)#else#define LOGE(format, ...)  printf("(>_<) " format "\n", ##__VA_ARGS__)#define LOGI(format, ...)  printf("(^_^) " format "\n", ##__VA_ARGS__)#endif//Output FFmpeg's av_log()void custom_log(void *ptr, int level, const char* fmt, va_list vl){FILE *fp=fopen("/storage/emulated/0/av_log.txt","a+");if(fp){vfprintf(fp,fmt,vl);fflush(fp);fclose(fp);}}JNIEXPORT jint JNICALL Java_com_leixiaohua1020_sffmpegandroiddecoder_MainActivity_decode  (JNIEnv *env, jobject obj, jstring input_jstr, jstring output_jstr){AVFormatContext*pFormatCtx;inti, videoindex;AVCodecContext*pCodecCtx;AVCodec*pCodec;AVFrame*pFrame,*pFrameYUV;uint8_t *out_buffer;AVPacket *packet;int y_size;int ret, got_picture;struct SwsContext *img_convert_ctx;FILE *fp_yuv;int frame_cnt;clock_t time_start, time_finish;double  time_duration = 0.0;char input_str[500]={0};char output_str[500]={0};char info[1000]={0};sprintf(input_str,"%s",(*env)->GetStringUTFChars(env,input_jstr, NULL));sprintf(output_str,"%s",(*env)->GetStringUTFChars(env,output_jstr, NULL));//FFmpeg av_log() callback  av_log_set_callback(custom_log);av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();if(avformat_open_input(&pFormatCtx,input_str,NULL,NULL)!=0){LOGE("Couldn't open input stream.\n");return -1;}if(avformat_find_stream_info(pFormatCtx,NULL)<0){LOGE("Couldn't find stream information.\n");return -1;}videoindex=-1;for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;break;}if(videoindex==-1){LOGE("Couldn't find a video stream.\n");return -1;}pCodecCtx=pFormatCtx->streams[videoindex]->codec;pCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){LOGE("Couldn't find Codec.\n");return -1;}if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){LOGE("Couldn't open codec.\n");return -1;}pFrame=av_frame_alloc();pFrameYUV=av_frame_alloc();out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);packet=(AVPacket *)av_malloc(sizeof(AVPacket));img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);     sprintf(info,   "[Input     ]%s\n", input_str);  sprintf(info, "%s[Output    ]%s\n",info,output_str);  sprintf(info, "%s[Format    ]%s\n",info, pFormatCtx->iformat->name);  sprintf(info, "%s[Codec     ]%s\n",info, pCodecCtx->codec->name);  sprintf(info, "%s[Resolution]%dx%d\n",info, pCodecCtx->width,pCodecCtx->height);  fp_yuv=fopen(output_str,"wb+");  if(fp_yuv==NULL){printf("Cannot open output file.\n");return -1;}frame_cnt=0;time_start = clock();while(av_read_frame(pFormatCtx, packet)>=0){if(packet->stream_index==videoindex){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if(ret < 0){LOGE("Decode Error.\n");return -1;}if(got_picture){sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);y_size=pCodecCtx->width*pCodecCtx->height;  fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //Ufwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V//Output infochar pictype_str[10]={0};switch(pFrame->pict_type){case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;  case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;default:sprintf(pictype_str,"Other");break;}LOGI("Frame Index: %5d. Type:%s",frame_cnt,pictype_str);frame_cnt++;}}av_free_packet(packet);}//flush decoder//FIX: Flush Frames remained in Codecwhile (1) {ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0)break;if (!got_picture)break;sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);int y_size=pCodecCtx->width*pCodecCtx->height;  fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //Ufwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V//Output infochar pictype_str[10]={0};switch(pFrame->pict_type){case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;  case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;default:sprintf(pictype_str,"Other");break;}LOGI("Frame Index: %5d. Type:%s",frame_cnt,pictype_str);frame_cnt++;}time_finish = clock(); time_duration=(double)(time_finish - time_start);sprintf(info, "%s[Time      ]%fms\n",info,time_duration);sprintf(info, "%s[Count     ]%d\n",info,frame_cnt);sws_freeContext(img_convert_ctx);  fclose(fp_yuv);av_frame_free(&pFrameYUV);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;}

Android.mk文件位于jni/Android.mk,如下所示。
# Android.mk for FFmpeg## Lei Xiaohua 雷霄骅# leixiaohua1020@126.com# http://blog.csdn.net/leixiaohua1020# LOCAL_PATH := $(call my-dir)# FFmpeg libraryinclude $(CLEAR_VARS)LOCAL_MODULE := avcodecLOCAL_SRC_FILES := libavcodec-56.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avdeviceLOCAL_SRC_FILES := libavdevice-56.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avfilterLOCAL_SRC_FILES := libavfilter-5.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avformatLOCAL_SRC_FILES := libavformat-56.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := avutilLOCAL_SRC_FILES := libavutil-54.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := postprocLOCAL_SRC_FILES := libpostproc-53.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swresampleLOCAL_SRC_FILES := libswresample-1.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := swscaleLOCAL_SRC_FILES := libswscale-3.soinclude $(PREBUILT_SHARED_LIBRARY)# Programinclude $(CLEAR_VARS)LOCAL_MODULE := sffdecoderLOCAL_SRC_FILES :=simplest_ffmpeg_decoder.cLOCAL_C_INCLUDES += $(LOCAL_PATH)/includeLOCAL_LDLIBS := -llog -lzLOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscaleinclude $(BUILD_SHARED_LIBRARY)

运行结果

App在手机上运行后的结果如下图所示。

注意需要把等待解码的视频文件拷贝至存储卡相应的目录中。例如对于上述截图的情况,需要将sintel.mp4拷贝至存储卡的根目录中。

单击“Start”按钮就可以将存储卡根目录中的视频文件解码为YUV文件(需要等待一段时间完成解码)。注意解码后的YUV文件体积巨大,可能会占用大量的存储卡空间。


下载


simplest ffmpeg mobile

项目主页

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_mobile

开源中国:https://git.oschina.net/leixiaohua1020/simplest_ffmpeg_mobile

SourceForge:https://sourceforge.net/projects/simplestffmpegmobile/


CSDN工程下载地址:http://download.csdn.net/detail/leixiaohua1020/8924391


本解决方案包含了使用FFmpeg在移动端处理多媒体的各种例子:
[Android]
simplest_android_player: 基于安卓接口的视频播放器
simplest_ffmpeg_android_helloworld: 安卓平台下基于FFmpeg的HelloWorld程序
simplest_ffmpeg_android_decoder: 安卓平台下最简单的基于FFmpeg的视频解码器
simplest_ffmpeg_android_decoder_onelib: 安卓平台下最简单的基于FFmpeg的视频解码器-单库版
simplest_ffmpeg_android_streamer: 安卓平台下最简单的基于FFmpeg的推流器
simplest_ffmpeg_android_transcoder: 安卓平台下移植的FFmpeg命令行工具
simplest_sdl_android_helloworld: 移植SDL到安卓平台的最简单程序
[IOS]
simplest_ios_player: 基于IOS接口的视频播放器
simplest_ffmpeg_ios_helloworld: IOS平台下基于FFmpeg的HelloWorld程序
simplest_ffmpeg_ios_decoder: IOS平台下最简单的基于FFmpeg的视频解码器
simplest_ffmpeg_ios_streamer: IOS平台下最简单的基于FFmpeg的推流器
simplest_ffmpeg_ios_transcoder: IOS平台下移植的ffmpeg.c命令行工具
simplest_sdl_ios_helloworld: 移植SDL到IOS平台的最简单程序





更多相关文章

  1. 最简单的基于FFmpeg的移动端例子:Android(安卓)HelloWorld
  2. Android数据可视化
  3. html移动应用开发技术分享
  4. Android(安卓)http请求例子、Unicode转UTF-8 java代码收藏
  5. android:configChanges属性
  6. Android(安卓)开发学习 HelloAndroid例子
  7. Android(安卓)开发学习 HelloAndroid例子
  8. Android中检测手机制式和移动网络类型
  9. Android调用WebService之客户端实现(二)

随机推荐

  1. Android音频数据传输
  2. Value xxx of type org.json.JSONObject
  3. Android应用程序的国际化与本地化
  4. Android(安卓)下载网络图片注意的问题
  5. appium 自动化测试之知乎Android客户端
  6. 不继承TabActivity定义TabHost
  7. Android(安卓)全局异常捕获DefaultUncaug
  8. android 面试经典(4)
  9. Android之AVD安装APK
  10. Flutter 基础组件之 Stack