From 29bddbd3fc415e2cce9a6987e9a58bbe96c02fa5 Mon Sep 17 00:00:00 2001 From: Seven Du Date: Sat, 14 Mar 2015 02:57:29 +0800 Subject: [PATCH] WIP add experimental vorbis codec made it like a codec mod so we can share the same code in case opus is fully supported in webm add -lvorbis -lvorbisenc to Makefile the audio is still completely silence, hope we can find out why --- src/mod/formats/mod_webm/mod_vorbis.c | 363 ++++++++++++++++++++++++++ src/mod/formats/mod_webm/mod_webm.cpp | 15 +- src/mod/formats/mod_webm/mod_webm.h | 47 ++++ 3 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 src/mod/formats/mod_webm/mod_vorbis.c create mode 100644 src/mod/formats/mod_webm/mod_webm.h diff --git a/src/mod/formats/mod_webm/mod_vorbis.c b/src/mod/formats/mod_webm/mod_vorbis.c new file mode 100644 index 0000000000..2f7dc8d248 --- /dev/null +++ b/src/mod/formats/mod_webm/mod_vorbis.c @@ -0,0 +1,363 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2015, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Seven Du + * + * + * mod_vorbis.c -- Vorbis Audio Codec + * + */ + +#include +#include + +// SWITCH_MODULE_LOAD_FUNCTION(mod_vorbis_load); +// SWITCH_MODULE_DEFINITION(mod_vorbis, mod_vorbis_load, NULL, NULL); + +#define SCC_GET_CODEC_PRIVATE SCC_VIDEO_BANDWIDTH + +typedef struct vorbis_context_s { + ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment vc; /* struct that stores all the user comments */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + uint8_t *codec_private_data; + uint16_t codec_private_data_len; +} vorbis_context_t; + +static void xiph_lacing(uint x, uint8_t **buffer) +{ + uint8_t *p = *buffer; + + while (x >= 255) { + *p++ = 255; + x -= 255; + } + + *p++ = x; + *buffer = p; +} + +static switch_status_t switch_vorbis_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) +{ + vorbis_context_t *context = (vorbis_context_t *)codec->private_info; + + switch(cmd) { + case SCC_GET_CODEC_PRIVATE: + { + uint32_t len = 0; + uint8_t *p; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%ld %ld %ld\n", + context->header.bytes, context->header_comm.bytes, context->header_code.bytes); + + len = context->header.bytes + context->header_comm.bytes + context->header_code.bytes; + len += 20; + + if (!context->codec_private_data) { + // uint16_t size; + + p = context->codec_private_data = switch_core_alloc(codec->memory_pool, len); + switch_assert(p); + +#if 0 + // ffmpeg also can decode this one + size = context->header.bytes; + *(uint16_t *)p = htons(size); + p+=2; + memcpy(p, context->header.packet, context->header.bytes); + p += context->header.bytes; + + size = context->header_comm.bytes; + *(uint16_t *)p = htons(size); + p+=2; + memcpy(p, context->header_comm.packet, context->header_comm.bytes); + p += context->header_comm.bytes; + + size = context->header_code.bytes; + *(uint16_t *)p = htons(size); + p+=2; + memcpy(p, context->header_code.packet, context->header_code.bytes); + p += context->header_code.bytes; +#else + // SPEC + // http://www.matroska.org/technical/specs/codecid/index.html + // A_VORBIS + *p++ = 2; + xiph_lacing(context->header.bytes, &p); + xiph_lacing(context->header_comm.bytes, &p); + memcpy(p, context->header.packet, context->header.bytes); + p += context->header.bytes; + memcpy(p, context->header_comm.packet, context->header_comm.bytes); + p += context->header_comm.bytes; + memcpy(p, context->header_code.packet, context->header_code.bytes); + p += context->header_code.bytes; +#endif + + context->codec_private_data_len = p - context->codec_private_data; + } + + switch_assert(context->codec_private_data_len < 20000); + + *(int16_t *)cmd_data = context->codec_private_data_len; + *ret_data = (void *)context->codec_private_data; + } + break; + default: break; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_vorbis_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) +{ + int encoding, decoding; + vorbis_context_t *context; + int ret = 0; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding)) { + return SWITCH_STATUS_FALSE; + } else { + if (codec->fmtp_in) { + codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in); + } + } + + context = switch_core_alloc(codec->memory_pool, sizeof(*context)); + memset(context, 0, sizeof(*context)); + codec->private_info = context; + + vorbis_info_init(&context->vi); + + // https://xiph.org/vorbis/doc/vorbisenc/examples.html + ret=vorbis_encode_init_vbr(&context->vi, + codec->implementation->number_of_channels, + codec->implementation->actual_samples_per_second, 0.4); + +#if 0 + ret = vorbis_encode_init(&context->vi, + codec->implementation->number_of_channels, + codec->implementation->actual_samples_per_second, + -1,128000,-1); +#endif + + if (ret) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VORBIS codec init error, ret = %d\n", ret); + goto error; + } + + vorbis_comment_init(&context->vc); + vorbis_comment_add_tag(&context->vc, "ENCODER", "FreeSWITCH"); + + ret = vorbis_analysis_init(&context->vd, &context->vi); + if (ret) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VORBIS analysis init error, ret = %d\n", ret); + goto error; + } + + ret = vorbis_block_init(&context->vd, &context->vb); + if (ret) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VORBIS block init error, ret = %d\n", ret); + goto error; + } + + // headers + vorbis_analysis_headerout(&context->vd, &context->vc, &context->header, &context->header_comm, &context->header_code); + + ogg_stream_init(&context->os, rand()); + + {//hack so we can use the codec control + switch_codec_implementation_t **impl = (switch_codec_implementation_t **)&codec->implementation; + (*impl)->codec_control = switch_vorbis_control; + } + + return SWITCH_STATUS_SUCCESS; + +error: + vorbis_block_clear(&context->vb); + vorbis_dsp_clear(&context->vd); + vorbis_comment_clear(&context->vc); + vorbis_info_clear(&context->vi); + + return SWITCH_STATUS_GENERR; +} + +static switch_status_t switch_vorbis_encode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *decoded_data, + uint32_t decoded_data_len, + uint32_t decoded_rate, void *encoded_data, uint32_t *encoded_data_len, uint32_t *encoded_rate, + unsigned int *flag) +{ + vorbis_context_t *context = (vorbis_context_t *)codec->private_info; + int i; + uint32_t len = 0; + uint16_t *data = (uint16_t *)decoded_data; + int channels = codec->implementation->number_of_channels; + float **buffer = vorbis_analysis_buffer(&context->vd, decoded_data_len); + + // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%u\n", decoded_data_len); + + for (i = 0; i < decoded_data_len * channels; i += channels) { + buffer[0][i] = *(data + i) / 32768.f; + if (channels > 1) { + buffer[1][i] = *(data + i + 1) / 32768.f; + } + } + + vorbis_analysis_wrote(&context->vd, decoded_data_len); + + while (vorbis_analysis_blockout(&context->vd, &context->vb) == 1) { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis(&context->vb, NULL); + vorbis_bitrate_addblock(&context->vb); + + while (vorbis_bitrate_flushpacket(&context->vd, &context->op)){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "got %ld bytes pts: %lld\n", context->op.bytes, context->op.granulepos); + + if (len + context->op.bytes > *encoded_data_len) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "buffer overflow %u\n", *encoded_data_len); + break; + } + + memcpy((uint8_t *)decoded_data + len, context->op.packet, context->op.bytes); + len += context->op.bytes; + } + } + + *encoded_data_len = len; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t switch_vorbis_decode(switch_codec_t *codec, + switch_codec_t *other_codec, + void *encoded_data, + uint32_t encoded_data_len, + uint32_t encoded_rate, void *decoded_data, uint32_t *decoded_data_len, uint32_t *decoded_rate, + unsigned int *flag) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_status_t switch_vorbis_destroy(switch_codec_t *codec) +{ + vorbis_context_t *context = (vorbis_context_t *)codec->private_info; + + vorbis_block_clear(&context->vb); + vorbis_dsp_clear(&context->vd); + vorbis_comment_clear(&context->vc); + vorbis_info_clear(&context->vi); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_vorbis_load) +{ + switch_codec_interface_t *codec_interface; + int mpf = 20000, spf = 160, bpf = 320, counta, countb; + int rates[] = {0, 8000, 44100, 48000, 88200, 96000, 176400, 192000}; + switch_payload_t ianacode[4] = { 0, 99, 99, 99 }; + + /* 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, "VORBIS"); + + for (counta = 1; counta <= 3; counta++) { + for (countb = 1; countb > 0; countb--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + ianacode[counta], /* the IANA code number */ + "VORBIS", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + rates[counta], /* samples transferred per second */ + rates[counta], /* actual samples transferred per second */ + 0, /* bits transferred per second */ + mpf * countb, /* number of microseconds per frame */ + spf * countb, /* number of samples per frame */ + bpf * countb, /* number of bytes per frame decompressed */ + 0, /* number of bytes per frame compressed */ + 1, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_vorbis_init, /* function to initialize a codec handle using this implementation */ + switch_vorbis_encode, /* function to encode raw data into encoded data */ + switch_vorbis_decode, /* function to decode encoded data into raw data */ + switch_vorbis_destroy); /* deinitalize a codec handle using this implementation */ + } + spf = spf * 2; + } + + for (counta = 1; counta <= 3; counta++) { + for (countb = 1; countb > 0; countb--) { + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + ianacode[counta], /* the IANA code number */ + "vorbis", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + rates[counta], /* samples transferred per second */ + rates[counta], /* actual samples transferred per second */ + 0, /* bits transferred per second */ + mpf * countb, /* number of microseconds per frame */ + spf * countb, /* number of samples per frame */ + bpf * countb, /* number of bytes per frame decompressed */ + 0, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_vorbis_init, /* function to initialize a codec handle using this implementation */ + switch_vorbis_encode, /* function to encode raw data into encoded data */ + switch_vorbis_decode, /* function to decode encoded data into raw data */ + switch_vorbis_destroy); /* deinitalize a codec handle using this implementation */ + } + spf = spf * 2; + } + + /* 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 noet: + */ diff --git a/src/mod/formats/mod_webm/mod_webm.cpp b/src/mod/formats/mod_webm/mod_webm.cpp index 6013fc7829..689b5e0914 100644 --- a/src/mod/formats/mod_webm/mod_webm.cpp +++ b/src/mod/formats/mod_webm/mod_webm.cpp @@ -37,6 +37,7 @@ // using mkvmuxer::int64; // using mkvmuxer::uint64; +#include "mod_webm.h" #ifdef _MSC_VER // Disable MSVC warnings that suggest making code non-portable. @@ -49,8 +50,8 @@ SWITCH_MODULE_DEFINITION(mod_webm, mod_webm_load, NULL, NULL); #define IS_VP8_KEY_FRAME(byte) ((((byte) & 0x01) ^ 0x01) ? true : false) #define IS_VP9_KEY_FRAME(byte) (((byte) & 0x01) ? true : false) -#define AUDIO_CODEC "OPUS" -// #define AUDIO_CODEC "VORBIS" +// #define AUDIO_CODEC "OPUS" +#define AUDIO_CODEC "VORBIS" struct webm_file_context { switch_memory_pool_t *pool; @@ -190,6 +191,14 @@ static switch_status_t webm_file_open(switch_file_handle_t *handle, const char * } } + if (!strcmp(AUDIO_CODEC, "VORBIS")) { + uint16_t size = 0; + uint8_t *codec_private_data = NULL; + switch_core_codec_control(&context->audio_codec, SCC_GET_CODEC_PRIVATE, SCCT_INT, (void *)&size, NULL, (void **)&codec_private_data); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "======codec_private_data size: %d data: %p\n", size, codec_private_data); + context->audio->SetCodecPrivate(codec_private_data, size); + } + if (1) { // for better quality? int bw = 4096; switch_core_codec_control(&context->video_codec, SCC_VIDEO_BANDWIDTH, SCCT_INT, (void *)&bw, NULL, NULL); @@ -474,6 +483,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_webm_load) file_interface->file_set_string = webm_file_set_string; file_interface->file_get_string = webm_file_get_string; + mod_vorbis_load(module_interface, pool); + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/formats/mod_webm/mod_webm.h b/src/mod/formats/mod_webm/mod_webm.h new file mode 100644 index 0000000000..322498723c --- /dev/null +++ b/src/mod/formats/mod_webm/mod_webm.h @@ -0,0 +1,47 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2015, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Seven Du + * + * + * mod_webm.h -- Webm headers + * + */ + +#include + +#define SCC_GET_CODEC_PRIVATE SCC_VIDEO_BANDWIDTH + +typedef struct vorbis_context_s vorbis_context_t; + +#ifdef __cplusplus +extern "C" { +#endif + +SWITCH_MODULE_LOAD_FUNCTION(mod_vorbis_load); + +#ifdef __cplusplus +} +#endif