393 lines
10 KiB
C
393 lines
10 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005-2020, Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Seven Du <dujinfang@gmail.com>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Seven Du <dujinfang@gmail.com>
|
|
*
|
|
* switch_packetizer H264 packetizer
|
|
*
|
|
*/
|
|
|
|
#include <switch.h>
|
|
#define MAX_NALUS 256
|
|
|
|
typedef struct our_h264_nalu_s {
|
|
const uint8_t *start;
|
|
const uint8_t *eat;
|
|
uint32_t len;
|
|
} our_h264_nalu_t;
|
|
|
|
typedef struct h264_packetizer_s {
|
|
switch_packetizer_bitstream_t type;
|
|
uint32_t slice_size;
|
|
int nalu_current_index;
|
|
our_h264_nalu_t nalus[MAX_NALUS];
|
|
uint8_t *extradata;
|
|
uint32_t extradata_size;
|
|
uint8_t *sps;
|
|
uint8_t *pps;
|
|
uint32_t sps_len;
|
|
uint32_t pps_len;
|
|
int sps_sent;
|
|
int pps_sent;
|
|
} h264_packetizer_t;
|
|
|
|
/* ff_avc_find_startcode is not exposed in the ffmpeg lib but you can use it
|
|
Either include the avc.h which available in the ffmpeg source, or
|
|
just add the declaration like we does following to avoid include that whole avc.h
|
|
The function is implemented in avc.h, guess we'll get rid of this later if we can directly use libx264
|
|
|
|
#include <libavformat/avc.h>
|
|
*/
|
|
|
|
const uint8_t *ff_avc_find_startcode(const uint8_t *p, const uint8_t *end);
|
|
|
|
static const uint8_t *fs_avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
|
|
{
|
|
const uint8_t *a = p + 4 - ((intptr_t)p & 3);
|
|
|
|
for (end -= 3; p < a && p < end; p++) {
|
|
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
|
return p;
|
|
}
|
|
|
|
for (end -= 3; p < end; p += 4) {
|
|
uint32_t x = *(const uint32_t*)p;
|
|
if ((x - 0x01010101) & (~x) & 0x80808080) {
|
|
if (p[1] == 0) {
|
|
if (p[0] == 0 && p[2] == 1)
|
|
return p;
|
|
if (p[2] == 0 && p[3] == 1)
|
|
return p+1;
|
|
}
|
|
if (p[3] == 0) {
|
|
if (p[2] == 0 && p[4] == 1)
|
|
return p+2;
|
|
if (p[4] == 0 && p[5] == 1)
|
|
return p+3;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (end += 3; p < end; p++) {
|
|
if (p[0] == 0 && p[1] == 0 && p[2] == 1)
|
|
return p;
|
|
}
|
|
|
|
return end + 3;
|
|
}
|
|
|
|
static const uint8_t *fs_avc_find_startcode(const uint8_t *p, const uint8_t *end)
|
|
{
|
|
const uint8_t *out= fs_avc_find_startcode_internal(p, end);
|
|
|
|
if (p < out && out < end && !out[-1]) {
|
|
out--;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_packetizer_t *) switch_packetizer_create(switch_packetizer_bitstream_t type, uint32_t slice_size)
|
|
{
|
|
h264_packetizer_t *context = malloc(sizeof(h264_packetizer_t));
|
|
memset(context, 0, sizeof(h264_packetizer_t));
|
|
context->slice_size = slice_size;
|
|
context->type = type;
|
|
return (switch_packetizer_t *)context;
|
|
}
|
|
|
|
// for H264
|
|
SWITCH_DECLARE(switch_status_t) switch_packetizer_feed_extradata(switch_packetizer_t *packetizer, void *data, uint32_t size)
|
|
{
|
|
h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
|
|
uint8_t *p;
|
|
int left = size;
|
|
int n_sps = 0;
|
|
int n_pps = 0;
|
|
int sps_len;
|
|
int pps_len;
|
|
int i;
|
|
|
|
if (left < 10) return SWITCH_STATUS_FALSE;
|
|
|
|
if (context->extradata) {
|
|
context->sps = NULL;
|
|
context->pps = NULL;
|
|
context->sps_len = 0;
|
|
context->pps_len = 0;
|
|
free(context->extradata);
|
|
context->extradata = NULL;
|
|
}
|
|
|
|
context->extradata = malloc(size);
|
|
if (!context->extradata) return SWITCH_STATUS_MEMERR;
|
|
memcpy(context->extradata, data, size);
|
|
|
|
/*
|
|
0x0000 | 01 64 00 1E FF E1 00 1F 67 64 00 1E AC C8 60 33 // E1: 1SPS 00 1F: SPS 31byte
|
|
0x0010 | 0E F9 E6 FF C1 C6 01 C4 44 00 00 03 00 04 00 00
|
|
0x0020 | 03 00 B8 3C 58 B6 68 01 00 05 68 E9 78 47 2C // 01: 1PPS 00 05: PPS 5byte
|
|
*/
|
|
|
|
p = context->extradata;
|
|
|
|
if (*p != 1) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT supported version: %d\n", *p);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
p += 5;
|
|
left -= 5;
|
|
|
|
//sps
|
|
n_sps = *p & 0x1f;
|
|
p += 1;
|
|
left -= 1;
|
|
|
|
for (i = 0; i < n_sps; i++) {
|
|
sps_len = ntohs(*(uint16_t *)p);
|
|
p += sizeof(uint16_t);
|
|
left -= sizeof(uint16_t);
|
|
|
|
if (left < sps_len) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data %d < %u\n", left, sps_len);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if (!context->sps) { // we only need the first one
|
|
context->sps = p;
|
|
context->sps_len = sps_len;
|
|
}
|
|
|
|
p += sps_len;
|
|
left -= sps_len;
|
|
}
|
|
|
|
if (left < 0) return SWITCH_STATUS_FALSE;
|
|
|
|
n_pps = *p & 0x1f;
|
|
p += 1;
|
|
left -= 1;
|
|
|
|
for (i = 0; i < n_pps; i++) {
|
|
pps_len = ntohs(*(uint16_t *)p);
|
|
p += sizeof(uint16_t);
|
|
left -= sizeof(uint16_t);
|
|
|
|
if (left < pps_len) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data %d < %u\n", left, pps_len);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if (!context->pps) { // we only need the first one
|
|
context->pps = p;
|
|
context->pps_len = pps_len;
|
|
}
|
|
p += pps_len;
|
|
left -= pps_len;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_packetizer_feed(switch_packetizer_t *packetizer, void *data, uint32_t size)
|
|
{
|
|
h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
|
|
const uint8_t *p = data;
|
|
const uint8_t *end = p + size;
|
|
int i = 0;
|
|
|
|
// reset everytime
|
|
memset(context->nalus, 0, MAX_NALUS * sizeof(our_h264_nalu_t));
|
|
context->nalu_current_index = 0;
|
|
|
|
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size = %u %x %x %x %x %x\n", size, *p, *(p+1), *(p+2), *(p+3), *(p+4));
|
|
|
|
if (context->type == SPT_H264_SIZED_BITSTREAM) {
|
|
int left = size;
|
|
uint32_t len;
|
|
|
|
while (left > 0) {
|
|
if (left < sizeof(uint32_t)) return SWITCH_STATUS_MORE_DATA;
|
|
len = htonl(*(uint32_t *)p);
|
|
left -= sizeof(uint32_t);
|
|
left -= len;
|
|
if (left < 0) return SWITCH_STATUS_MORE_DATA;
|
|
p += sizeof(uint32_t);
|
|
|
|
context->nalus[i].start = p;
|
|
context->nalus[i].eat = p;
|
|
context->nalus[i].len = len;
|
|
|
|
p += len;
|
|
|
|
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "#%d %x len=%u\n", i, *context->nalus[i].start, context->nalus[i].len);
|
|
i++;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (context->type == SPT_H264_SIGNALE_NALU) {
|
|
context->nalus[0].start = data;
|
|
context->nalus[0].eat = data;
|
|
context->nalus[0].len = size;
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
// SPT_H264_BITSTREAM
|
|
while ((p = fs_avc_find_startcode(p, end)) < end) {
|
|
if (!context->nalus[i].start) {
|
|
while (!(*p++)) ; /* eat the sync bytes, what ever 0 0 1 or 0 0 0 1 */
|
|
context->nalus[i].start = p;
|
|
context->nalus[i].eat = p;
|
|
} else {
|
|
context->nalus[i].len = p - context->nalus[i].start;
|
|
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "#%d %x len=%u\n", i, *context->nalus[i].start, context->nalus[i].len);
|
|
while (!(*p++)) ; /* eat the sync bytes, what ever 0 0 1 or 0 0 0 1 */
|
|
i++;
|
|
context->nalus[i].start = p;
|
|
context->nalus[i].eat = p;
|
|
}
|
|
if (i >= MAX_NALUS - 2) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "TOO MANY SLICES!\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
context->nalus[i].len = p - context->nalus[i].start;
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_packetizer_read(switch_packetizer_t *packetizer, switch_frame_t *frame)
|
|
{
|
|
h264_packetizer_t *context = (h264_packetizer_t *)packetizer;
|
|
uint32_t slice_size = context->slice_size;
|
|
our_h264_nalu_t *nalu = &context->nalus[context->nalu_current_index];
|
|
uint8_t nalu_hdr = 0;
|
|
uint8_t nalu_type = 0;
|
|
uint8_t nri = 0;
|
|
int left = nalu->len - (nalu->eat - nalu->start);
|
|
uint8_t *p = frame->data;
|
|
uint8_t start = nalu->start == nalu->eat ? 0x80 : 0;
|
|
int n = nalu->len / slice_size + 1;
|
|
int real_slice_size = nalu->len / n + 1 + 2;
|
|
|
|
if (nalu->start == NULL) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "INVALID BITSTREAM\n");
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
nalu_hdr = *(uint8_t *)(nalu->start);
|
|
nalu_type = nalu_hdr & 0x1f;
|
|
nri = nalu_hdr & 0x60;
|
|
|
|
if (real_slice_size > slice_size) real_slice_size = slice_size;
|
|
if (frame->datalen < slice_size) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "frame buffer too small %u < %u\n", frame->datalen, slice_size);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if (context->type == SPT_H264_BITSTREAM || SPT_H264_SIZED_BITSTREAM) {
|
|
if (nalu_type == 0x05) {
|
|
// insert SPS/PPS before
|
|
if (context->sps && !context->sps_sent) {
|
|
memcpy(frame->data, context->sps, context->sps_len);
|
|
frame->datalen = context->sps_len;
|
|
frame->m = 0;
|
|
context->sps_sent = 1;
|
|
return SWITCH_STATUS_MORE_DATA;
|
|
} else if (context->pps && !context->pps_sent) {
|
|
memcpy(frame->data, context->pps, context->pps_len);
|
|
frame->datalen = context->pps_len;
|
|
frame->m = 0;
|
|
context->pps_sent = 1;
|
|
return SWITCH_STATUS_MORE_DATA;
|
|
}
|
|
} else if (nalu_type == 0x07) {
|
|
context->sps_sent = 1;
|
|
} else if (nalu_type == 0x08) {
|
|
context->pps_sent = 1;
|
|
}
|
|
}
|
|
|
|
if (nalu->len <= slice_size) {
|
|
memcpy(frame->data, nalu->start, nalu->len);
|
|
frame->datalen = nalu->len;
|
|
context->nalu_current_index++;
|
|
|
|
switch_clear_flag(frame, SFF_CNG);
|
|
|
|
if (context->nalus[context->nalu_current_index].len) {
|
|
frame->m = 0;
|
|
return SWITCH_STATUS_MORE_DATA;
|
|
}
|
|
|
|
frame->m = 1;
|
|
|
|
if (nalu_type == 0x05) {
|
|
context->sps_sent = 0;
|
|
context->pps_sent = 0;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (left <= (real_slice_size - 2)) {
|
|
p[0] = nri | 28; // FU-A
|
|
p[1] = 0x40 | nalu_type;
|
|
memcpy(p+2, nalu->eat, left);
|
|
nalu->eat += left;
|
|
frame->datalen = left + 2;
|
|
context->nalu_current_index++;
|
|
|
|
if (!context->nalus[context->nalu_current_index].len) {
|
|
frame->m = 1;
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
return SWITCH_STATUS_MORE_DATA;
|
|
}
|
|
|
|
p[0] = nri | 28; // FU-A
|
|
p[1] = start | nalu_type;
|
|
if (start) nalu->eat++;
|
|
memcpy(p+2, nalu->eat, real_slice_size - 2);
|
|
nalu->eat += (real_slice_size - 2);
|
|
frame->datalen = real_slice_size;
|
|
frame->m = 0;
|
|
return SWITCH_STATUS_MORE_DATA;
|
|
}
|
|
|
|
SWITCH_DECLARE(void) switch_packetizer_close(switch_packetizer_t **packetizer)
|
|
{
|
|
h264_packetizer_t *context = (h264_packetizer_t *)(*packetizer);
|
|
if (context->extradata) free(context->extradata);
|
|
free(context);
|
|
*packetizer = NULL;
|
|
}
|