0%

记一次 mtk native mediacodec 使用的坑

问题

在基于红米9做 native 层的 mediacodec 开发的时候发现在复用同一个 mediacodec 对象的时候会出现无法编码出帧的情况

测试代码

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
static int mediacodec_encode_header(AMediaCodec* codec, AMediaFormat* format);
static int mediacodec_encode_receive_packet(AMediaCodec* codec);
static void mediacodec_encode_send_frame(AMediaCodec* codec);

void TestRun() {
AMediaCodec* codec = NULL;
AMediaFormat* format = NULL;

do {
const char* mime = "video/avc";
codec = AMediaCodec_createEncoderByType(mime);
if (!codec) {
break;
}

format = AMediaFormat_new();
if (!format) {
break;
}

AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "video/avc");
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, 720);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, 1280);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, 21);//19 yuv420p 21 nv12
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1);//FIXME
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 2000000);
AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, 25);
AMediaFormat_setInt32(format, "bitrate-mode", 1);

LOGI("%s %d format: %s", __FUNCTION__, __LINE__, AMediaFormat_toString(format));

mediacodec_encode_header(codec, format);

media_status_t status = AMEDIA_OK;
if ((status = AMediaCodec_configure(codec, format, NULL, 0, AMEDIACODEC_CONFIGURE_FLAG_ENCODE)) != AMEDIA_OK) {
LOGI("%s %d status: %d", __FUNCTION__, __LINE__, status);
break;
}

status = AMediaCodec_start(codec);
if (status != AMEDIA_OK) {
LOGI("%s %d status: %d", __FUNCTION__, __LINE__, status);
break;
}

int cnt = 10;
while (cnt -- ) {
mediacodec_encode_send_frame(codec);
mediacodec_encode_receive_packet(codec);
usleep(10000);
}

LOGI("%s %d", __FUNCTION__, __LINE__);

} while (false);

if (format) {
AMediaFormat_delete(format);
}

if (codec) {
AMediaCodec_stop(codec);
AMediaCodec_delete(codec);
}
}


static void mediacodec_encode_send_frame(AMediaCodec* codec) {
ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, TIMEOUT_USEC);
if (bufferIndex < 0) {
LOGE("%s %d No input buffers available (%d)", __FUNCTION__, __LINE__, bufferIndex);
return;
}

size_t bufferSize = 0;
uint8_t* buffer = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufferSize);
if (!buffer) {
LOGE("%s %d AMediaCodec_getInputBuffer failed idx: %d !", __FUNCTION__, __LINE__, bufferIndex);
return;
}

static uint64_t stamp = 0;
media_status_t status = AMediaCodec_queueInputBuffer(codec, bufferIndex, 0, bufferSize, stamp, 0);
stamp += 30000;

LOGI("%s %d buffInfo (idx: %u size: %u)", __FUNCTION__, __LINE__, bufferIndex, bufferSize);
}

static int mediacodec_encode_receive_packet(AMediaCodec* codec) {
int try_times = 5;
while (try_times --) {
int buff_idx = -1;

do {
AMediaCodecBufferInfo bufferInfo;
buff_idx = AMediaCodec_dequeueOutputBuffer(codec, &bufferInfo, TIMEOUT_USEC);
if (buff_idx < 0) {
if (buff_idx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
//AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED = -3,
// AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED = -2,
LOGE("%s %d AMediaCodec_dequeueOutputBuffer idx: %d", __FUNCTION__, __LINE__, buff_idx);
}
break;
}

if (bufferInfo.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
LOGE("%s %d Got EOS at output", __FUNCTION__, __LINE__);
break;
}

size_t buff_size = 0;
uint8_t *buffer = AMediaCodec_getOutputBuffer(codec, buff_idx, &buff_size);
if (!buffer) {
LOGE("%s %d AMediaCodec_getOutputBuffer failed, flags: %d status: %d size: %u",
__FUNCTION__, __LINE__, bufferInfo.flags, buff_idx, buff_size);
break;
}

bool config_frame = bufferInfo.flags & LOCAL_BUFFER_FLAG_CODECCONFIG ? true : false;
bool key_frame = bufferInfo.flags & LOCAL_BUFFER_FLAG_SYNCFRAME ? true : false;

LOGI("%s %d AMediaCodec OutputBuffer idx: %d outsize: %u flags: %d offset: %d size: %d pts: %lld nalu: [%x %x %x %x %x %x]",
__FUNCTION__, __LINE__, buff_idx, buff_size, bufferInfo.flags, bufferInfo.offset,
bufferInfo.size, bufferInfo.presentationTimeUs, buffer[0], buffer[1], buffer[2],
buffer[3], buffer[4], buffer[5]);
} while (false);

if (buff_idx >= 0) {
AMediaCodec_releaseOutputBuffer(codec, buff_idx, false);
}
}
return 0;
}

int mediacodec_encode_header(AMediaCodec* codec, AMediaFormat* format) {
int ret = -1;
do {
media_status_t status = AMEDIA_OK;
if ((status = AMediaCodec_configure(codec, format, NULL, 0, AMEDIACODEC_CONFIGURE_FLAG_ENCODE)) != AMEDIA_OK) {
LOGI("%s %d status: %d", __FUNCTION__, __LINE__, status);
break;
}

status = AMediaCodec_start(codec);
if (status != AMEDIA_OK) {
LOGI("%s %d status: %d", __FUNCTION__, __LINE__, status);
break;
}

LOGI("%s %d", __FUNCTION__, __LINE__);
mediacodec_encode_send_frame(codec);
mediacodec_encode_receive_packet(codec);
LOGI("%s %d", __FUNCTION__, __LINE__);
ret = 0;
} while (false);

LOGI("%s %d", __FUNCTION__, __LINE__);
// AMediaCodec_flush(codec);
AMediaCodec_stop(codec);
LOGI("%s %d", __FUNCTION__, __LINE__);
return -1;
}

现象

在调用 AMediaCodec_stop 之后,再调用 AMediaCodec_configure & AMediaCodec_start 重新启动编码器,会无法编码出帧;
控制台日志如下:

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
08-20 16:46:11.661   706  7344 D MtkOmxVenc: [0xe6174e80] # Got general command (OMX_CommandStateSet) (2)
08-20 16:46:11.662 706 7346 D MtkOmxVencV4L2: [0xe6174e80] NOTIFY: FrameCount=1/1/1/1 BsCount=14/12/2/2 mEncodeFlags 0x3
08-20 16:46:11.662 706 7346 D MtkOmxVencV4L2: [0xe6174e80] NOTIFY: FrameCount=1/1/1/1 BsCount=14/13/2/2 mEncodeFlags 0x3
08-20 16:46:11.662 706 7344 D MtkOmxVencV4L2: [0xe6174e80] @@ Send Stop Command
08-20 16:46:11.662 706 7344 D MtkOmxVencV4L2: [0xe6174e80] + waitFlushDone mEncodeFlags 3 mEncoderInitCompleteFlag 1 mIsComponentAlive 1
08-20 16:46:11.662 706 7346 E MtkOmxVencV4L2: [0xe6174e80] poll event venc: 2
08-20 16:46:11.662 706 7346 D MtkOmxVencV4L2: [0xe6174e80] NOTIFY: FrameCount=1/1/1/1 BsCount=14/13/3/3 mEncodeFlags 0x3
08-20 16:46:11.662 706 7346 D MtkOmxVencV4L2: [0xe6174e80] STREAM OFF 2
08-20 16:46:11.665 706 7346 D MtkOmxVencV4L2: [0xe6174e80] STREAM OFF 1
08-20 16:46:11.665 706 7346 D MtkOmxVencV4L2: [0xe6174e80] MtkOmxVencDequeThread terminated pVenc=0xE6174E80 hint 4
08-20 16:46:11.665 706 7344 D MtkOmxVencV4L2: [0xe6174e80] - waitFlushDone mEncodeFlags 5 mEncoderInitCompleteFlag 1 mIsComponentAlive 1
08-20 16:46:11.666 706 7344 D MtkOmxVencV4L2: [0xe6174e80] STREAM OFF 1
08-20 16:46:11.666 706 7344 D MtkOmxVencV4L2: [0xe6174e80] STREAM OFF 2
08-20 16:46:11.666 706 7344 D MtkOmxVencV4L2: [0xe6174e80] STREAM OFF 3
08-20 16:46:11.667 706 7345 D MtkOmxVencV4L2: [0xe6174e80] MtkOmxVencEncodeThread terminated pVenc=0xE6174E80 hint 4
08-20 16:46:11.667 706 7344 D MtkOmxVenc: [0xe6174e80] # Got general command (OMX_CommandStateSet) (1)
08-20 16:46:11.680 706 914 E MtkOmxVenc: [0xe6174e80] MtkOmxVenc::SetParameter unsupported nParamIndex(0x7F000004)
08-20 16:46:11.680 706 914 E OMXNodeInstance: setParameter(0xe6174e84:MTK.ENCODER.AVC, OMX.google.android.index.enableAndroidNativeBuffers(0x7f000004): Input:0 en=0) ERROR: UnsupportedIndex(0x8000101a)
08-20 16:46:11.680 706 914 E MtkOmxVenc: [0xe6174e80] OMX_GoogleAndroidIndexAllocateAndroidNativeHandle: does not support input port
08-20 16:46:11.680 706 914 E OMXNodeInstance: setParameter(0xe6174e84:MTK.ENCODER.AVC, OMX.google.android.index.allocateNativeHandle(0x7f20040e): Input:0 en=0) ERROR: BadParameter(0x80001005)
08-20 16:46:11.680 706 8380 E MtkOmxVenc: [0xe6174e80] MtkOmxVenc::SetParameter unsupported nParamIndex(0x7F000004)
08-20 16:46:11.680 706 8380 E OMXNodeInstance: setParameter(0xe6174e84:MTK.ENCODER.AVC, OMX.google.android.index.enableAndroidNativeBuffers(0x7f000004): Output:1 en=0) ERROR: UnsupportedIndex(0x8000101a)
08-20 16:46:11.681 706 8380 E OMXNodeInstance: getParameter(0xe6174e84:MTK.ENCODER.AVC, ??(0x7f200405)) ERROR: UnsupportedIndex(0x8000101a)
08-20 16:46:11.682 706 914 D MtkOmxVenc: [0xe6174e80] getInputBufferSizeByFormat: 1413120
08-20 16:46:11.682 706 914 D MtkOmxVenc: [0xe6174e80] @@ Set input port: nFrameWidth(1280), nFrameHeight(720), nStride(1280), nSliceHeight(720), nBitrate(64000), xFramerate(0x190000), eColorFormat(0x00000015), nBufferSize(1413120), nBufferCountActual(12)
08-20 16:46:11.682 7009 7343 I MtkACodec: setMTKParameters, width: 1280
08-20 16:46:11.682 7009 7343 I MtkACodec: setMTKParameters, height: 720
08-20 16:46:11.682 7009 7343 I ACodec_roi: + setRoiParameters 239
08-20 16:46:11.682 7009 7343 I ACodec_roi: - setRoiParameters 245
08-20 16:46:11.682 706 8380 D MtkOmxVenc: [0xe6174e80] @@ Set output port: nFrameWidth(1280), nFrameHeight(720), nStride(176), nSliceHeight(144),nBitrate(2000000), xFramerate(0x0), compressionFormat(0x00000007), nBufferSize(1572864),nBufferCountActual(12)
08-20 16:46:11.686 7009 7343 I ACodec : setupAVCEncoderParameters with [profile: High] [level: Level41]
08-20 16:46:11.686 706 914 E MtkOmxVenc: [0xe6174e80] MtkOmxVenc::GetExtensionIndex Unknown parameter name: OMX.google.android.index.describeColorAspects
08-20 16:46:11.686 7009 7343 I ACodec : [OMX.MTK.VIDEO.ENCODER.AVC] cannot encode color aspects. Ignoring.
08-20 16:46:11.687 706 914 E MtkOmxVenc: [0xe6174e80] MtkOmxVenc::GetExtensionIndex Unknown parameter name: OMX.google.android.index.describeHDRStaticInfo
08-20 16:46:11.687 7009 7343 I ACodec : [OMX.MTK.VIDEO.ENCODER.AVC] cannot encode HDR static metadata. Ignoring.
08-20 16:46:11.687 7009 7343 I ACodec : setupVideoEncoder succeeded

重启 mediacodec 之后 setupVideoEncoder 虽然成功了,但是却无法正常使用,推测是 mediacodec 线程同步异常, 状态没清理干净。

建议

为了确保兼容性,还是不要使用停止和重启的方式来复用 mediacodec,每次需要使用的时候创建一个;

用户的需求就是我的追求!