media-codec-decoder.cpp 8.55 KB
Newer Older
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 Mediastreamer2 media-codec-decoder.cpp
 Copyright (C) 2018 Belledonne Communications SARL

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19
*/

François Grisez's avatar
François Grisez committed
20

21
#include "android_mediacodec.h"
François Grisez's avatar
François Grisez committed
22

23
#include "media-codec-decoder.h"
24

25
using namespace mediastreamer;
26
using namespace std;
27

28 29
namespace mediastreamer {

30
MediaCodecDecoder::MediaCodecDecoder(const std::string &mime): H26xDecoder(mime) {
31
	try {
32 33 34 35 36 37 38
		_impl = AMediaCodec_createDecoderByType(mime.c_str());
		if (_impl == nullptr) {
			ostringstream msg;
			msg << "could not create MediaCodec for '" << mime << "'";
			throw runtime_error(msg.str());
		}
		_format = createFormat(mime);
39
		_bufAllocator = ms_yuv_buf_allocator_new();
40
		_naluHeader.reset(H26xToolFactory::get(mime).createNaluHeader());
41
		_psStore.reset(H26xToolFactory::get(mime).createParameterSetsStore());
42
		startImpl();
43
	} catch (const runtime_error &e) {
44 45 46
		if (_impl) AMediaCodec_delete(_impl);
		if (_format) AMediaFormat_delete(_format);
		if (_bufAllocator) ms_yuv_buf_allocator_free(_bufAllocator);
47
		_impl = nullptr;
48 49 50 51
	}
}

MediaCodecDecoder::~MediaCodecDecoder() {
52
	if (_impl) AMediaCodec_delete(_impl);
53 54 55
	ms_yuv_buf_allocator_free(_bufAllocator);
}

56
bool MediaCodecDecoder::setParameterSets(MSQueue *parameterSets, uint64_t timestamp) {
57
	if (!feed(parameterSets, timestamp, true)) {
58
		ms_error("MediaCodecDecoder: parameter sets has been refused by the decoder.");
59
		return false;
60
	}
61
	_needParameters = false;
62
	return true;
63
}
64

65
bool MediaCodecDecoder::feed(MSQueue *encodedFrame, uint64_t timestamp) {
66
	bool status = false;
67

68 69 70 71 72
	if (_impl == nullptr) {
		ms_queue_flush(encodedFrame);
		return true;
	}

73 74
	_psStore->extractAllPs(encodedFrame);
	if (_psStore->hasNewParameters()) {
75
		ms_message("MediaCodecDecoder: new paramter sets received");
76 77 78 79
		_needParameters = true;
		_psStore->acknowlege();
	}

80
	if (_needParameters && _psStore->psGatheringCompleted()) {
81 82 83 84
		MSQueue parameters;
		ms_queue_init(&parameters);
		_psStore->fetchAllPs(&parameters);
		if (!setParameterSets(&parameters, timestamp)) {
85
			ms_error("MediaCodecDecoder: waiting for parameter sets.");
86 87
			goto clean;
		}
88 89
	}

90 91 92 93 94
	if (_needParameters) {
		ms_error("MediaCodecDecoder: missing parameter sets");
		goto clean;
	}

95 96
	if (_needKeyFrame) {
		if (!isKeyFrame(encodedFrame)) {
97
			ms_error("MediaCodecDecoder: waiting for key frame.");
98 99
			goto clean;
		}
100
		ms_message("MediaCodecDecoder: key frame received");
101
		_needKeyFrame = false;
102
	}
103 104 105 106 107

	if (!feed(encodedFrame, timestamp, false)) {
		goto clean;
	}

108
	_pendingFrames++;
109 110 111
	status = true;

clean:
112
	ms_queue_flush(encodedFrame);
113
	return status;
114 115
}

116
MediaCodecDecoder::Status MediaCodecDecoder::fetch(mblk_t *&frame) {
117 118 119 120 121
	AMediaImage image = {0};
	int dst_pix_strides[4] = {1, 1, 1, 1};
	MSRect dst_roi = {0};
	AMediaCodecBufferInfo info;
	ssize_t oBufidx = -1;
122
	Status status = noError;
123

124 125 126
	frame = nullptr;

	if (_impl == nullptr || _pendingFrames <= 0) {
127
		status = noFrameAvailable;
128 129
		goto end;
	}
130 131

	oBufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
132
	while (oBufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED || oBufidx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
133
		ms_message("MediaCodecDecoder: %s", codecInfoToString(oBufidx).c_str());
134 135 136 137 138
		if (oBufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
			AMediaFormat *format = AMediaCodec_getOutputFormat(_impl);
			ms_message("MediaCodecDecoder: new format:\n%s", AMediaFormat_toString(format));
			AMediaFormat_delete(format);
		}
139 140 141 142
		oBufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
	}

	if (oBufidx < 0) {
143 144
		if (oBufidx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
			return noFrameAvailable;
145
		} else {
146
			ms_error("MediaCodecDecoder: error while dequeueing an output buffer: %s", codecInfoToString(oBufidx).c_str());
147 148 149 150 151 152 153 154 155 156
			if (oBufidx == AMEDIA_ERROR_UNKNOWN) {
				try {
					ms_message("MdeiaCodecDecoder: restarting decoding session");
					stopImpl();
					startImpl();
				} catch (const runtime_error &e) {
					ms_error("MediaCodecDecoder: session restart failed. %s", e.what());
					ms_error("MediaCodecDecoder: the session is definitively lost !");
				}
			}
157
			return decodingFailure;
158 159 160 161 162 163 164
		}
		goto end;
	}

	_pendingFrames--;

	if (AMediaCodec_getOutputImage(_impl, oBufidx, &image) <= 0) {
165
		ms_error("MediaCodecDecoder: AMediaCodec_getOutputImage() failed");
166
		status = decodingFailure;
167 168 169 170
		goto end;
	}

	MSPicture pic;
171
	frame = ms_yuv_buf_allocator_get(_bufAllocator, &pic, image.crop_rect.w, image.crop_rect.h);
172 173 174 175 176 177
	ms_yuv_buf_copy_with_pix_strides(image.buffers, image.row_strides, image.pixel_strides, image.crop_rect,
										pic.planes, pic.strides, dst_pix_strides, dst_roi);
	AMediaImage_close(&image);

end:
	if (oBufidx >= 0) AMediaCodec_releaseOutputBuffer(_impl, oBufidx, FALSE);
178
	return status;
179 180
}

181
AMediaFormat *MediaCodecDecoder::createFormat(const std::string &mime) const {
182 183 184 185 186
	AMediaFormat *format = AMediaFormat_new();
	AMediaFormat_setString(format, "mime", mime.c_str());
	AMediaFormat_setInt32(format, "color-format", 0x7f420888);
	AMediaFormat_setInt32(format, "max-width", 1920);
	AMediaFormat_setInt32(format, "max-height", 1920);
187
	AMediaFormat_setInt32(format, "priority", 0);
188 189
	return format;
}
190

191 192 193
void MediaCodecDecoder::startImpl() {
	media_status_t status = AMEDIA_OK;
	ostringstream errMsg;
194
	ms_message("MediaCodecDecoder: starting decoder with following parameters:\n%s", AMediaFormat_toString(_format));
195
	if ((status = AMediaCodec_configure(_impl, _format, nullptr, nullptr, 0)) != AMEDIA_OK) {
196
		errMsg << "configuration failure: " << int(status);
197
		throw runtime_error(errMsg.str());
198 199 200 201 202 203
	}

	if ((status = AMediaCodec_start(_impl)) != AMEDIA_OK) {
		errMsg << "starting failure: " << int(status);
		throw runtime_error(errMsg.str());
	}
204 205

	ms_message("MediaCodecDecoder: decoder successfully started. In-force parameters:\n%s", AMediaFormat_toString(_format));
206 207
}

208
void MediaCodecDecoder::stopImpl() {
209
	ms_message("MediaCodecDecoder: stopping decoder");
210 211 212
	AMediaCodec_stop(_impl);
}

213
bool MediaCodecDecoder::feed(MSQueue *encodedFrame, uint64_t timestamp, bool isPs) {
214 215
	H26xUtils::nalusToByteStream(encodedFrame, _bitstream);

216 217 218 219
	if (_impl == nullptr) return false;

	ssize_t iBufidx = AMediaCodec_dequeueInputBuffer(_impl, _timeoutUs);
	if (iBufidx < 0) {
220
		ms_error("MediaCodecDecoder: %s.", iBufidx == -1 ? "no buffer available for queuing this frame ! Decoder is too slow" : "AMediaCodec_dequeueInputBuffer() had an exception");
221 222 223 224 225 226
		return false;
	}

	size_t bufsize;
	uint8_t *buf = AMediaCodec_getInputBuffer(_impl, iBufidx, &bufsize);
	if (buf == nullptr) {
227
		ms_error("MediaCodecDecoder: AMediaCodec_getInputBuffer() returned NULL");
228 229 230
		return false;
	}

231
	size_t size = _bitstream.size();
232
	if (size > bufsize) {
233
		ms_error("MediaCodecDecoder: cannot copy the all the bitstream into the input buffer size : %zu and bufsize %zu", size, bufsize);
234 235
		size = min(size, bufsize);
	}
236
	memcpy(buf, _bitstream.data(), size);
237 238 239

	uint32_t flags = isPs ? BufferFlag::CodecConfig : BufferFlag::None;
	if (AMediaCodec_queueInputBuffer(_impl, iBufidx, 0, size, timestamp * 1000ULL, flags) != 0) {
240
		ms_error("MediaCodecDecoder: AMediaCodec_queueInputBuffer() had an exception");
241 242 243 244 245 246
		return false;
	}

	return true;
}

247 248
bool MediaCodecDecoder::isKeyFrame(const MSQueue *frame) const {
	for (const mblk_t *nalu = ms_queue_peek_first(frame); !ms_queue_end(frame, nalu); nalu = ms_queue_next(frame, nalu)) {
249 250 251 252 253 254
		_naluHeader->parse(nalu->b_rptr);
		if (_naluHeader->getAbsType().isKeyFramePart()) return true;
	}
	return false;
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
std::string MediaCodecDecoder::codecInfoToString(ssize_t codecStatusCode) {
	switch (codecStatusCode) {
		case AMEDIA_ERROR_UNKNOWN:
			return "MediaCodec had an exception";
		case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
			return "output buffers has changed";
		case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
			return "output format has changed";
		case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
			return "no output buffer available";
		default:
			break;
	}
	ostringstream os;
	if (codecStatusCode >= 0) {
		os << "unqueued buffer (index=" << codecStatusCode << ")";
	} else {
		os << "unknown error (" << codecStatusCode << ")";
	}
	return os.str();
}

277
} // namespace mediastreamer