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. ...@@ -32,10 +32,18 @@ It is part of the public API to allow external H264 plugins use this api.
extern "C"{ extern "C"{
#endif #endif
typedef enum{
Rfc3984FrameAvailable = 1,
Rfc3984FrameCorrupted = 1<<1,
Rfc3984IsKeyFrame = 1<<2
}Rfc3984Status;
typedef struct Rfc3984Context{ typedef struct Rfc3984Context{
MSQueue q; MSQueue q;
mblk_t *m; mblk_t *m;
int maxsz; int maxsz;
unsigned int status; /*bitmask of Rfc3984Status values*/
mblk_t *sps, *pps;
uint32_t last_ts; uint32_t last_ts;
uint16_t ref_cseq; uint16_t ref_cseq;
uint8_t mode; uint8_t mode;
...@@ -56,12 +64,25 @@ MS2_PUBLIC void rfc3984_enable_stap_a(Rfc3984Context *ctx, bool_t yesno); ...@@ -56,12 +64,25 @@ MS2_PUBLIC void rfc3984_enable_stap_a(Rfc3984Context *ctx, bool_t yesno);
/*process NALUs and pack them into rtp payloads */ /*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_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. * Process incoming rtp data and output NALUs, whenever possible.
* @return 0 if everything was ok, -1 on error (inconsistencies in sequence numbers for example). * @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. * @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); void rfc3984_uninit(Rfc3984Context *ctx);
......
...@@ -269,6 +269,7 @@ static void dec_process(MSFilter *f){ ...@@ -269,6 +269,7 @@ static void dec_process(MSFilter *f){
mblk_t *im, *om; mblk_t *im, *om;
MSQueue nalus; MSQueue nalus;
bool_t requestPLI = FALSE; bool_t requestPLI = FALSE;
unsigned int ret = 0;
ms_queue_init(&nalus); ms_queue_init(&nalus);
while((im=ms_queue_get(f->inputs[0]))!=NULL){ while((im=ms_queue_get(f->inputs[0]))!=NULL){
...@@ -283,15 +284,13 @@ static void dec_process(MSFilter *f){ ...@@ -283,15 +284,13 @@ static void dec_process(MSFilter *f){
} }
/*push the sps/pps given in sprop-parameter-sets if any*/ /*push the sps/pps given in sprop-parameter-sets if any*/
if (d->packet_num==0 && d->sps && d->pps){ if (d->packet_num==0 && d->sps && d->pps){
mblk_set_timestamp_info(d->sps,mblk_get_timestamp_info(im)); rfc3984_unpack_out_of_band_sps_pps(&d->unpacker, d->sps, d->pps);
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);
d->sps=NULL; d->sps=NULL;
d->pps=NULL; d->pps=NULL;
} }
requestPLI |= (rfc3984_unpack(&d->unpacker,im,&nalus) != -0); ret = rfc3984_unpack2(&d->unpacker,im,&nalus);
if (!ms_queue_empty(&nalus)){
if (ret & Rfc3984FrameAvailable){
int size; int size;
uint8_t *p,*end; uint8_t *p,*end;
bool_t need_reinit=FALSE; bool_t need_reinit=FALSE;
...@@ -327,6 +326,7 @@ static void dec_process(MSFilter *f){ ...@@ -327,6 +326,7 @@ static void dec_process(MSFilter *f){
} }
p+=len; p+=len;
} }
if (ret & Rfc3984FrameCorrupted) requestPLI = TRUE;
} }
d->packet_num++; d->packet_num++;
} }
......
...@@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ...@@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/rfc3984.h" #include "mediastreamer2/rfc3984.h"
#include "mediastreamer2/msfilter.h" #include "mediastreamer2/msfilter.h"
#include "h264utils.h"
#define TYPE_FU_A 28 /*fragmented unit 0x1C*/ #define TYPE_FU_A 28 /*fragmented unit 0x1C*/
#define TYPE_STAP_A 24 /*single time aggregation packet 0x18*/ #define TYPE_STAP_A 24 /*single time aggregation packet 0x18*/
...@@ -53,6 +55,7 @@ void rfc3984_init(Rfc3984Context *ctx){ ...@@ -53,6 +55,7 @@ void rfc3984_init(Rfc3984Context *ctx){
ctx->m=NULL; ctx->m=NULL;
ctx->maxsz=MS_DEFAULT_MAX_PAYLOAD_SIZE; ctx->maxsz=MS_DEFAULT_MAX_PAYLOAD_SIZE;
ctx->mode=0; ctx->mode=0;
ctx->status = 0;
ctx->last_ts=0x943FEA43;/*some random value*/ ctx->last_ts=0x943FEA43;/*some random value*/
ctx->stap_a_allowed=TRUE; ctx->stap_a_allowed=TRUE;
} }
...@@ -243,27 +246,76 @@ static mblk_t * aggregate_fua(Rfc3984Context *ctx, mblk_t *im){ ...@@ -243,27 +246,76 @@ static mblk_t * aggregate_fua(Rfc3984Context *ctx, mblk_t *im){
return om; 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){ 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 type=nal_header_get_type(im->b_rptr);
uint8_t *p; uint8_t *p;
int marker = mblk_get_marker_info(im); int marker = mblk_get_marker_info(im);
uint32_t ts=mblk_get_timestamp_info(im); uint32_t ts=mblk_get_timestamp_info(im);
uint16_t cseq=mblk_get_cseq(im); uint16_t cseq=mblk_get_cseq(im);
int res = 0; unsigned int ret = 0;
if (ctx->last_ts!=ts){ if (ctx->last_ts!=ts){
/*a new frame is arriving, in case the marker bit was not set in previous frame, output it now*/ /*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)*/ unless it is a FU-A packet (workaround for buggy implementations)*/
ctx->last_ts=ts; ctx->last_ts=ts;
if (ctx->m==NULL){ if (ctx->m==NULL && !ms_queue_empty(&ctx->q)){
bool_t out_without_marker = FALSE; ret = output_frame(ctx, out, Rfc3984FrameAvailable | Rfc3984FrameCorrupted);
while(!ms_queue_empty(&ctx->q)){ ms_warning("Incomplete H264 frame (missing marker bit)");
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)");
} }
} }
...@@ -277,7 +329,7 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){ ...@@ -277,7 +329,7 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
if (ctx->ref_cseq != cseq) { if (ctx->ref_cseq != cseq) {
ms_message("sequence inconsistency detected (diff=%i)",(int)(cseq - ctx->ref_cseq)); ms_message("sequence inconsistency detected (diff=%i)",(int)(cseq - ctx->ref_cseq));
ctx->ref_cseq = cseq; ctx->ref_cseq = cseq;
res = -1; ctx->status |= Rfc3984FrameCorrupted;
} }
} }
...@@ -302,13 +354,13 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){ ...@@ -302,13 +354,13 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
freemsg(nal); freemsg(nal);
break; break;
} }
ms_queue_put(&ctx->q,nal); store_nal(ctx, nal);
} }
freemsg(im); freemsg(im);
}else if (type==TYPE_FU_A){ }else if (type==TYPE_FU_A){
mblk_t *o=aggregate_fua(ctx,im); mblk_t *o=aggregate_fua(ctx,im);
ms_debug("Receiving FU-A"); ms_debug("Receiving FU-A");
if (o) ms_queue_put(&ctx->q,o); if (o) store_nal(ctx, o);
}else{ }else{
if (ctx->m){ if (ctx->m){
/*discontinued FU-A, purge it*/ /*discontinued FU-A, purge it*/
...@@ -317,19 +369,16 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){ ...@@ -317,19 +369,16 @@ int rfc3984_unpack(Rfc3984Context *ctx, mblk_t *im, MSQueue *out){
} }
/*single nal unit*/ /*single nal unit*/
ms_debug("Receiving single NAL"); ms_debug("Receiving single NAL");
ms_queue_put(&ctx->q,im); store_nal(ctx, im);
} }
if (marker){ if (marker){
ctx->last_ts=ts; ctx->last_ts=ts;
ms_debug("Marker bit set"); ms_debug("Marker bit set");
/*end of frame, output everything*/ ret = output_frame(ctx, out, Rfc3984FrameAvailable);
while(!ms_queue_empty(&ctx->q)){
ms_queue_put(out,ms_queue_get(&ctx->q));
}
} }
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