Commit 3a0a26d2 authored by François Grisez's avatar François Grisez

Merge branch 'feature/h265_mediacodec_samsung'

Fix an error in h26x bytestream generation occuring when there at least
four consecutive zeros in a NAL unit. That lead to enteroperability
issues in any call involving a Samsung device.
parents 8223d878 e52e49ae
......@@ -83,6 +83,8 @@ struct AMediaFormat {
jmethodID getInteger;
jmethodID setString;
jmethodID containsKey;
jmethodID toString;
std::string description;
};
int handle_java_exception() {
......@@ -589,6 +591,7 @@ bool AMediaFormat_loadMethodID(AMediaFormat * format) {
success &= _getMethodID(env, mediaFormatClass, "getInteger", "(Ljava/lang/String;)I", &(format->getInteger));
success &= _getMethodID(env, mediaFormatClass, "setString", "(Ljava/lang/String;Ljava/lang/String;)V", &(format->setString));
success &= _getMethodID(env, mediaFormatClass, "containsKey", "(Ljava/lang/String;)Z", &(format->containsKey));
success &= _getMethodID(env, mediaFormatClass, "toString", "()Ljava/lang/String;", &(format->toString));
if(!success) {
ms_error("%s(): one method or field could not be found", __FUNCTION__);
goto error;
......@@ -634,6 +637,17 @@ media_status_t AMediaFormat_delete(AMediaFormat* format) {
return AMEDIA_OK;
}
const char *AMediaFormat_toString(AMediaFormat *format) {
JNIEnv *env = ms_get_jni_env();
jstring jdescription = static_cast<jstring>(env->CallObjectMethod(format->jformat, format->toString));
if (handle_java_exception() != 0) return nullptr;
const char *description = env->GetStringUTFChars(jdescription, nullptr);
format->description = description;
env->ReleaseStringUTFChars(jdescription, description);
env->DeleteLocalRef(jdescription);
return format->description.c_str();
}
bool AMediaFormat_getInt32(AMediaFormat *format, const char *name, int32_t *out){
JNIEnv *env = ms_get_jni_env();
bool hasValue;
......
......@@ -107,8 +107,7 @@ void H26xUtils::nalusToByteStream(MSQueue *nalus, std::vector<uint8_t> &byteStre
byteStream.push_back(0);
byteStream.push_back(0);
byteStream.push_back(3); // emulation prevention three byte
byteStream.push_back(src[2]);
src += 3;
src += 2;
} else {
byteStream.push_back(*src++);
}
......
......@@ -77,7 +77,7 @@ bool MediaCodecDecoder::feed(MSQueue *encodedFrame, uint64_t timestamp) {
_psStore->acknowlege();
}
if (_needParameters) {
if (_needParameters && _psStore->psGatheringCompleted()) {
MSQueue parameters;
ms_queue_init(&parameters);
_psStore->fetchAllPs(&parameters);
......@@ -87,11 +87,17 @@ bool MediaCodecDecoder::feed(MSQueue *encodedFrame, uint64_t timestamp) {
}
}
if (_needParameters) {
ms_error("MediaCodecDecoder: missing parameter sets");
goto clean;
}
if (_needKeyFrame) {
if (!isKeyFrame(encodedFrame)) {
ms_error("MediaCodecDecoder: waiting for key frame.");
goto clean;
}
ms_error("MediaCodecDecoder: key frame received");
_needKeyFrame = false;
}
......@@ -123,21 +129,22 @@ MediaCodecDecoder::Status MediaCodecDecoder::fetch(mblk_t *&frame) {
}
oBufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
if (oBufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
ms_message("MediaCodecDecoder: output format has changed.");
if (oBufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED || oBufidx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
ms_message("MediaCodecDecoder: %s", codecInfoToString(oBufidx).c_str());
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);
}
oBufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
}
if (oBufidx < 0) {
if (oBufidx == AMEDIA_ERROR_UNKNOWN) {
ms_error("MediaCodecDecoder: AMediaCodec_dequeueOutputBuffer() had an exception");
status = decodingFailure;
} else if (oBufidx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
ms_debug("MediaCodecDecoder: no output picture available");
status = noFrameAvailable;
if (oBufidx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
return noFrameAvailable;
} else {
ms_error("MediaCodecDecoder: unknown error while dequeueing an output buffer (oBufidx=%zd)", oBufidx);
status = noFrameAvailable;
ms_error("MediaCodecDecoder: error while dequeueing an output buffer: %s", codecInfoToString(oBufidx).c_str());
return decodingFailure;
}
goto end;
}
......@@ -174,7 +181,7 @@ AMediaFormat *MediaCodecDecoder::createFormat(const std::string &mime) const {
void MediaCodecDecoder::startImpl() {
media_status_t status = AMEDIA_OK;
ostringstream errMsg;
ms_message("MediaCodecDecoder: starting decoder");
ms_message("MediaCodecDecoder: starting decoder with following parameters:\n%s", AMediaFormat_toString(_format));
if ((status = AMediaCodec_configure(_impl, _format, nullptr, nullptr, 0)) != AMEDIA_OK) {
errMsg << "configuration failure: " << int(status);
throw runtime_error(errMsg.str());
......@@ -184,6 +191,8 @@ void MediaCodecDecoder::startImpl() {
errMsg << "starting failure: " << int(status);
throw runtime_error(errMsg.str());
}
ms_message("MediaCodecDecoder: decoder successfully started. In-force parameters:\n%s", AMediaFormat_toString(_format));
}
void MediaCodecDecoder::stopImpl() {
......@@ -211,7 +220,7 @@ bool MediaCodecDecoder::feed(MSQueue *encodedFrame, uint64_t timestamp, bool isP
size_t size = _bitstream.size();
if (size > bufsize) {
ms_error("Cannot copy the all the bitstream into the input buffer size : %zu and bufsize %zu", size, bufsize);
ms_error("MediaCodecDecoder: cannot copy the all the bitstream into the input buffer size : %zu and bufsize %zu", size, bufsize);
size = min(size, bufsize);
}
memcpy(buf, _bitstream.data(), size);
......@@ -233,4 +242,26 @@ bool MediaCodecDecoder::isKeyFrame(const MSQueue *frame) const {
return false;
}
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();
}
} // namespace mediastreamer
......@@ -19,6 +19,7 @@
#pragma once
#include <memory>
#include <vector>
#include "h26x-utils.h"
......@@ -54,6 +55,7 @@ protected:
void stopImpl();
bool feed(MSQueue *encodedFrame, uint64_t timestamp, bool isPs);
bool isKeyFrame(const MSQueue *frame) const;
static std::string codecInfoToString(ssize_t codecStatusCode);
AMediaCodec *_impl = nullptr;
AMediaFormat *_format = nullptr;
......
......@@ -150,13 +150,7 @@ void MediaCodecEncoder::feed(mblk_t *rawData, uint64_t time, bool requestIFrame)
return;
}
size_t bufsize;
uint8_t *buf = AMediaCodec_getInputBuffer(_impl, ibufidx, &bufsize);
if (buf == nullptr) {
ms_error("MediaCodecEncoder: obtained InputBuffer, but no address.");
return;
}
size_t bufsize = 0;
AMediaImage image;
if (AMediaCodec_getInputImage(_impl, ibufidx, &image)) {
if (image.format == 35 /* YUV_420_888 */) {
......@@ -192,6 +186,9 @@ bool MediaCodecEncoder::fetch(MSQueue *encodedData) {
ssize_t obufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
if (obufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
ms_message("MediaCodecEncoder: output format has changed.");
AMediaFormat *format = AMediaCodec_getOutputFormat(_impl);
ms_message("MediaCodecEncoder: new output format:\n%s", AMediaFormat_toString(format));
AMediaFormat_delete(format);
obufidx = AMediaCodec_dequeueOutputBuffer(_impl, &info, _timeoutUs);
}
if (obufidx < 0) {
......@@ -231,8 +228,7 @@ void MediaCodecEncoder::createImpl() {
void MediaCodecEncoder::configureImpl() {
AMediaFormat *format = createMediaFormat();
ms_message("MediaCodecEncoder: configuring MediaCodec with the following parameters:");
ms_message("%s", getMediaForamtAsString().str().c_str());
ms_message("MediaCodecEncoder: configuring MediaCodec with the following parameters:\n%s", AMediaFormat_toString(format));
media_status_t status = AMediaCodec_configure(_impl, format, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
AMediaFormat_delete(format);
......@@ -241,7 +237,9 @@ void MediaCodecEncoder::configureImpl() {
throw runtime_error("could not configure encoder.");
}
ms_message("MediaCodecEncoder: encoder successfully configured.");
format = AMediaCodec_getOutputFormat(_impl);
ms_message("MediaCodecEncoder: encoder successfully configured. In-force parameters:\n%s", AMediaFormat_toString(format));
AMediaFormat_delete(format);
}
AMediaFormat *MediaCodecEncoder::createMediaFormat() const {
......@@ -259,26 +257,4 @@ AMediaFormat *MediaCodecEncoder::createMediaFormat() const {
return format;
}
std::ostringstream MediaCodecEncoder::getMediaForamtAsString() const {
ostringstream os;
os << "\tmime: " << _mime << endl;
os.unsetf(ios::basefield);
os.setf(ios::hex);
os.setf(ios::showbase);
os << "\tcolor-format: " << _colorFormat << endl;
os.unsetf(ios::basefield);
os.setf(ios::dec);
os.unsetf(ios::showbase);
os << "\tvideo size: " << _vsize.width << "x" << _vsize.height << endl;
os << "\tframe-rate: " << _fps << " fps" << endl;
os << "\tbitrate: " << _bitrate << " b/s" << endl;
os << "\tbitrate-mode: " << _bitrateMode << endl;
os << "\ti-frame-intervale: " << _iFrameInterval << endl;
os << "\tlatency: " << _encodingLatency << endl;
os << "\tpriority: " << _priority << endl;
return os;
}
} // namespace mediastreamer
......@@ -55,7 +55,6 @@ protected:
void createImpl();
void configureImpl();
virtual AMediaFormat *createMediaFormat() const;
virtual std::ostringstream getMediaForamtAsString() const;
std::unique_ptr<H26xParameterSetsInserter> _psInserter;
MSVideoSize _vsize;
......
......@@ -51,12 +51,6 @@ private:
return format;
}
std::ostringstream getMediaForamtAsString() const override {
std::ostringstream os = MediaCodecEncoder::getMediaForamtAsString();
os << "\tprofile: " << _profile << std::endl;
return os;
}
static const int32_t _profile = 1; // AVCProfileBaseline
static const int32_t _level = 512; // AVCLevel31
};
......
......@@ -64,9 +64,7 @@ static std::vector<uint8_t> loadFrameByteStream(const std::string &frame) {
return byteStream;
}
static void bytestream_transcoding_test(const std::string &frame) {
vector<uint8_t> byteStream = loadFrameByteStream(frame);
static void bytestream_transcoding_test(const std::vector<uint8_t> &byteStream) {
MSQueue nalus;
ms_queue_init(&nalus);
H26xUtils::byteStreamToNalus(byteStream, &nalus);
......@@ -81,14 +79,21 @@ static void bytestream_transcoding_test(const std::string &frame) {
}
static void paramter_sets_bytestream_transcoding_test() {
bytestream_transcoding_test("h265-parameter-sets-frame");
vector<uint8_t> byteStream = loadFrameByteStream("h265-parameter-sets-frame");
bytestream_transcoding_test(byteStream);
}
static void iframe_bytestream_transcoding_test() {
bytestream_transcoding_test("h265-iframe");
vector<uint8_t> byteStream = loadFrameByteStream("h265-iframe");
bytestream_transcoding_test(byteStream);
}
static void bytestream_transcoding_two_consecutive_prevention_thee_bytes() {
vector<uint8_t> byteStream = {0, 0, 0, 1, 0, 0, 3, 0, 0, 3, 0};
bytestream_transcoding_test(byteStream);
}
static void packing_unpacking_test(const std::string &frame, const std::string &mime) {
static void packing_unpacking_test(const std::vector<uint8_t> &byteStream, const std::string &mime) {
MSQueue nalus, rtp;
ms_queue_init(&nalus);
......@@ -100,7 +105,6 @@ static void packing_unpacking_test(const std::string &frame, const std::string &
packer->setPacketizationMode(NalPacker::NonInterleavedMode);
packer->enableAggregation(true);
vector<uint8_t> byteStream = loadFrameByteStream(frame);
H26xUtils::byteStreamToNalus(byteStream, &nalus);
packer->pack(&nalus, &rtp, 0);
......@@ -126,16 +130,19 @@ static void packing_unpacking_test(const std::string &frame, const std::string &
}
static void packing_unpacking_test_h265_ps() {
packing_unpacking_test("h265-parameter-sets-frame", "video/hevc");
vector<uint8_t> byteStream = loadFrameByteStream("h265-parameter-sets-frame");
packing_unpacking_test(byteStream, "video/hevc");
}
static void packing_unpacking_test_h265_iframe() {
packing_unpacking_test("h265-iframe", "video/hevc");
vector<uint8_t> byteStream = loadFrameByteStream("h265-iframe");
packing_unpacking_test(byteStream, "video/hevc");
}
static test_t tests[] = {
TEST_NO_TAG("Bytestream transcoding - paramter sets frame", paramter_sets_bytestream_transcoding_test),
TEST_NO_TAG("Bytestream transcoding - i-frame", iframe_bytestream_transcoding_test),
TEST_NO_TAG("Bytestream transcoding - two consecutive prevention three bytes", bytestream_transcoding_two_consecutive_prevention_thee_bytes),
TEST_NO_TAG("H265 Packing/Unpacking - paramter sets frame", packing_unpacking_test_h265_ps),
TEST_NO_TAG("H265 Packing/Unpacking - i-frame", packing_unpacking_test_h265_iframe)
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment