2014-11-10 21:38:56 -06:00
|
|
|
/*
|
|
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
|
|
* Copyright (C) 2005-2013, 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>
|
|
|
|
*
|
|
|
|
* mod_openh264 -- H264 Coded Using Cisco OpenH264
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
// #define DEBUG_H264
|
|
|
|
#include <switch.h>
|
|
|
|
|
|
|
|
#define EPSN (0.000001f) // (1e-6) // desired float precision
|
|
|
|
#define PESN (0.000001f) // (1e-6) // desired float precision
|
2014-12-09 15:19:05 -06:00
|
|
|
//#define MT_ENABLED
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
#include "codec_api.h"
|
|
|
|
//#include "inc/logging.h" // for debug
|
|
|
|
|
2014-12-04 18:35:20 -06:00
|
|
|
#define FPS 30.0f // frame rate
|
2014-11-10 21:38:56 -06:00
|
|
|
#define H264_NALU_BUFFER_SIZE 65536
|
2014-11-20 22:40:27 +08:00
|
|
|
#define SLICE_SIZE SWITCH_DEFAULT_VIDEO_SIZE //NALU Slice Size
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_openh264_load);
|
|
|
|
SWITCH_MODULE_DEFINITION(mod_openh264, mod_openh264_load, NULL, NULL);
|
|
|
|
|
|
|
|
typedef struct h264_codec_context_s {
|
|
|
|
ISVCEncoder *encoder;
|
|
|
|
switch_bool_t encoder_initialized;
|
|
|
|
SEncParamExt encoder_params;
|
|
|
|
SFrameBSInfo bit_stream_info;
|
|
|
|
EVideoFrameType last_frame_type;
|
|
|
|
int cur_layer;
|
|
|
|
int cur_nalu_index;
|
|
|
|
uint8_t last_nalu_type;
|
|
|
|
uint8_t last_nri;
|
|
|
|
int last_nalu_data_pos;
|
|
|
|
int nalu_eat;
|
2014-11-26 20:30:42 +08:00
|
|
|
int nalu_28_start;
|
2014-12-05 17:18:23 -06:00
|
|
|
SSourcePicture pic;
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
ISVCDecoder *decoder;
|
|
|
|
SDecodingParam decoder_params;
|
|
|
|
switch_buffer_t *nalu_buffer;
|
|
|
|
switch_image_t *img;
|
|
|
|
int got_sps;
|
|
|
|
int64_t pts;
|
2014-11-18 16:39:32 -06:00
|
|
|
int need_key_frame;
|
2014-11-10 21:38:56 -06:00
|
|
|
switch_size_t last_received_timestamp;
|
|
|
|
switch_bool_t last_received_complete_picture;
|
|
|
|
} h264_codec_context_t;
|
|
|
|
|
|
|
|
int FillSpecificParameters(SEncParamExt& param) {
|
|
|
|
/* Test for temporal, spatial, SNR scalability */
|
2014-11-18 16:39:32 -06:00
|
|
|
param.iPicWidth = 1280; // width of picture in samples
|
|
|
|
param.iPicHeight = 720; // height of picture in samples
|
2014-12-09 15:19:05 -06:00
|
|
|
param.iTargetBitrate = 1250000;//1280 * 720 * 8; // target bitrate desired
|
2014-11-10 21:38:56 -06:00
|
|
|
param.iRCMode = RC_QUALITY_MODE; // rc mode control
|
|
|
|
param.iTemporalLayerNum = 1; // layer number at temporal level
|
|
|
|
param.iSpatialLayerNum = 1; // layer number at spatial level
|
|
|
|
param.bEnableDenoise = 0; // denoise control
|
|
|
|
param.bEnableBackgroundDetection = 1; // background detection control
|
2014-12-05 17:18:23 -06:00
|
|
|
param.bEnableSceneChangeDetect= 1;
|
|
|
|
//param.bEnableFrameSkip = 1;
|
|
|
|
param.iMultipleThreadIdc= 1;
|
2014-11-10 21:38:56 -06:00
|
|
|
param.bEnableAdaptiveQuant = 1; // adaptive quantization control
|
|
|
|
param.bEnableLongTermReference = 0; // long term reference control
|
|
|
|
param.iLtrMarkPeriod = 30;
|
2014-12-05 17:18:23 -06:00
|
|
|
param.iLoopFilterAlphaC0Offset= 0;
|
|
|
|
param.iLoopFilterBetaOffset= 0;
|
2014-11-10 21:38:56 -06:00
|
|
|
param.iComplexityMode = MEDIUM_COMPLEXITY;
|
|
|
|
param.uiIntraPeriod = FPS * 3; // period of Intra frame
|
2014-12-05 17:18:23 -06:00
|
|
|
#ifdef MT_ENABLED
|
2014-12-04 18:35:20 -06:00
|
|
|
param.bEnableSpsPpsIdAddition = 1;
|
2014-12-08 14:29:18 -06:00
|
|
|
#else
|
2014-12-05 17:18:23 -06:00
|
|
|
param.bEnableSpsPpsIdAddition = 0;
|
|
|
|
#endif
|
2014-11-10 21:38:56 -06:00
|
|
|
param.bPrefixNalAddingCtrl = 0;
|
|
|
|
|
|
|
|
int iIndexLayer = 0;
|
2014-11-18 16:39:32 -06:00
|
|
|
param.sSpatialLayers[iIndexLayer].iVideoWidth = 1280;
|
|
|
|
param.sSpatialLayers[iIndexLayer].iVideoHeight = 720;
|
|
|
|
param.sSpatialLayers[iIndexLayer].fFrameRate = (double) (FPS * 1.0f);
|
2014-11-10 21:38:56 -06:00
|
|
|
// param.sSpatialLayers[iIndexLayer].iQualityLayerNum = 1;
|
2014-12-09 15:19:05 -06:00
|
|
|
param.sSpatialLayers[iIndexLayer].iSpatialBitrate = param.iTargetBitrate;
|
|
|
|
//param.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate = param.iTargetBitrate;
|
2014-12-04 18:35:20 -06:00
|
|
|
//param.sSpatialLayers[iIndexLayer].uiLevelIdc = LEVEL_1_3;
|
|
|
|
param.sSpatialLayers[iIndexLayer].uiProfileIdc = PRO_BASELINE;
|
|
|
|
|
|
|
|
|
|
|
|
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
|
|
|
|
param.bEnableFrameCroppingFlag = 1;
|
|
|
|
//param.iMaxBitrate = 1250000;
|
|
|
|
//param.iTargetBitrate = 1250000;
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
#ifdef MT_ENABLED
|
|
|
|
param.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = SM_DYN_SLICE;
|
|
|
|
param.sSpatialLayers[iIndexLayer].sSliceCfg.sSliceArgument.uiSliceSizeConstraint = SLICE_SIZE;
|
2014-12-08 14:29:18 -06:00
|
|
|
param.uiMaxNalSize = SLICE_SIZE;
|
2014-12-04 18:35:20 -06:00
|
|
|
#else
|
|
|
|
param.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = SM_SINGLE_SLICE;
|
2014-11-10 21:38:56 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
float fMaxFr = param.sSpatialLayers[param.iSpatialLayerNum - 1].fFrameRate;
|
|
|
|
for (int32_t i = param.iSpatialLayerNum - 2; i >= 0; --i) {
|
|
|
|
if (param.sSpatialLayers[i].fFrameRate > fMaxFr + EPSN) {
|
|
|
|
fMaxFr = param.sSpatialLayers[i].fFrameRate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
param.fMaxFrameRate = fMaxFr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return none-zero on error
|
|
|
|
long set_decoder_options(ISVCDecoder *decoder)
|
|
|
|
{
|
|
|
|
int32_t iColorFormat = videoFormatI420;
|
|
|
|
// EBufferProperty eOutputProperty = BUFFER_HOST;
|
|
|
|
long ret = 0;
|
|
|
|
|
|
|
|
ret += decoder->SetOption(DECODER_OPTION_DATAFORMAT, &iColorFormat);
|
|
|
|
// ret += decoder->SetOption(DECODER_OPTION_OUTPUT_PROPERTY, &eOutputProperty);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_size_t buffer_h264_nalu(h264_codec_context_t *context, switch_frame_t *frame)
|
|
|
|
{
|
|
|
|
uint8_t nalu_idc = 0;
|
|
|
|
uint8_t nalu_type = 0;
|
|
|
|
uint8_t *data = (uint8_t *)frame->data;
|
|
|
|
uint8_t nalu_hdr = *data;
|
|
|
|
uint8_t sync_bytes[] = {0, 0, 0, 1};
|
|
|
|
switch_buffer_t *buffer = context->nalu_buffer;
|
|
|
|
switch_size_t size = 0;
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
switch_assert(frame);
|
2014-11-26 20:30:42 +08:00
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
nalu_idc = (nalu_hdr & 0x60) >> 5;
|
|
|
|
nalu_type = nalu_hdr & 0x1f;
|
|
|
|
|
|
|
|
if (!context->got_sps && nalu_type != 7) {
|
2014-12-09 15:19:05 -06:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Waiting SPS/PPS, Got %d\n", nalu_type);
|
2014-11-18 16:39:32 -06:00
|
|
|
return 0;
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
2014-12-09 18:15:53 -06:00
|
|
|
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "XXX GOT %d\n", nalu_type);
|
|
|
|
|
2014-12-09 15:19:05 -06:00
|
|
|
if (!context->got_sps) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found SPS/PPS\n");
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "=========================================================================================\n");
|
|
|
|
context->got_sps = 1;
|
|
|
|
}
|
2014-11-10 21:38:56 -06:00
|
|
|
|
2014-11-26 20:30:42 +08:00
|
|
|
/* hack for phones sending sps/pps with frame->m = 1 such as grandstream */
|
|
|
|
if ((nalu_type == 7 || nalu_type == 8) && frame->m) frame->m = SWITCH_FALSE;
|
2014-12-09 15:19:05 -06:00
|
|
|
|
2014-11-26 20:30:42 +08:00
|
|
|
if (nalu_type == 28) { // 0x1c FU-A
|
2014-12-09 18:15:53 -06:00
|
|
|
int start = *(data + 1) & 0x80;
|
|
|
|
int end = *(data + 1) & 0x40;
|
|
|
|
|
2014-11-26 20:30:42 +08:00
|
|
|
nalu_type = *(data + 1) & 0x1f;
|
2014-11-18 16:39:32 -06:00
|
|
|
|
2014-12-09 18:15:53 -06:00
|
|
|
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "WTF start:%d end:%d mark:%d\n", start, end, frame->m);
|
|
|
|
|
|
|
|
if (frame->m) end = 1;
|
2014-11-26 20:30:42 +08:00
|
|
|
|
2014-12-09 18:15:53 -06:00
|
|
|
if (start && end) return 1;
|
|
|
|
|
|
|
|
if (start) {
|
|
|
|
if (context->nalu_28_start) {
|
|
|
|
context->nalu_28_start = 0;
|
|
|
|
switch_buffer_zero(buffer);
|
|
|
|
}
|
|
|
|
} else if (end) {
|
|
|
|
context->nalu_28_start = 0;
|
|
|
|
} else if (!context->nalu_28_start) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start) {
|
|
|
|
//uint8_t nalu_idc = (nalu_hdr & 0x60) >> 5;
|
|
|
|
nalu_type |= (nalu_idc << 5);
|
2014-11-26 20:30:42 +08:00
|
|
|
size = switch_buffer_write(buffer, sync_bytes, sizeof(sync_bytes));
|
|
|
|
size = switch_buffer_write(buffer, &nalu_type, 1);
|
|
|
|
context->nalu_28_start = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = switch_buffer_write(buffer, (void *)(data + 2), frame->datalen - 2);
|
|
|
|
} else {
|
|
|
|
size = switch_buffer_write(buffer, sync_bytes, sizeof(sync_bytes));
|
|
|
|
size = switch_buffer_write(buffer, frame->data, frame->datalen);
|
|
|
|
context->nalu_28_start = 0;
|
|
|
|
}
|
2014-11-10 21:38:56 -06:00
|
|
|
|
2014-12-09 15:19:05 -06:00
|
|
|
if (frame->m) context->nalu_28_start = 0;
|
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
#ifdef DEBUG_H264
|
2014-11-26 20:30:42 +08:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ts: %ld len: %4d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x mark=%d size=%" SWITCH_SIZE_T_FMT "\n",
|
2014-11-10 21:38:56 -06:00
|
|
|
(frame)->timestamp, (frame)->datalen,
|
|
|
|
*((uint8_t *)(frame)->data), *((uint8_t *)(frame)->data + 1),
|
|
|
|
*((uint8_t *)(frame)->data + 2), *((uint8_t *)(frame)->data + 3),
|
|
|
|
*((uint8_t *)(frame)->data + 4), *((uint8_t *)(frame)->data + 5),
|
|
|
|
*((uint8_t *)(frame)->data + 6), *((uint8_t *)(frame)->data + 7),
|
|
|
|
*((uint8_t *)(frame)->data + 8), *((uint8_t *)(frame)->data + 9),
|
|
|
|
*((uint8_t *)(frame)->data + 10), (frame)->m, size);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
static switch_status_t nalu_slice(h264_codec_context_t *context, switch_frame_t *frame)
|
2014-11-10 21:38:56 -06:00
|
|
|
{
|
|
|
|
int nalu_len;
|
|
|
|
uint8_t *buffer;
|
|
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->m = SWITCH_FALSE;
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
if (context->cur_nalu_index >= context->bit_stream_info.sLayerInfo[context->cur_layer].iNalCount) {
|
|
|
|
context->cur_nalu_index = 0;
|
|
|
|
context->cur_layer++;
|
|
|
|
context->last_nalu_data_pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "uiTemporalId:%d uiSpatialId:%d uiQualityId:%d uiLayerType:%d FrameType: %d\n",
|
|
|
|
context->bit_stream_info.sLayerInfo[context->cur_layer].uiTemporalId,
|
|
|
|
context->bit_stream_info.sLayerInfo[context->cur_layer].uiSpatialId,
|
|
|
|
context->bit_stream_info.sLayerInfo[context->cur_layer].uiQualityId,
|
|
|
|
context->bit_stream_info.sLayerInfo[context->cur_layer].uiLayerType,
|
|
|
|
context->last_frame_type);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (context->last_frame_type == videoFrameTypeSkip ||
|
|
|
|
context->cur_layer >= context->bit_stream_info.iLayerNum) {
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->datalen = 0;
|
|
|
|
frame->m = SWITCH_TRUE;
|
2014-11-10 21:38:56 -06:00
|
|
|
context->cur_layer = 0;
|
|
|
|
context->cur_nalu_index = 0;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
nalu_len = context->bit_stream_info.sLayerInfo[context->cur_layer].pNalLengthInByte[context->cur_nalu_index] - 4; // NALU w/o sync bits
|
|
|
|
buffer = context->bit_stream_info.sLayerInfo[context->cur_layer].pBsBuf;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "layer: %d/%d nalu:%d/%d nalu_len:%d/%d\n",
|
|
|
|
context->cur_layer, context->bit_stream_info.iLayerNum,
|
|
|
|
context->cur_nalu_index, context->bit_stream_info.sLayerInfo[context->cur_layer].iNalCount,
|
|
|
|
nalu_len, context->last_nalu_data_pos);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch_assert(nalu_len > 0);
|
|
|
|
|
2014-12-05 17:18:23 -06:00
|
|
|
if (nalu_len <= SLICE_SIZE) {
|
2014-11-10 21:38:56 -06:00
|
|
|
uint8_t nalu_type;
|
|
|
|
|
|
|
|
context->last_nalu_data_pos += 4;
|
|
|
|
nalu_type = *(buffer + context->last_nalu_data_pos) & 0x1f;
|
|
|
|
|
|
|
|
// if (nalu_type == 7) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Got SPS\n");
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
memcpy(frame->data, (buffer + context->last_nalu_data_pos), nalu_len);
|
|
|
|
frame->datalen = nalu_len;
|
2014-12-08 14:29:18 -06:00
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
if ((context->cur_nalu_index == context->bit_stream_info.sLayerInfo[context->cur_layer].iNalCount - 1) &&
|
|
|
|
(context->cur_layer == context->bit_stream_info.iLayerNum - 1)) {
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->m = SWITCH_TRUE;
|
2014-11-10 21:38:56 -06:00
|
|
|
} else {
|
|
|
|
status = SWITCH_STATUS_MORE_DATA;
|
|
|
|
}
|
|
|
|
context->cur_nalu_index++;
|
|
|
|
context->last_nalu_data_pos += nalu_len;
|
|
|
|
context->last_nalu_type = nalu_type;
|
|
|
|
goto end;
|
|
|
|
} else {
|
|
|
|
int left = nalu_len;
|
2014-11-18 16:39:32 -06:00
|
|
|
uint8_t *p = (uint8_t *) frame->data;
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
if (context->nalu_eat) {
|
|
|
|
left = nalu_len + 4 - context->nalu_eat;
|
|
|
|
switch_assert(left > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left > (SLICE_SIZE - 2)) {
|
|
|
|
uint8_t start_bit;
|
|
|
|
|
|
|
|
if (context->nalu_eat) {
|
|
|
|
start_bit = 0;
|
|
|
|
} else {
|
|
|
|
start_bit = 0x80;
|
|
|
|
context->last_nalu_data_pos += 4;
|
|
|
|
context->last_nalu_type = *(buffer + context->last_nalu_data_pos) & 0x1f;
|
2014-12-08 14:29:18 -06:00
|
|
|
context->last_nri = *(buffer + context->last_nalu_data_pos) & 0x60;
|
2014-11-10 21:38:56 -06:00
|
|
|
context->last_nalu_data_pos++;
|
|
|
|
context->nalu_eat = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
p[0] = context->last_nri | 28; // FU-A
|
|
|
|
p[1] = start_bit | context->last_nalu_type;
|
|
|
|
|
|
|
|
memcpy(p + 2, buffer + context->last_nalu_data_pos, SLICE_SIZE - 2);
|
|
|
|
context->last_nalu_data_pos += (SLICE_SIZE - 2);
|
|
|
|
context->nalu_eat += (SLICE_SIZE - 2);
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->datalen = SLICE_SIZE;
|
2014-11-10 21:38:56 -06:00
|
|
|
status = SWITCH_STATUS_MORE_DATA;
|
|
|
|
goto end;
|
|
|
|
} else {
|
|
|
|
p[0] = context->last_nri | 28; // FU-A
|
|
|
|
p[1] = 0x40 | context->last_nalu_type;
|
|
|
|
memcpy(p + 2, buffer + context->last_nalu_data_pos, left);
|
|
|
|
context->last_nalu_data_pos += left;
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->datalen = left + 2;
|
|
|
|
frame->m = SWITCH_TRUE;
|
2014-11-10 21:38:56 -06:00
|
|
|
context->nalu_eat = 0;
|
|
|
|
context->cur_nalu_index++;
|
2014-11-20 22:30:43 +08:00
|
|
|
status = SWITCH_STATUS_SUCCESS;
|
2014-11-10 21:38:56 -06:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
2014-12-08 14:29:18 -06:00
|
|
|
#if 0
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%d mark=%d BYTES datalen:%d %02x %02x\n", nalu_len, frame->m, frame->datalen, *((uint8_t *)frame->data), *((uint8_t *)frame->data + 1));
|
|
|
|
#endif
|
2014-11-10 21:38:56 -06:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static switch_status_t switch_h264_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
|
|
|
|
{
|
|
|
|
h264_codec_context_t *context = NULL;
|
|
|
|
int encoding, decoding;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
|
|
|
|
decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
|
|
|
|
|
|
|
|
if (!(encoding || decoding)) {
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (codec->fmtp_in) {
|
|
|
|
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
context = (h264_codec_context_t*)switch_core_alloc(codec->memory_pool, sizeof(h264_codec_context_t));
|
|
|
|
memset(context, 0, sizeof(*context));
|
|
|
|
|
|
|
|
if (decoding) {
|
|
|
|
WelsCreateDecoder(&context->decoder);
|
|
|
|
|
|
|
|
if (!context->decoder) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "CreateDecoder Error\n");
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
context->decoder_params.eOutputColorFormat = videoFormatI420;
|
|
|
|
context->decoder_params.uiTargetDqLayer = (uint8_t) -1;
|
|
|
|
context->decoder_params.eEcActiveIdc = ERROR_CON_SLICE_COPY;
|
|
|
|
context->decoder_params.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;
|
|
|
|
context->decoder_params.sVideoProperty.size = sizeof(context->decoder_params.sVideoProperty);
|
|
|
|
|
|
|
|
if (context->decoder->Initialize(&context->decoder_params)) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Initialize failed\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set_decoder_options(context->decoder)) {
|
2014-11-26 20:30:42 +08:00
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Set Decoder Options Error\n");
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encoding) {
|
|
|
|
ret = WelsCreateSVCEncoder(&context->encoder);
|
|
|
|
if (ret) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create encoder, error: %d\n", ret);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
FillSpecificParameters(context->encoder_params);
|
|
|
|
}
|
|
|
|
|
|
|
|
//if (encoding | decoding) WelsStderrSetTraceLevel(10);
|
|
|
|
|
|
|
|
switch_buffer_create_dynamic(&(context->nalu_buffer), H264_NALU_BUFFER_SIZE, H264_NALU_BUFFER_SIZE * 8, 0);
|
|
|
|
codec->private_info = context;
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
error:
|
|
|
|
// TODO, do some proper clean up
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static switch_status_t init_encoder(h264_codec_context_t *context, uint32_t width, uint32_t height)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
context->encoder_params.iPicWidth = width;
|
|
|
|
context->encoder_params.iPicHeight = height;
|
2014-12-09 15:19:05 -06:00
|
|
|
//context->encoder_params.iTargetBitrate = width * height * 8;
|
2014-11-10 21:38:56 -06:00
|
|
|
for (int i=0; i<context->encoder_params.iSpatialLayerNum; i++) {
|
|
|
|
context->encoder_params.sSpatialLayers[i].iVideoWidth = width;
|
|
|
|
context->encoder_params.sSpatialLayers[i].iVideoHeight = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* just do it, the encoder will Uninitialize first by itself if already initialized */
|
|
|
|
if (cmResultSuccess != context->encoder->InitializeExt(&context->encoder_params)) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder Init Error\n");
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
2014-11-18 16:39:32 -06:00
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
context->encoder_initialized = SWITCH_TRUE;
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
static switch_status_t switch_h264_encode(switch_codec_t *codec, switch_frame_t *frame)
|
2014-11-10 21:38:56 -06:00
|
|
|
{
|
|
|
|
h264_codec_context_t *context = (h264_codec_context_t *)codec->private_info;
|
|
|
|
int width = 0;
|
|
|
|
int height = 0;
|
|
|
|
long enc_ret;
|
|
|
|
long result;
|
|
|
|
|
2014-11-20 22:30:43 +08:00
|
|
|
frame->m = SWITCH_FALSE;
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
if (context->need_key_frame) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "H264 KEYFRAME GENERATED\n");
|
2014-11-10 21:38:56 -06:00
|
|
|
context->encoder->ForceIntraFrame(1);
|
2014-11-18 16:39:32 -06:00
|
|
|
context->need_key_frame = 0;
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
if (frame->flags & SFF_SAME_IMAGE) {
|
|
|
|
return nalu_slice(context, frame);
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
|
|
|
|
if (frame->img->d_h > 1) {
|
|
|
|
width = frame->img->d_w;
|
|
|
|
height = frame->img->d_h;
|
|
|
|
} else {
|
|
|
|
width = frame->img->w;
|
|
|
|
height = frame->img->h;
|
|
|
|
}
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
//switch_assert(width > 0 && (width % 2 == 0));
|
|
|
|
//switch_assert(height > 0 && (height % 2 == 0));
|
|
|
|
|
|
|
|
if (!context->encoder_initialized) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "initializing encoder %dx%d\n", width, height);
|
|
|
|
init_encoder(context, width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width != context->encoder_params.iPicWidth || height != context->encoder_params.iPicHeight ) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "picture size changed from %dx%d to %dx%d, reinitializing encoder",
|
|
|
|
context->encoder_params.iPicWidth, context->encoder_params.iPicHeight, width, height);
|
|
|
|
init_encoder(context, width, height);
|
|
|
|
}
|
|
|
|
|
2014-12-05 17:18:23 -06:00
|
|
|
context->pic.iColorFormat = videoFormatI420;
|
|
|
|
context->pic.iPicHeight = height;
|
|
|
|
context->pic.iPicWidth = width;
|
|
|
|
context->pic.iStride[0] = frame->img->stride[0];
|
|
|
|
context->pic.iStride[1] = frame->img->stride[1];
|
|
|
|
context->pic.iStride[2] = frame->img->stride[2];
|
2014-11-10 21:38:56 -06:00
|
|
|
|
2014-12-05 17:18:23 -06:00
|
|
|
context->pic.pData[0] = frame->img->planes[0];
|
|
|
|
context->pic.pData[1] = frame->img->planes[1];
|
|
|
|
context->pic.pData[2] = frame->img->planes[2];
|
2014-11-10 21:38:56 -06:00
|
|
|
|
2014-12-05 17:18:23 -06:00
|
|
|
result = (EVideoFrameType)context->encoder->EncodeFrame(&context->pic, &context->bit_stream_info);
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
if (result != cmResultSuccess ) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "EncodeFrame() failed, result = %ld\n", result);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
context->cur_layer = 0;
|
|
|
|
context->cur_nalu_index = 0;
|
|
|
|
context->last_nalu_data_pos = 0;
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
return nalu_slice(context, frame);
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
error:
|
2014-11-18 16:39:32 -06:00
|
|
|
|
|
|
|
frame->datalen = 0;
|
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
return SWITCH_STATUS_FALSE;
|
|
|
|
}
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
static switch_status_t switch_h264_decode(switch_codec_t *codec, switch_frame_t *frame)
|
2014-11-10 21:38:56 -06:00
|
|
|
{
|
|
|
|
h264_codec_context_t *context = (h264_codec_context_t *)codec->private_info;
|
|
|
|
switch_size_t size = 0;
|
|
|
|
uint32_t error_code;
|
2014-11-18 16:39:32 -06:00
|
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "len: %d ts: %u mark:%d\n", frame->datalen, ntohl(frame->timestamp), frame->m);
|
|
|
|
|
2014-12-09 15:19:05 -06:00
|
|
|
if (context->last_received_timestamp && context->last_received_timestamp != frame->timestamp &&
|
2014-11-10 21:38:56 -06:00
|
|
|
(!frame->m) && (!context->last_received_complete_picture)) {
|
|
|
|
// possible packet loss
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Packet Loss, skip privousely received packets\n");
|
2014-11-18 16:39:32 -06:00
|
|
|
switch_goto_status(SWITCH_STATUS_RESTART, end);
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
context->last_received_timestamp = frame->timestamp;
|
|
|
|
context->last_received_complete_picture = frame->m ? SWITCH_TRUE : SWITCH_FALSE;
|
|
|
|
|
|
|
|
size = buffer_h264_nalu(context, frame);
|
2014-11-26 20:30:42 +08:00
|
|
|
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "READ buf:%ld got_key:%d st:%d m:%d size:%" SWITCH_SIZE_T_FMT "\n", size, context->got_sps, status, frame->m, size);
|
2014-11-10 21:38:56 -06:00
|
|
|
|
2014-12-09 18:15:53 -06:00
|
|
|
if (size == 1) {
|
|
|
|
switch_goto_status(SWITCH_STATUS_RESTART, end);
|
|
|
|
}
|
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
if (frame->m && size) {
|
|
|
|
int got_picture = 0;
|
|
|
|
int i;
|
|
|
|
const void *nalu = NULL;
|
|
|
|
int width, height;
|
|
|
|
SBufferInfo dest_buffer_info;
|
|
|
|
switch_buffer_peek_zerocopy(context->nalu_buffer, &nalu);
|
|
|
|
uint8_t* pData[3] = { 0 };
|
2014-11-18 16:39:32 -06:00
|
|
|
|
|
|
|
frame->m = SWITCH_FALSE;
|
|
|
|
frame->flags = 0;
|
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
pData[0] = NULL;
|
|
|
|
pData[1] = NULL;
|
|
|
|
pData[2] = NULL;
|
|
|
|
memset(&dest_buffer_info, 0, sizeof(dest_buffer_info));
|
|
|
|
|
|
|
|
error_code = context->decoder->DecodeFrame2((uint8_t *)nalu, size, (uint8_t **)pData, &dest_buffer_info);
|
|
|
|
|
|
|
|
if (error_code == dsErrorFree && dest_buffer_info.iBufferStatus == 1) {
|
|
|
|
width = dest_buffer_info.UsrData.sSystemBuffer.iWidth;
|
|
|
|
height = dest_buffer_info.UsrData.sSystemBuffer.iHeight;
|
|
|
|
|
2014-11-26 20:30:42 +08:00
|
|
|
#ifdef DEBUG_H264
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got pic: [%dx%d]\n", width, height);
|
|
|
|
#endif
|
2014-11-10 21:38:56 -06:00
|
|
|
if (!context->img) {
|
|
|
|
context->img = switch_img_wrap(NULL, SWITCH_IMG_FMT_I420, width, height, 0, pData[0]);
|
|
|
|
assert(context->img);
|
|
|
|
}
|
|
|
|
|
|
|
|
context->img->w = dest_buffer_info.UsrData.sSystemBuffer.iStride[0];
|
|
|
|
context->img->h = dest_buffer_info.UsrData.sSystemBuffer.iStride[1];
|
|
|
|
context->img->d_w = width;
|
|
|
|
context->img->d_h = height;
|
|
|
|
context->img->planes[0] = pData[0];
|
|
|
|
context->img->planes[1] = pData[1];
|
|
|
|
context->img->planes[2] = pData[2];
|
|
|
|
context->img->stride[0] = dest_buffer_info.UsrData.sSystemBuffer.iStride[0];
|
|
|
|
context->img->stride[1] = dest_buffer_info.UsrData.sSystemBuffer.iStride[1];
|
|
|
|
context->img->stride[2] = dest_buffer_info.UsrData.sSystemBuffer.iStride[1];
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
frame->img = context->img;
|
2014-11-10 21:38:56 -06:00
|
|
|
// TODO: keep going and see if more picture available
|
|
|
|
// pDecoder->DecodeFrame (NULL, 0, pData, &sDstBufInfo);
|
|
|
|
} else {
|
|
|
|
if (error_code) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decode error: 0x%x\n", error_code);
|
2014-11-18 16:39:32 -06:00
|
|
|
switch_goto_status(SWITCH_STATUS_RESTART, end);
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
}
|
2014-12-03 20:34:49 -06:00
|
|
|
//switch_set_flag(frame, SFF_USE_VIDEO_TIMESTAMP);
|
2014-11-10 21:38:56 -06:00
|
|
|
switch_buffer_zero(context->nalu_buffer);
|
2014-12-09 15:19:05 -06:00
|
|
|
context->nalu_28_start = 0;
|
2014-11-18 16:39:32 -06:00
|
|
|
status = SWITCH_STATUS_SUCCESS;
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
2014-11-18 16:39:32 -06:00
|
|
|
|
|
|
|
if (size == 0) {
|
2014-11-20 22:30:43 +08:00
|
|
|
status = SWITCH_STATUS_MORE_DATA;
|
2014-11-18 16:39:32 -06:00
|
|
|
}
|
2014-11-20 22:30:43 +08:00
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
if (status == SWITCH_STATUS_RESTART) {
|
|
|
|
context->got_sps = 0;
|
|
|
|
switch_buffer_zero(context->nalu_buffer);
|
2014-12-09 15:19:05 -06:00
|
|
|
context->nalu_28_start = 0;
|
|
|
|
|
2014-11-26 20:30:42 +08:00
|
|
|
#if 0
|
|
|
|
/* re-initialize decoder, trying to recover from really bad H264 bit streams */
|
|
|
|
if (context->decoder->Initialize(&context->decoder_params)) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Initialize failed\n");
|
|
|
|
} else if (set_decoder_options(context->decoder)) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Set Decoder Options Error\n");
|
|
|
|
}
|
|
|
|
#endif
|
2014-11-18 16:39:32 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!context->got_sps) {
|
|
|
|
switch_set_flag(frame, SFF_WAIT_KEY_FRAME);
|
|
|
|
}
|
|
|
|
|
2014-11-18 17:36:55 -06:00
|
|
|
if (frame->img) {
|
|
|
|
switch_set_flag(frame, SFF_USE_VIDEO_TIMESTAMP);
|
|
|
|
} else {
|
2014-11-18 16:39:32 -06:00
|
|
|
status = SWITCH_STATUS_MORE_DATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2014-11-10 21:38:56 -06:00
|
|
|
}
|
|
|
|
|
2014-11-12 21:30:39 -06:00
|
|
|
static switch_status_t switch_h264_control(switch_codec_t *codec,
|
|
|
|
switch_codec_control_command_t cmd,
|
|
|
|
switch_codec_control_type_t ctype,
|
|
|
|
void *cmd_data,
|
|
|
|
switch_codec_control_type_t *rtype,
|
|
|
|
void **ret_data) {
|
|
|
|
|
2014-11-18 16:39:32 -06:00
|
|
|
|
|
|
|
|
|
|
|
h264_codec_context_t *context = (h264_codec_context_t *)codec->private_info;
|
|
|
|
|
|
|
|
switch(cmd) {
|
|
|
|
case SCC_VIDEO_REFRESH:
|
|
|
|
context->need_key_frame = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-11-12 21:30:39 -06:00
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-11-10 21:38:56 -06:00
|
|
|
static switch_status_t switch_h264_destroy(switch_codec_t *codec)
|
|
|
|
{
|
|
|
|
h264_codec_context_t *context = (h264_codec_context_t *)codec->private_info;
|
|
|
|
|
|
|
|
if (!context) return SWITCH_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
if (context->nalu_buffer) switch_buffer_destroy(&context->nalu_buffer);
|
|
|
|
|
|
|
|
if (context->encoder) {
|
|
|
|
context->encoder->Uninitialize();
|
|
|
|
WelsDestroySVCEncoder(context->encoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (context->decoder) {
|
2014-11-25 15:01:52 -06:00
|
|
|
switch_img_free(&context->img);
|
2014-11-10 21:38:56 -06:00
|
|
|
context->decoder->Uninitialize();
|
|
|
|
WelsDestroyDecoder(context->decoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SWITCH_MODULE_LOAD_FUNCTION(mod_openh264_load)
|
|
|
|
{
|
|
|
|
switch_codec_interface_t *codec_interface;
|
|
|
|
|
|
|
|
/* connect my internal structure to the blank pointer passed to me */
|
|
|
|
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
|
|
|
|
|
|
|
SWITCH_ADD_CODEC(codec_interface, "H264 Video (with Cisco OpenH264)");
|
|
|
|
switch_core_codec_add_video_implementation(pool, codec_interface, 99, "H264", NULL,
|
2014-11-12 21:30:39 -06:00
|
|
|
switch_h264_init, switch_h264_encode, switch_h264_decode, switch_h264_control, switch_h264_destroy);
|
2014-11-10 21:38:56 -06:00
|
|
|
|
|
|
|
/* indicate that the module should continue to be loaded */
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For Emacs:
|
|
|
|
* Local Variables:
|
|
|
|
* mode:c
|
|
|
|
* indent-tabs-mode:t
|
|
|
|
* tab-width:4
|
|
|
|
* c-basic-offset:4
|
|
|
|
* End:
|
|
|
|
* For VIM:
|
|
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
|
|
|
|
*/
|