From 5bfef2a8b4674382f959b21a3b8e14cf1d942bab Mon Sep 17 00:00:00 2001 From: Jaco Kroon Date: Mon, 2 May 2016 12:57:03 +0200 Subject: translate: Enables native Packet-Loss Concealment (PLC) for supporting codecs. ASTERISK-25629 #close Change-Id: Ibfcf0670e094e9718d82fd9920f1fb2dae122006 --- main/translate.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 13 deletions(-) (limited to 'main') diff --git a/main/translate.c b/main/translate.c index 8d37e3724..b7443b81b 100644 --- a/main/translate.c +++ b/main/translate.c @@ -358,6 +358,7 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t, struct ast_format pvt->f.offset = AST_FRIENDLY_OFFSET; pvt->f.src = pvt->t->name; pvt->f.data.ptr = pvt->outbuf.c; + pvt->f.seqno = 0x10000; /* * If the translator has not provided a format @@ -524,13 +525,46 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a /*! \brief do the actual translation */ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume) { - struct ast_trans_pvt *p = path; - struct ast_frame *out; + const unsigned int rtp_seqno_max_value = 0xffff; + struct ast_frame *out_last, *out = NULL; + struct ast_trans_pvt *step; struct timeval delivery; int has_timing_info; long ts; long len; - int seqno; + int seqno, frames_missing; + + /* Determine the amount of lost packets for PLC */ + /* But not at start with first frame = path->f.seqno is still 0x10000 */ + /* But not when there is no sequence number = frame created internally */ + if ((path->f.seqno <= rtp_seqno_max_value) && (path->f.seqno != f->seqno)) { + if (f->seqno < path->f.seqno) { /* seqno overrun situation */ + frames_missing = rtp_seqno_max_value + f->seqno - path->f.seqno - 1; + } else { + frames_missing = f->seqno - path->f.seqno - 1; + } + /* Out-of-order packet - more precise: late packet */ + if ((rtp_seqno_max_value + 1) / 2 < frames_missing) { + if (consume) { + ast_frfree(f); + } + /* + * Do not pass late packets to any transcoding module, because that + * confuses the state of any library (packets inter-depend). With + * the next packet, this one is going to be treated as lost packet. + */ + return NULL; + } + + if (frames_missing > 96) { + struct ast_str *str = ast_str_alloca(256); + + /* not DEBUG but NOTICE because of WARNING in main/cannel.c:__ast_queue_frame */ + ast_log(LOG_NOTICE, "%d lost frame(s) %d/%d %s\n", frames_missing, f->seqno, path->f.seqno, ast_translate_path_to_str(path, &str)); + } + } else { + frames_missing = 0; + } has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO); ts = f->ts; @@ -560,18 +594,93 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, f->samples, ast_format_get_sample_rate(f->subclass.format))); } delivery = f->delivery; - for (out = f; out && p ; p = p->next) { - struct ast_frame *current = out; - - do { - framein(p, current); - current = AST_LIST_NEXT(current, frame_list); - } while (current); - if (out != f) { - ast_frfree(out); + + for (out_last = NULL; frames_missing + 1; frames_missing--) { + struct ast_frame *frame_to_translate, *inner_head; + struct ast_frame missed = { + .frametype = AST_FRAME_VOICE, + .subclass.format = f->subclass.format, + .datalen = 0, + /* In RTP, the amount of samples might change anytime */ + /* If that happened while frames got lost, what to do? */ + .samples = f->samples, /* FIXME */ + .src = __FUNCTION__, + .data.uint32 = 0, + .delivery.tv_sec = 0, + .delivery.tv_usec = 0, + .flags = 0, + /* RTP sequence number is between 0x0001 and 0xffff */ + .seqno = (rtp_seqno_max_value + 1 + f->seqno - frames_missing) & rtp_seqno_max_value, + }; + + if (frames_missing) { + frame_to_translate = &missed; + } else { + frame_to_translate = f; + } + + /* The translation path from one format to another might contain several steps */ + /* out* collects the result for missed frame(s) and input frame(s) */ + /* out is the result of the conversion of all frames, translated into the destination format */ + /* out_last is the last frame in that list, to add frames faster */ + for (step = path, inner_head = frame_to_translate; inner_head && step; step = step->next) { + struct ast_frame *current, *inner_last, *inner_prev = frame_to_translate; + + /* inner* collects the result of each conversion step, the input for the next step */ + /* inner_head is a list of frames created by each conversion step */ + /* inner_last is the last frame in that list, to add frames faster */ + for (inner_last = NULL, current = inner_head; current; current = AST_LIST_NEXT(current, frame_list)) { + struct ast_frame *tmp; + + framein(step, current); + tmp = step->t->frameout(step); + + if (!tmp) { + continue; + } else if (inner_last) { + struct ast_frame *t; + + /* Determine the last frame of the list before appending to it */ + while ((t = AST_LIST_NEXT(inner_last, frame_list))) { + inner_last = t; + } + AST_LIST_NEXT(inner_last, frame_list) = tmp; + } else { + inner_prev = inner_head; + inner_head = tmp; + inner_last = tmp; + } + } + + /* The current step did not create any frames = no frames for the next step */ + /* The steps are not lost because framein buffered those for the next input frame */ + if (!inner_last) { + inner_prev = inner_head; + inner_head = NULL; + } + if (inner_prev != frame_to_translate) { + ast_frfree(inner_prev); /* Frees just the intermediate lists */ + } + } + + /* This frame created no frames after translation = continue with next frame */ + /* The frame is not lost because framein buffered it to be combined with the next frame */ + if (!inner_head) { + continue; + } else if (out_last) { + struct ast_frame *t; + + /* Determine the last frame of the list before appending to it */ + while ((t = AST_LIST_NEXT(out_last, frame_list))) { + out_last = t; + } + AST_LIST_NEXT(out_last, frame_list) = inner_head; + } else { + out = inner_head; + out_last = inner_head; } - out = p->t->frameout(p); } + if (out) { /* we have a frame, play with times */ if (!ast_tvzero(delivery)) { -- cgit v1.2.3