[原]如何在Android用FFmpeg解码图像

前一篇[原]如何用Android NDK编译FFmpeg 我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库。这篇文章我们就可以使用Android下的JNI来调用FFMpeg进行解码了。

一、编译出来可以使用的动态库,我们会看到如下输出则表示link完成了:

  

CC    libavcodec/log2_tab.o
CC    libavutil/log2_tab.o
CC    libswresample/log2_tab.o
AR    libavcodec/libavcodec.a
LD    libavutil/libavutil.so.52
AR    libavutil/libavutil.a
AR    libswresample/libswresample.a
LD    libavcodec/libavcodec.so.55
LD    libswresample/libswresample.so.0
LD    libswscale/libswscale.so.2
LD    libavformat/libavformat.so.55
INSTALL    libavformat/libavformat.a
INSTALL    libavformat/libavformat.so
STRIP    install-libavformat-shared
INSTALL    libavcodec/libavcodec.a
INSTALL    libavcodec/libavcodec.so
STRIP    install-libavcodec-shared
INSTALL    libswresample/libswresample.a
INSTALL    libswresample/libswresample.so
STRIP    install-libswresample-shared
INSTALL    libswscale/libswscale.a
INSTALL    libswscale/libswscale.so
STRIP    install-libswscale-shared
INSTALL    libavutil/libavutil.a
INSTALL    libavutil/libavutil.so
STRIP    install-libavutil-shared
INSTALL    libavformat/avformat.h
INSTALL    libavformat/avio.h
INSTALL    libavformat/version.h
INSTALL    libavformat/libavformat.pc
INSTALL    libavcodec/avcodec.h
INSTALL    libavcodec/avfft.h
INSTALL    libavcodec/dxva2.h
INSTALL    libavcodec/old_codec_ids.h
INSTALL    libavcodec/vaapi.h
INSTALL    libavcodec/vda.h
INSTALL    libavcodec/vdpau.h
INSTALL    libavcodec/version.h
INSTALL    libavcodec/xvmc.h
INSTALL    libavcodec/libavcodec.pc
INSTALL    libswresample/swresample.h
INSTALL    libswresample/version.h
INSTALL    libswresample/libswresample.pc
INSTALL    libswscale/swscale.h
INSTALL    libswscale/version.h
INSTALL    libswscale/libswscale.pc
INSTALL    libavutil/adler32.h
INSTALL    libavutil/aes.h
INSTALL    libavutil/attributes.h
INSTALL    libavutil/audio_fifo.h
INSTALL    libavutil/audioconvert.h
INSTALL    libavutil/avassert.h
INSTALL    libavutil/avstring.h
INSTALL    libavutil/avutil.h
INSTALL    libavutil/base64.h
INSTALL    libavutil/blowfish.h
INSTALL    libavutil/bprint.h
INSTALL    libavutil/bswap.h
INSTALL    libavutil/buffer.h
INSTALL    libavutil/channel_layout.h
INSTALL    libavutil/common.h
INSTALL    libavutil/cpu.h
INSTALL    libavutil/crc.h
INSTALL    libavutil/error.h
INSTALL    libavutil/eval.h
INSTALL    libavutil/fifo.h
INSTALL    libavutil/file.h
INSTALL    libavutil/frame.h
INSTALL    libavutil/hmac.h
INSTALL    libavutil/imgutils.h
INSTALL    libavutil/intfloat.h
INSTALL    libavutil/intfloat_readwrite.h
INSTALL    libavutil/intreadwrite.h
INSTALL    libavutil/lfg.h
INSTALL    libavutil/log.h
INSTALL    libavutil/mathematics.h
INSTALL    libavutil/md5.h
INSTALL    libavutil/mem.h
INSTALL    libavutil/murmur3.h
INSTALL    libavutil/dict.h
INSTALL    libavutil/old_pix_fmts.h
INSTALL    libavutil/opt.h
INSTALL    libavutil/parseutils.h
INSTALL    libavutil/pixdesc.h
INSTALL    libavutil/pixfmt.h
INSTALL    libavutil/random_seed.h
INSTALL    libavutil/rational.h
INSTALL    libavutil/ripemd.h
INSTALL    libavutil/samplefmt.h
INSTALL    libavutil/sha.h
INSTALL    libavutil/sha512.h
INSTALL    libavutil/time.h
INSTALL    libavutil/timecode.h
INSTALL    libavutil/timestamp.h
INSTALL    libavutil/version.h
INSTALL    libavutil/xtea.h
INSTALL    libavutil/lzo.h
INSTALL    libavutil/avconfig.h
INSTALL    libavutil/libavutil.pc

 link ffmpeg.

二、新建一个Android工程,在工程目录下新建一个jni文件夹,在文件夹下新建一个ffmpeg文件夹,用来放ffmpeg相关的头文件。在ffmpeg文件夹下新建Android.mk文件用来预先加载ffmpeg动态库。Android.mk文件内容如下:

 

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := /path/to/build/output/libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)

三、在jni下新建Android.mk文件和Application.mk两个文件用来指定编译的顺序和编译的平台以及对应的cpu指令集。

Application.mk

APP_ABI := armeabi
APP_PLATFORM := android-9
Android.mk

include $(call all-subdir-makefiles)

四、编写JNI文件,用来绑定java文件与.c文件的交互,文件内容如下:

/*
 * ffmpeg_jni.c
 *
 *  Created on: Sep 1, 2014
 *      Author: clarck
 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>

#include "../include/ffmpeg_logger.h"
#include "../include/ffmpeg.h"

// 指定要注册的类,对应完整的java类名
#define JNIREG_CLASS "com/clarck/android/ffmpeg/MainActivity"

JNIEXPORT void JNICALL native_setDataSource(JNIEnv *env, jclass classzz, jstring path) {
    char *filepath = ffmpeg_jstringTostr(env, path);
    ffmpeg_setDataSource(filepath);
}

//Java和JNI函数的绑定
static JNINativeMethod method_table[] = {
    { "setDataSource", "(Ljava/lang/String;)V", native_setDataSource }
};

//注冊native方法到java中
static int registerNativeMethods(JNIEnv *env, const char *className,
        JNINativeMethod *gMethods, int numMethods) {
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }

    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

//調用註冊方法
int register_ndk_load(JNIEnv *env) {
    return registerNativeMethods(env, JNIREG_CLASS, method_table,
            (int) (sizeof(method_table) / sizeof(method_table[0])));
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }

    register_ndk_load(env);

    //返回JNI的版本
    return JNI_VERSION_1_6;
}

五、编写ffmpeg调用函数,内容如下:

/*
 * ffmpeg.c
 *
 *  Created on: Sep 1, 2014
 *      Author: clarck
 */
#include <jni.h>
#include <android/native_window_jni.h>
#include "../include/ffmpeg.h"
#include "../include/ffmpeg_logger.h"
#include "../ffmpeg/include/libavcodec/avcodec.h"
#include "../ffmpeg/include/libavformat/avformat.h"
#include "../ffmpeg/include/libavutil/pixfmt.h"
#include "../ffmpeg/include/libswscale/swscale.h"

char* ffmpeg_jstringTostr(JNIEnv* env, jstring jstr) {
    char* pStr = NULL;

    jclass jstrObj = (*env)->FindClass(env, "java/lang/String");
    jstring encode = (*env)->NewStringUTF(env, "utf-8");
    jmethodID methodId = (*env)->GetMethodID(env, jstrObj, "getBytes",
            "(Ljava/lang/String;)[B");
    jbyteArray byteArray = (jbyteArray) (*env)->CallObjectMethod(env, jstr,
            methodId, encode);
    jsize strLen = (*env)->GetArrayLength(env, byteArray);
    jbyte *jBuf = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);

    if (jBuf > 0) {
        pStr = (char*) malloc(strLen + 1);

        if (!pStr) {
            return NULL ;
        }

        memcpy(pStr, jBuf, strLen);

        pStr[strLen] = 0;
    }

    (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);

    return pStr;
}

void ffmpeg_setDataSource(char *file_path) {
    LOGI("ffmpeg_setDataSource:%s", file_path);

    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {
        LOGE("can‘t open the file. \n");
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE("Could‘t find stream infomation.\n");
        return;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        LOGE("Didn‘t find a video stream.\n");
        return;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        LOGE("Codec not found.\n");
        return;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        LOGE("Could not open codec.\n");
        return;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, file_path, 0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                    packet);

            LOGI("avcodec_decode_video2 ret:%d", ret);

            if (ret < 0) {
                LOGE("decode error.\n");
                return;
            }

            if (got_picture) {
                //TODO 此处可以将解码出来的图片保存起来。
            }
        }
        av_free_packet(packet);
    }

    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

六、编写Android.mk用来编译相关的.c文件,内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

FFMPEG_PATH := ../ffmpeg
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(FFMPEG_PATH)/include

LOCAL_MODULE    := ffmpeg_player
LOCAL_SRC_FILES += ffmpeg_jni.c
LOCAL_SRC_FILES += ffmpeg.c

LOCAL_SHARED_LIBRARIES := ffmpeg
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

七、编写java文件中相关执行调用方法

package com.clarck.android.ffmpeg;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

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

        setDataSource("/sdcard/a.mp4");
    }

    public native void setDataSource(String path);

    static {
        System.loadLibrary("ffmpeg");
        System.loadLibrary("ffmpeg_player");
    }
}

八、执行结果如下图:

时间: 2024-10-11 05:12:09

[原]如何在Android用FFmpeg解码图像的相关文章

[原]如何在Android用FFmpeg+SDL2.0解码图像线程

关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声音参考[原]如何在Android用FFmpeg+SDL2.0解码声音.但是该文章有一个问题,就是解码出来的声音有很大的噪音,基本无法听清,这是由于对于声音的处理有问题.故本文参考ffmpeg-sdl音频播放分析声音解码的处理,解码出来的声音就正常了. 博主的开发环境:Ubuntu 14.04 64位

ffmpeg 解码库在android中的使用

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">前面有博客讲到linux下编译ffmpeg</span> 那么编译完成之后应该怎么使用呢? 在参考了ffmpeg解码文件的demo这里给出一个解码的so库JNI实现方法 在编译完成ffmpeg的源码后,新建一个工程如下图目录结构 在ffmpeg编译后的源码中include文件

Android开发之《ffmpeg解码mjpeg视频流》

FFmpeg解码USB摄像头MJPEG输出:http://blog.csdn.net/light_in_dark/article/details/56276799?locationNum=14&fps=1

ffmpeg解码流程

来源:http://www.xuebuyuan.com/807209.html ffmpeg解码流程 . 2013年08月20日 ⁄ 综合 ⁄ 共 14054字 ⁄ 字号 小 中 大 ⁄ 评论关闭 文章目录 1. 注册所有容器格式和CODEC:av_register_all() 2. 打开文件:av_open_input_file() 3. 从文件中提取流信息:av_find_stream_info() 4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO 5. 查找对应的解码器:

最简单的基于FFMPEG的图像编码器(YUV编码为JPEG)

伴随着毕业论文的完毕,这两天最终腾出了空暇,又有时间搞搞FFMPEG的研究了.想着之前一直搞的都是FFMPEG解码方面的工作,非常少涉及到FFMPEG编码方面的东西,于是打算研究一下FFMPEG的编码.在网上看了一些样例,发现要不然是难度稍微有些大,要不然就是类库比較陈旧,于是就决定自己做一个编码方面的样例,方便以后学习. 本文的编码器实现了YUV420P的数据编码为JPEG图片.本着简单的原则,代码基本上精简到了极限.使用了2014年5月6号编译的最新的FFMPEG类库. 程序非常easy,打

在iOS平台使用ffmpeg解码h264视频流

来源:http://www.aichengxu.com/view/37145 在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下. 对于视频文件和rtsp之类的主流视频传输协议,ffmpeg提供avformat_open_input接口,直接将文件路径或URL传入即可打开.读取视频数据.解码器初始参数设置等,都可以通过调用API来完成. 但是对于h264流,没有任何封装格式,也就无法使用libavformat.所以许多工作需要自己手工完成. 这里的h264流指AnnexB,也

如何在Android中播放网络传输过来的AAC音频?

============问题描述============ 如何在Android中播放网络传输过来的AAC音频?每个包都有ADTS头的. ============解决方案1============ 如果是应用的话,直接设置MediaPlayer setDataSource设置为网络音频的uri,然后prepareAsync(), prepare完成后调用start不就可以了吗? framework层的话还是要用aacdecoder进行解码播放,要么用软件解码,要么用硬件解码.

FFmpeg解码H264及swscale缩放详解

本文概要: 本文介绍著名开源音视频编解码库ffmpeg如何解码h264码流,比较详细阐述了其h264码流输入过程,解码原理,解码过程.同时,大部分应用环境下,以原始码流视频大小展示并不是最佳方式,因此,开发者不仅仅需要对视频流解码,并且需要缩放图像以展示于不同窗体下. 综上,本文除介绍ffmpeg解码h264,同时阐述如何使用swscale缩放视频流. 文章使用的开发环境Ubuntu12.04..交流邮箱:[email protected]. 转载请注明出处 CSDN--固本培元. ffmpeg

使用FFmpeg解码H264-2015.01.14

使用jni方式调用FFmepg项目中接口,对H264裸码进行解码. 该Demo主要实现从文件中读取H264编码的视频流,然后使用FFmpeg解码,将解码后的码流保存到文件. 工程目录结构如图所示: Android.mk文件内容如下 LOCAL_PATH := $(call my-dir) # FFmpeg library include $(CLEAR_VARS) LOCAL_MODULE := avcodec LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/liba