Commit 4c4d1041 authored by Simon Morlat's avatar Simon Morlat

improve RFC3984 unpacker so that it provides more useful return information:

- whether a frame is outputed
- whether this frame is corrupted
- whether this frame is a key-frame
It fixes a corner case where outputing a complete keyframe consecutively to a lost packet was reported with error, which was
preventing "freeze-on-error" configured decoders to operate nicely.
parent bd83c83b
......@@ -32,10 +32,18 @@ It is part of the public API to allow external H264 plugins use this api.
extern "C"{
#endif
typedef enum{
Rfc3984FrameAvailable = 1,
Rfc3984FrameCorrupted = 1<<1,
Rfc3984IsKeyFrame = 1<<2
}Rfc3984Status;
typedef struct Rfc3984Context{
MSQueue q;
mblk_t *m;
int maxsz;
unsigned int status; /*bitmask of Rfc3984Status values*/
mblk_t *sps, *pps;
uint32_t last_ts;
uint16_t ref_cseq;
uint8_t mode;
......@@ -56,12 +64,25 @@ MS2_PUBLIC void rfc3984_enable_stap_a(Rfc3984Context *ctx, bool_t yesno);
/*process NALUs and pack them into rtp payloads */
MS2_PUBLIC void rfc3984_pack(Rfc3984Context *ctx, MSQueue *naluq, MSQueue *rtpq, uint32_t ts);
MS2_PUBLIC void rfc3984_unpack_out_of_band_sps_pps(Rfc3984Context *ctx, mblk_t *sps, mblk_t *pps);
/**
* Process incoming rtp data and output NALUs, whenever possible.
* @return 0 if everything was ok, -1 on error (inconsistencies in sequence numbers for example).
* @note the naluq output argument may be filled with incomplete data even if return value was -1.
* @deprecated use rfc3984_unpack2
**/
MS2_DEPRECATED MS2_PUBLIC int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *naluq);
/**
* Process incoming rtp data and output NALUs, whenever possible.
* @param ctx the Rfc3984Context object
* @param im a new H264 packet to process
* @param naluq a MSQueue into which a frame ready to be decoded will be output, in the form of a sequence of NAL units.
* @return a bitmask of Rfc3984Status values.
* The return value is a bitmask of the #Rfc3984Status enum.
**/
MS2_PUBLIC int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *naluq);
MS2_PUBLIC unsigned int rfc3984_unpack2(Rfc3984Context *ctx, mblk_t *im, MSQueue *naluq);
void rfc3984_uninit(Rfc3984Context *ctx);
......
......@@ -269,6 +269,7 @@ static void dec_process(MSFilter *f){
mblk_t *im, *om;
MSQueue nalus;
bool_t requestPLI = FALSE;
unsigned int ret = 0;
ms_queue_init(&nalus);
while((im=ms_queue_get(f->inputs[0]))!=NULL){
......@@ -283,15 +284,13 @@ static void dec_process(MSFilter *f){
}
/*push the sps/pps given in sprop-parameter-sets if any*/
if (d->packet_num==0 && d->sps && d->pps){
mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im));
mblk_set_timestamp_info(d->pps,mblk_get_timestamp_info(im));
rfc3984_unpack(&d->unpacker,d->sps,&nalus);
rfc3984_unpack(&d->unpacker,d->pps,&nalus);
rfc3984_unpack_out_of_band_sps_pps(&d->unpacker, d->sps, d->pps);
d->sps=NULL;
d->pps=NULL;
}
requestPLI |= (rfc3984_unpack(&d->unpacker,im,&nalus) != -0);
if (!ms_queue_empty(&nalus)){
ret = rfc3984_unpack2(&d->unpacker,im,&nalus);
if (ret & Rfc3984FrameAvailable){
int size;
uint8_t *p,*end;
bool_t need_reinit=FALSE;
......@@ -327,6 +326,7 @@ static void dec_process(MSFilter *f){
}
p+=len;
}
if (ret & Rfc3984FrameCorrupted) requestPLI = TRUE;
}
d->packet_num++;
}
......
......@@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/rfc3984.h"
#include "mediastreamer2/msfilter.h"
#include "h264utils.h"
#define TYPE_FU_A 28 /*fragmented unit 0x1C*/
#define TYPE_STAP_A 24 /*single time aggregation packet 0x18*/
......@@ -53,6 +55,7 @@ void rfc3984_init(Rfc3984Context *ctx){
ctx->m=NULL;
ctx->maxsz=MS_DEFAULT_MAX_PAYLOAD_SIZE;
ctx->mode=0;
ctx->status = 0;
ctx->last_ts=0x943FEA43;/*some random value*/
ctx->stap_a_allowed=TRUE;
}
......@@ -243,27 +246,76 @@ static mblk_t * aggregate_fua(Rfc3984Context *ctx, mblk_t *im){
return om;
}
/*process incoming rtp data and output NALUs, whenever possible*/
void rfc3984_unpack_out_of_band_sps_pps(Rfc3984Context *ctx, mblk_t *sps, mblk_t *pps){
if (ctx->sps){
freemsg(ctx->sps);
}
if (ctx->pps){
freemsg(ctx->pps);
}
ctx->sps = sps;
ctx->pps = pps;
}
int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
if (rfc3984_unpack2(ctx, im, out) & Rfc3984FrameCorrupted) return -1;
return 0;
}
static unsigned int output_frame(Rfc3984Context * ctx, MSQueue *out, unsigned int flags){
unsigned int res = ctx->status;
if (!ms_queue_empty(out)){
ms_warning("rfc3984_unpack: output_frame invoked several times in a row, this should not happen");
}
res |= flags;
if ((res & Rfc3984IsKeyFrame) && ctx->sps && ctx->pps){
/*prepend out of band provided sps and pps*/
ms_queue_put(out, ctx->sps);
ctx->sps = NULL;
ms_queue_put(out, ctx->pps);
ctx->pps = NULL;
}
while(!ms_queue_empty(&ctx->q)){
ms_queue_put(out,ms_queue_get(&ctx->q));
}
ctx->status = 0;
return res;
}
static void store_nal(Rfc3984Context *ctx, mblk_t *nal){
uint8_t type=nal_header_get_type(nal->b_rptr);
if (ms_queue_empty(&ctx->q) && (ctx->status & Rfc3984FrameCorrupted)
&& (type == MSH264NaluTypeSPS || type == MSH264NaluTypePPS || type == MSH264NaluTypeIDR)){
ms_message("Previous discontinuity ignored since we are restarting with a keyframe.");
ctx->status &= ~Rfc3984FrameCorrupted;
ctx->status |= Rfc3984IsKeyFrame;
}
ms_queue_put(&ctx->q,nal);
if (type == MSH264NaluTypeIDR){
ctx->status |= Rfc3984IsKeyFrame;
}
}
unsigned int rfc3984_unpack2(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
uint8_t type=nal_header_get_type(im->b_rptr);
uint8_t *p;
int marker = mblk_get_marker_info(im);
uint32_t ts=mblk_get_timestamp_info(im);
uint16_t cseq=mblk_get_cseq(im);
int res = 0;
unsigned int ret = 0;
if (ctx->last_ts!=ts){
/*a new frame is arriving, in case the marker bit was not set in previous frame, output it now*/
/* unless this is a FU-A (workarond some other apps bugs)*/
/*a new frame is arriving, in case the marker bit was not set in previous frame, output it now,
unless it is a FU-A packet (workaround for buggy implementations)*/
ctx->last_ts=ts;
if (ctx->m==NULL){
bool_t out_without_marker = FALSE;
while(!ms_queue_empty(&ctx->q)){
ms_queue_put(out,ms_queue_get(&ctx->q));
out_without_marker = TRUE;
res = -1;
}
if (out_without_marker) ms_warning("Incomplete H264 frame (missing marker bit)");
if (ctx->m==NULL && !ms_queue_empty(&ctx->q)){
ret = output_frame(ctx, out, Rfc3984FrameAvailable | Rfc3984FrameCorrupted);
ms_warning("Incomplete H264 frame (missing marker bit)");
}
}
......@@ -277,7 +329,7 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
if (ctx->ref_cseq != cseq) {
ms_message("sequence inconsistency detected (diff=%i)",(int)(cseq - ctx->ref_cseq));
ctx->ref_cseq = cseq;
res = -1;
ctx->status |= Rfc3984FrameCorrupted;
}
}
......@@ -302,13 +354,13 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
freemsg(nal);
break;
}
ms_queue_put(&ctx->q,nal);
store_nal(ctx, nal);
}
freemsg(im);
}else if (type==TYPE_FU_A){
mblk_t *o=aggregate_fua(ctx,im);
ms_debug("Receiving FU-A");
if (o) ms_queue_put(&ctx->q,o);
if (o) store_nal(ctx, o);
}else{
if (ctx->m){
/*discontinued FU-A, purge it*/
......@@ -317,19 +369,16 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
}
/*single nal unit*/
ms_debug("Receiving single NAL");
ms_queue_put(&ctx->q,im);
store_nal(ctx, im);
}
if (marker){
ctx->last_ts=ts;
ms_debug("Marker bit set");
/*end of frame, output everything*/
while(!ms_queue_empty(&ctx->q)){
ms_queue_put(out,ms_queue_get(&ctx->q));
}
ret = output_frame(ctx, out, Rfc3984FrameAvailable);
}
return res;
return ret;
}
......
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