Android jni基于NdkMediaCodec硬解码mjpeg

NdkMediaCodec需要安卓5.0以上系统才能运行,在Android.mk里加入

1
LOCAL_LDLIBS += -lmediandk

如果是在安卓5.0以下系统中跑,程序会直接崩掉,主函数都不会进,所以要在旧手机中运行,要根据系统版本来判断是不是5.0以上系统,如果是则动态加载libmediandk.so,而不要写死在编译脚本里。

还有,要看手机上有没有解mjpeg的硬解码器,通过以下java代码来查看

1
2
3
4
5
6
7
8
9
10
11
12
int n = MediaCodecList.getCodecCount();
for (int i = 0; i < n; ++i) {
	MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
	String[] supportedTypes = info.getSupportedTypes();
	boolean mime_support = false;
	if(info.isEncoder()){
		continue;
	}
	for (int j = 0; j < supportedTypes.length; ++j) {
		Log.v("euhat", "codec info:" + info.getName()+" supportedTypes:" + supportedTypes[j]);
	}
}

以下是jni硬解码代码,因为没找到支持mjpeg硬解的手机,所以还没实际跑起来过。
但实际测试过程中,新的手机上软解1920x1080的mjpeg输出yuv都很流畅了。

DecodeOp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once
 
class AMediaCodec;
 
class DecodeOp
{
    int mWidth;
    int mHeight;
    int mOriFrameSize;
    int mFrameSize;
 
public:
    int init(int width, int height);
    int fini();
    int decode(char *bufData, int bufSize, char *outBuf);
 
    AMediaCodec *mMediaCodec;
};

DecodeOp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "DecodeOp.h"
#include <media/NdkMediaCodec.h>
#include "CommonOp.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
#define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a))
 
int DecodeOp::init(int width, int height)
{
    const char *mine = "video/mjpeg";
    mMediaCodec = AMediaCodec_createDecoderByType(mine);
    //注意这里,如果系统不存在对应的mine,mMediaCodec也会返回对象,
    //不过之后涉及到mMediaCodec的任何函数都会直接导致程序崩掉。
    if (NULL == mMediaCodec)
    {
        return 0;
    }
 
    AMediaFormat *videoFormat = AMediaFormat_new();
    AMediaFormat_setString(videoFormat, "mime", "video/mjpeg");
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_WIDTH, width);
    AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_HEIGHT, height);
    media_status_t status = AMediaCodec_configure(mMediaCodec, videoFormat, NULL, NULL, 0);
    if (status != AMEDIA_OK)
    {
        AMediaCodec_delete(mMediaCodec);
        mMediaCodec = NULL;
        return 0;
    }
 
    mWidth = width;
    mHeight = height;
    mOriFrameSize = width * height * 3 / 2;
    mFrameSize = mOriFrameSize;
 
    status = AMediaCodec_start(mMediaCodec);
    if (status != AMEDIA_OK)
    {
        AMediaCodec_delete(mMediaCodec);
        mMediaCodec = NULL;
        return 0;
    }
 
    return 1;
}
 
int DecodeOp::fini()
{
    AMediaCodec_stop(mMediaCodec);
    AMediaCodec_delete(mMediaCodec);
    mMediaCodec = NULL;
    return 1;
}
 
int DecodeOp::decode(char *bufData, int bufSize, char *outBuf)
{
    if (NULL == mMediaCodec)
        return 0;
 
    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mMediaCodec, 2000);
    if (bufidx >= 0) {
        // 获取buffer的索引
        size_t outsize;
        uint8_t* inputBuf = AMediaCodec_getInputBuffer(mMediaCodec, bufidx, &outsize);
        if (inputBuf != nullptr && bufSize <= outsize) {
            // 将待解码的数据copy到硬件中
            memcpy(inputBuf, bufData, bufSize);
            media_status_t status = AMediaCodec_queueInputBuffer(mMediaCodec, bufidx, 0, bufSize, 2000 /* pts */, 0);
        }
    }
 
    AMediaCodecBufferInfo info;
    ssize_t outbufidx = AMediaCodec_dequeueOutputBuffer(mMediaCodec, &info, 2000);
    if (outbufidx >= 0) {
        size_t outsize;
        uint8_t* outputBuf = AMediaCodec_getOutputBuffer(mMediaCodec, outbufidx, &outsize);
        if (outputBuf != nullptr) {
            //pts = info.presentationTimeUs;
            //int32_t pts32 = (int32_t) pts;
            uint8_t *dst = (uint8_t *)outBuf;
            memcpy(dst, outputBuf, MIN(mOriFrameSize, mFrameSize));
 
            uint8_t *uvBuf = outputBuf + mFrameSize;
 
            uint32_t uvSize = MIN(mOriFrameSize >> 1, mFrameSize >> 1);
            uint8_t *uBuf = dst  + mOriFrameSize;
            uint8_t *vBuf = uBuf + (mOriFrameSize >> 2);
            memcpy(uBuf, uvBuf, uvSize);
            AMediaCodec_releaseOutputBuffer(mMediaCodec, outbufidx, info.size != 0);
            return 1;
        }
    }
    else {
        switch (outbufidx) {
            case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
            // 解码输出的格式发生变化
            {
                auto format = AMediaCodec_getOutputFormat(mMediaCodec);
                AMediaFormat_getInt32(format, "width", &mWidth);
                AMediaFormat_getInt32(format, "height", &mHeight);
                int32_t localColorFMT;
                AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &localColorFMT);
                /*
                mColorFormat = getTTFormatFromMC(localColorFMT);
                int32_t stride = 0;
                AMediaFormat_getInt32(format, "stride", &stride);
                if(stride == 0) {
                    stride = mWidth;
                }
                mLineSize[0] = stride;
                mFrameSize    = stride * mHeight;
                mOriFrameSize = stride * mOriHeight;
                */
                return 0;
            }
            case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
                break;
            default:
                break;
        }
    }
    return 1;