media-codec-h264-decoder.cpp 6.53 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 Mediastreamer2 media-codec-h264-decoder.cpp
 Copyright (C) 2015 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.
*/

20
#include <algorithm>
21 22 23 24
#include <cstring>

#include <sys/system_properties.h>

25 26
#include <ortp/b64.h>

27
#include "filter-wrapper/decoding-filter-wrapper.h"
François Grisez's avatar
François Grisez committed
28
#include "h26x-decoder-filter.h"
29
#include "h264-nal-unpacker.h"
François Grisez's avatar
François Grisez committed
30
#include "h264-utils.h"
31

32
#include "media-codec-h264-decoder.h"
33 34

using namespace b64;
35
using namespace std;
36 37 38

namespace mediastreamer {

39 40 41
MediaCodecH264Decoder::MediaCodecH264Decoder(): MediaCodecDecoder("video/avc") {
	DeviceInfo info = getDeviceInfo();
	ms_message("MediaCodecH264Decoder: got device info: %s", info.toString().c_str());
42
	if (find(_tvDevices.cbegin(), _tvDevices.cend(), info) != _tvDevices.cend()) {
43 44 45 46 47
		ms_message("MediaCodecH264Decoder: enabling reset on new SPS/PPS mode");
		_resetOnPsReceiving = true;
	}
}

48 49 50 51
MediaCodecH264Decoder::~MediaCodecH264Decoder() {
	if (_lastSps) freemsg(_lastSps);
}

52
bool MediaCodecH264Decoder::setParameterSets(MSQueue *parameterSet, uint64_t timestamp) {
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	if (_resetOnPsReceiving) {
		for (mblk_t *m = ms_queue_peek_first(parameterSet); !ms_queue_end(parameterSet, m); m = ms_queue_next(parameterSet, m)) {
			MSH264NaluType type = ms_h264_nalu_get_type(m);
			if (type == MSH264NaluTypeSPS && isNewPps(m)) {
				int32_t curWidth, curHeight;
				AMediaFormat_getInt32(_format, "width", &curWidth);
				AMediaFormat_getInt32(_format, "height", &curHeight);
				MSVideoSize vsize = ms_h264_sps_get_video_size(m);
				if (vsize.width != curWidth || vsize.height != curHeight) {
					ms_message("MediaCodecDecoder: restarting decoder because the video size has changed (%dx%d->%dx%d)",
					           curWidth,
					           curHeight,
					           vsize.width,
					           vsize.height
					          );
					AMediaFormat_setInt32(_format, "width", vsize.width);
					AMediaFormat_setInt32(_format, "height", vsize.height);
					stopImpl();
					startImpl();
				}
73
			}
74 75
		}
	}
76
	return MediaCodecDecoder::setParameterSets(parameterSet, timestamp);
77 78
}

79
bool MediaCodecH264Decoder::DeviceInfo::operator==(const DeviceInfo &info) const {
80 81 82 83 84 85 86 87 88 89 90
	return this->manufacturer == info.manufacturer
		&& this->model == info.model
		&& this->platform == info.platform;
}

std::string MediaCodecH264Decoder::DeviceInfo::toString() const {
	ostringstream os;
	os << "{ '" << this->manufacturer << "', '" << this->model << "', '" << this->platform << "' }";
	return os.str();
}

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
bool MediaCodecH264Decoder::isNewPps(mblk_t *sps) {
	if (_lastSps == nullptr) {
		_lastSps = dupmsg(sps);
		return true;
	}
	const size_t spsSize = size_t(sps->b_wptr - sps->b_rptr);
	const size_t lastSpsSize = size_t(_lastSps->b_wptr - _lastSps->b_rptr);
	if (spsSize != lastSpsSize || memcmp(_lastSps->b_rptr, sps->b_rptr, spsSize) != 0) {
		freemsg(_lastSps);
		_lastSps = dupmsg(sps);
		return true;
	}
	return false;
}

106 107 108 109 110 111 112 113 114 115 116 117 118
MediaCodecH264Decoder::DeviceInfo MediaCodecH264Decoder::getDeviceInfo() {
	const size_t propSize = 256;
	char manufacturer[propSize];
	char model[propSize];
	char platform[propSize];

	if (__system_property_get("ro.product.manufacturer", manufacturer) < 0) manufacturer[0] = '\0';
	if (__system_property_get("ro.product.model", model) < 0) model[0] = '\0';
	if (__system_property_get("ro.board.platform", platform) < 0) platform[0] = '\0';

	return DeviceInfo({manufacturer, model, platform});
}

François Grisez's avatar
François Grisez committed
119
class MediaCodecH264DecoderFilterImpl: public H26xDecoderFilter {
120
public:
121
	MediaCodecH264DecoderFilterImpl(MSFilter *f): H26xDecoderFilter(f, new MediaCodecH264Decoder()) {}
122 123 124 125 126 127
	~MediaCodecH264DecoderFilterImpl() {
		if (_sps) freemsg(_sps);
		if (_pps) freemsg(_pps);
	}

	void process() {
128
		if (_sps && _pps) {
129 130 131 132
			static_cast<H264NalUnpacker &>(*_unpacker).setOutOfBandSpsPps(_sps, _pps);
			_sps = nullptr;
			_pps = nullptr;
		}
François Grisez's avatar
François Grisez committed
133
		H26xDecoderFilter::process();
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 163 164 165 166 167 168 169 170 171
	}

	void addFmtp(const char *fmtp) {
		char value[256];
		if (fmtp_get_value(fmtp, "sprop-parameter-sets", value, sizeof(value))) {
			char *b64_sps = value;
			char *b64_pps = strchr(value, ',');

			if (b64_pps) {
				*b64_pps = '\0';
				++b64_pps;
				ms_message("Got sprop-parameter-sets : sps=%s , pps=%s", b64_sps, b64_pps);
				_sps = allocb(sizeof(value), 0);
				_sps->b_wptr += b64_decode(b64_sps, strlen(b64_sps), _sps->b_wptr, sizeof(value));
				_pps = allocb(sizeof(value), 0);
				_pps->b_wptr += b64_decode(b64_pps, strlen(b64_pps), _pps->b_wptr, sizeof(value));
			}
		}
	}

private:
	void updateSps(mblk_t *sps) {
		if (_sps) freemsg(_sps);
		_sps = dupb(sps);
	}

	void updatePps(mblk_t *pps) {
		if (_pps) freemsg(_pps);
		if (pps) _pps = dupb(pps);
		else _pps = nullptr;
	}

	bool checkSpsChange(mblk_t *sps) {
		bool ret = false;
		if (_sps) {
			ret = (msgdsize(sps) != msgdsize(_sps)) || (memcmp(_sps->b_rptr, sps->b_rptr, msgdsize(sps)) != 0);

			if (ret) {
172
				ms_message("MediaCodecDecoder: SPS changed ! %i,%i", (int)msgdsize(sps), (int)msgdsize(_sps));
173 174 175 176
				updateSps(sps);
				updatePps(nullptr);
			}
		} else {
177
			ms_message("MediaCodecDecoder: receiving first SPS");
178 179 180 181 182 183 184 185 186 187 188
			updateSps(sps);
		}
		return ret;
	}

	bool checkPpsChange(mblk_t *pps) {
		bool ret = false;
		if (_pps) {
			ret = (msgdsize(pps) != msgdsize(_pps)) || (memcmp(_pps->b_rptr, pps->b_rptr, msgdsize(pps)) != 0);

			if (ret) {
189
				ms_message("MediaCodecDecoder: PPS changed ! %i,%i", (int)msgdsize(pps), (int)msgdsize(_pps));
190 191 192
				updatePps(pps);
			}
		} else {
193
			ms_message("MediaCodecDecoder: receiving first PPS");
194 195 196 197 198 199 200 201 202
			updatePps(pps);
		}
		return ret;
	}

	mblk_t *_sps = nullptr;
	mblk_t *_pps = nullptr;
};

203 204 205 206 207
const std::vector<const MediaCodecH264Decoder::DeviceInfo> MediaCodecH264Decoder::_tvDevices = {
	{"Amlogic", "Quad-Core Enjoy TV Box", "gxl"},
	{"rockchip", "X9-LX", "rk3288"}
};

208 209 210 211
}

using namespace mediastreamer;

212 213
MS_DECODING_FILTER_WRAPPER_METHODS_DECLARATION(MediaCodecH264Decoder);
MS_DECODING_FILTER_WRAPPER_DESCRIPTION_DECLARATION(MediaCodecH264Decoder, MS_MEDIACODEC_H264_DEC_ID, "A H264 decoder based on MediaCodec API.", "H264", MS_FILTER_IS_PUMP);