Commit 9e620f06 authored by François Grisez's avatar François Grisez
Browse files

Moves frame analysing outside the H264Unpacker class.

parent d41e7cbd
......@@ -17,11 +17,15 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "h264utils.h"
#include <mediastreamer2/msqueue.h>
#include <math.h>
// #include <ortp/str_utils.h>
#include "mediastreamer2/bits_rw.h"
#include "mediastreamer2/msqueue.h"
#include "mediastreamer2/rfc3984.h"
#include <math.h>
#include "h264utils.h"
extern "C" {
......@@ -216,4 +220,59 @@ MSVideoSize ms_h264_sps_get_video_size(const mblk_t *sps) {
return video_size;
}
};
} // extern "C"
namespace mediastreamer2 {
unsigned int H264FrameAnalyser::Info::toUInt() const {
unsigned int res = 0;
if (this->hasIdr) res |= Rfc3984HasIDR;
if (this->hasSps) res |= Rfc3984HasSPS;
if (this->hasPps) res |= Rfc3984HasPPS;
if (this->newSps) res |= Rfc3984NewSPS;
if (this->newPps) res |= Rfc3984NewPPS;
return res;
}
H264FrameAnalyser::~H264FrameAnalyser() {
if (_lastSps) freemsg(_lastSps);
if (_lastPps) freemsg(_lastPps);
}
H264FrameAnalyser::Info H264FrameAnalyser::analyse(const MSQueue *frame) {
Info info;
for (const mblk_t *nalu = qbegin(&frame->q); !qend(&frame->q, nalu); nalu = qnext(&frame->q, nalu)) {
MSH264NaluType type = ms_h264_nalu_get_type(nalu);
if (type == MSH264NaluTypeIDR) {
info.hasIdr = true;
} else if (type == MSH264NaluTypeSPS) {
info.hasSps = true;
info.newSps = updateParameterSet(nalu);
} else if (type == MSH264NaluTypePPS) {
info.hasPps = true;
info.newPps = updateParameterSet(nalu);
}
}
return info;
}
bool H264FrameAnalyser::updateParameterSet(const mblk_t *new_parameter_set) {
mblk_t *&last_parameter_set = ms_h264_nalu_get_type(new_parameter_set) == MSH264NaluTypePPS ? _lastPps : _lastSps;
if (last_parameter_set != nullptr) {
size_t last_size = last_parameter_set->b_wptr - last_parameter_set->b_rptr;
size_t new_size = new_parameter_set->b_wptr - new_parameter_set->b_rptr;
if (last_size != new_size || memcmp(last_parameter_set->b_rptr, new_parameter_set->b_rptr, new_size) != 0) {
freemsg(last_parameter_set);
last_parameter_set = copyb(new_parameter_set);
return true;
} else {
return false;
}
} else {
last_parameter_set = copyb(new_parameter_set);
return true;
}
}
} // namespace mediastreamer2
......@@ -98,4 +98,32 @@ MSVideoSize ms_h264_sps_get_video_size(const mblk_t* sps);
}
#endif
#ifdef __cplusplus
namespace mediastreamer2 {
class H264FrameAnalyser {
public:
struct Info {
bool hasSps = false;
bool hasPps = false;
bool hasIdr = false;
bool newSps = false;
bool newPps = false;
unsigned int toUInt() const;
};
~H264FrameAnalyser();
Info analyse(const MSQueue *frame);
private:
bool updateParameterSet(const mblk_t *new_parameter_set);
mblk_t *_lastSps = nullptr;
mblk_t *_lastPps = nullptr;
};
};
#endif
#endif /* defined(H264_UTILS_H) */
......@@ -382,8 +382,6 @@ Rfc3984Unpacker::Rfc3984Unpacker(): Unpacker() {
Rfc3984Unpacker::~Rfc3984Unpacker() {
if (_sps != nullptr) freemsg(_sps);
if (_pps != nullptr) freemsg(_pps);
if (_lastPps != nullptr) freemsg(_lastPps);
if (_lastSps != nullptr) freemsg(_lastSps);
}
void Rfc3984Unpacker::setOutOfBandSpsPps(mblk_t *sps, mblk_t *pps) {
......@@ -402,76 +400,17 @@ uint8_t Rfc3984Unpacker::getNaluType(const mblk_t *nalu) const {
Rfc3984Unpacker::Status Rfc3984Unpacker::outputFrame(MSQueue *out, const Status &flags) {
Status res = _status;
if (res.test(Unpacker::StatusFlag::IsKeyFrame) && _sps && _pps) {
/*prepend out of band provided sps and pps*/
ms_queue_put(out, _sps);
_sps = NULL;
ms_queue_put(out, _pps);
_sps = NULL;
_pps = NULL;
}
/* Log some bizarre things */
if (res.test(Unpacker::StatusFlag::FrameCorrupted)) {
if (res.test(StatusFlag::HasSPS) && res.test(StatusFlag::HasPPS) && !res.test(StatusFlag::HasIDR) && !res.test(Unpacker::StatusFlag::IsKeyFrame)) {
/*some decoders may not be happy with this*/
ms_warning("rfc3984_unpack: a frame with SPS+PPS but no IDR was output, starting at seq number %u",
mblk_get_cseq(ms_queue_peek_first(&_q)));
}
}
Unpacker::outputFrame(out, flags);
return res;
}
void Rfc3984Unpacker::storeNal(mblk_t *nal) {
uint8_t type = ms_h264_nalu_get_type(nal);
if (_status.test(StatusFlag::HasSPS) && _status.test(StatusFlag::HasPPS) && type != MSH264NaluTypeIDR && mblk_get_marker_info(nal) && isUniqueISlice(nal->b_rptr + 1)) {
ms_warning("Receiving a nal unit which is not IDR but a single I-slice bundled with SPS & PPS - considering it as a key frame.");
_status.set(Unpacker::StatusFlag::IsKeyFrame);
}
if (type == MSH264NaluTypeIDR) {
_status.set(StatusFlag::HasIDR);
_status.set(Unpacker::StatusFlag::IsKeyFrame);
} else if (type == MSH264NaluTypeSPS) {
_status.set(StatusFlag::HasSPS);
if (updateParameterSet(&_lastSps, nal)) {
_status.set(StatusFlag::NewSPS);
}
} else if (type == MSH264NaluTypePPS) {
_status.set(StatusFlag::HasPPS);
if (updateParameterSet(&_lastPps, nal)) {
_status.set(StatusFlag::NewPPS);
}
}
Unpacker::storeNal(nal);
}
bool_t Rfc3984Unpacker::updateParameterSet(mblk_t **last_parameter_set, mblk_t *new_parameter_set) {
if (*last_parameter_set != NULL) {
size_t last_size = (*last_parameter_set)->b_wptr - (*last_parameter_set)->b_rptr;
size_t new_size = new_parameter_set->b_wptr - new_parameter_set->b_rptr;
if (last_size != new_size || memcmp((*last_parameter_set)->b_rptr, new_parameter_set->b_rptr, new_size) != 0) {
freemsg(*last_parameter_set);
*last_parameter_set = dupmsg(new_parameter_set);
return TRUE;
} else {
return FALSE;
}
} else {
*last_parameter_set = dupmsg(new_parameter_set);
return TRUE;
}
}
int Rfc3984Unpacker::isUniqueISlice(const uint8_t *slice_header) {
ms_message("is_unique_I_slice: %i", (int)*slice_header);
return slice_header[0] == 0x88; /*this corresponds to first_mb_in_slice to zero and slice_type = 7*/
}
}; // end of mediastreamer2 namespace
......@@ -483,6 +422,7 @@ int Rfc3984Unpacker::isUniqueISlice(const uint8_t *slice_header) {
struct _Rfc3984Context {
mediastreamer2::Rfc3984Packer packer;
mediastreamer2::Rfc3984Unpacker unpacker;
mediastreamer2::H264FrameAnalyser analyser;
_Rfc3984Context() = default;
_Rfc3984Context(MSFactory *factory): packer(factory), unpacker() {}
......@@ -523,7 +463,15 @@ extern "C" {
}
unsigned int rfc3984_unpack2(Rfc3984Context *ctx, mblk_t *im, MSQueue *naluq) {
return ctx->unpacker.unpack(im, naluq).to_ulong();
MSQueue q;
ms_queue_init(&q);
unsigned int status = ctx->unpacker.unpack(im, &q).to_ulong();
if (status & Rfc3984FrameAvailable) {
status |= ctx->analyser.analyse(&q).toUInt();
while(mblk_t *m = ms_queue_get(&q)) {
ms_queue_put(naluq, m);
}
}
return status;
}
}
......@@ -153,15 +153,6 @@ private:
class Rfc3984Unpacker: public Unpacker {
public:
class StatusFlag {
public:
static const size_t NewSPS = 3;
static const size_t NewPPS = 4;
static const size_t HasSPS = 5;
static const size_t HasPPS = 6;
static const size_t HasIDR = 7;
};
Rfc3984Unpacker();
~Rfc3984Unpacker();
......@@ -170,15 +161,9 @@ public:
private:
uint8_t getNaluType(const mblk_t *nalu) const override;
Status outputFrame(MSQueue *out, const Status &flags) override;
void storeNal(mblk_t *nal) override;
bool_t updateParameterSet(mblk_t **last_parameter_set, mblk_t *new_parameter_set);
static int isUniqueISlice(const uint8_t *slice_header);
mblk_t *_sps = nullptr;
mblk_t *_pps = nullptr;
mblk_t *_lastSps = nullptr;
mblk_t *_lastPps = nullptr;
};
}; // end of mediastreamer2 namespace
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