mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-23 12:42:02 +00:00
FS-7500: Work in progress. Added codec config params that can be set from session and made vpx codec re-init on size change. Also add periodic key frame timer
This commit is contained in:
committed by
Michael Jerris
parent
365a5dd820
commit
659c1e474e
@@ -44,18 +44,21 @@
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load);
|
||||
SWITCH_MODULE_DEFINITION(mod_vpx, mod_vpx_load, NULL, NULL);
|
||||
|
||||
|
||||
#define encoder_interface (vpx_codec_vp8_cx())
|
||||
#define decoder_interface (vpx_codec_vp8_dx())
|
||||
|
||||
struct vpx_context {
|
||||
switch_codec_t *codec;
|
||||
unsigned int flags;
|
||||
|
||||
switch_codec_settings_t codec_settings;
|
||||
unsigned int bandwidth;
|
||||
vpx_codec_enc_cfg_t config;
|
||||
|
||||
vpx_codec_ctx_t encoder;
|
||||
uint8_t encoder_init;
|
||||
vpx_image_t *pic;
|
||||
switch_bool_t force_key_frame;
|
||||
int width;
|
||||
int height;
|
||||
int bitrate;
|
||||
int fps;
|
||||
int format;
|
||||
int intra_period;
|
||||
@@ -65,9 +68,8 @@ struct vpx_context {
|
||||
const vpx_codec_cx_pkt_t *pkt;
|
||||
int pkt_pos;
|
||||
vpx_codec_iter_t iter;
|
||||
switch_time_t last_ts;
|
||||
|
||||
vpx_codec_ctx_t decoder;
|
||||
uint8_t decoder_init;
|
||||
switch_buffer_t *vpx_packet_buffer;
|
||||
int got_key_frame;
|
||||
switch_size_t last_received_timestamp;
|
||||
@@ -76,48 +78,17 @@ struct vpx_context {
|
||||
};
|
||||
typedef struct vpx_context vpx_context_t;
|
||||
|
||||
static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
|
||||
|
||||
static switch_status_t init_codec(switch_codec_t *codec)
|
||||
{
|
||||
vpx_context_t *context = NULL;
|
||||
int encoding, decoding;
|
||||
vpx_codec_ctx_t *encoder = NULL;
|
||||
vpx_codec_ctx_t *decoder = NULL;
|
||||
vpx_codec_enc_cfg_t *config;
|
||||
const vpx_codec_iface_t* encoder_interface = vpx_codec_vp8_cx();
|
||||
const vpx_codec_iface_t* decoder_interface = vpx_codec_vp8_dx();
|
||||
|
||||
|
||||
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
|
||||
decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
|
||||
|
||||
if (!(encoding || decoding) || ((context = switch_core_alloc(codec->memory_pool, sizeof(*context))) == 0)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
memset(context, 0, sizeof(*context));
|
||||
context->flags = flags;
|
||||
codec->private_info = context;
|
||||
|
||||
if (codec->fmtp_in) {
|
||||
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
|
||||
}
|
||||
|
||||
config = &context->config;
|
||||
|
||||
if (vpx_codec_enc_config_default(encoder_interface, config, 0) != VPX_CODEC_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder config Error\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
// very big defaults till we know why scaling segs it
|
||||
context->width = 3840;
|
||||
context->height = 2160;
|
||||
context->bitrate = 3840000;
|
||||
vpx_context_t *context = (vpx_context_t *)codec->private_info;
|
||||
vpx_codec_enc_cfg_t *config = &context->config;
|
||||
|
||||
// settings
|
||||
config->g_profile = 1;
|
||||
config->g_w = context->width;
|
||||
config->g_h = context->height;
|
||||
config->rc_target_bitrate = context->bitrate;
|
||||
config->g_w = context->codec_settings.video.width;
|
||||
config->g_h = context->codec_settings.video.height;
|
||||
config->rc_target_bitrate = context->bandwidth;
|
||||
config->g_timebase.num = 1;
|
||||
config->g_timebase.den = 1000;
|
||||
config->g_error_resilient = VPX_ERROR_RESILIENT_PARTITIONS;
|
||||
@@ -127,11 +98,11 @@ static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_
|
||||
config->rc_dropframe_thresh = 0;
|
||||
config->rc_end_usage = VPX_CBR;
|
||||
config->g_pass = VPX_RC_ONE_PASS;
|
||||
// config->kf_mode = VPX_KF_DISABLED;
|
||||
config->kf_mode = VPX_KF_AUTO;
|
||||
// config->kf_min_dist = FPS;// Intra Period 3 seconds;
|
||||
// config->kf_max_dist = FPS * 3;
|
||||
config->rc_resize_allowed = 0;
|
||||
config->kf_mode = VPX_KF_DISABLED;
|
||||
//config->kf_mode = VPX_KF_AUTO;
|
||||
//config->kf_min_dist = FPS;// Intra Period 3 seconds;
|
||||
//config->kf_max_dist = FPS;
|
||||
config->rc_resize_allowed = 1;
|
||||
config->rc_min_quantizer = 2;
|
||||
config->rc_max_quantizer = 56;
|
||||
//Rate control adaptation undershoot control.
|
||||
@@ -157,59 +128,74 @@ static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_
|
||||
// indicates that the client will buffer (at least) 5000ms worth
|
||||
// of encoded data. Use the target bitrate (rc_target_bitrate) to
|
||||
// convert to bits/bytes, if necessary.
|
||||
config->rc_buf_sz = 1000;
|
||||
config->rc_buf_sz = 5000;
|
||||
//Decoder Buffer Initial Size.
|
||||
// This value indicates the amount of data that will be buffered
|
||||
// by the decoding application prior to beginning playback.
|
||||
// This value is expressed in units of time (milliseconds).
|
||||
// Use the target bitrate (rc_target_bitrate) to convert to
|
||||
// bits/bytes, if necessary.
|
||||
config->rc_buf_initial_sz = 500;
|
||||
config->rc_buf_initial_sz = 1000;
|
||||
//Decoder Buffer Optimal Size.
|
||||
// This value indicates the amount of data that the encoder should
|
||||
// try to maintain in the decoder's buffer. This value is expressed
|
||||
// in units of time (milliseconds).
|
||||
// Use the target bitrate (rc_target_bitrate) to convert to
|
||||
// bits/bytes, if necessary.
|
||||
config->rc_buf_optimal_sz = 600;
|
||||
config->rc_buf_optimal_sz = 1000;
|
||||
|
||||
if (context->flags & SWITCH_CODEC_FLAG_ENCODE) {
|
||||
|
||||
if (context->encoder_init) {
|
||||
vpx_codec_destroy(&context->encoder);
|
||||
context->encoder_init = 0;
|
||||
}
|
||||
|
||||
if (encoding) {
|
||||
if (vpx_codec_enc_init(&context->encoder, encoder_interface, config, 0 & VPX_CODEC_USE_OUTPUT_PARTITION) != VPX_CODEC_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec init error: [%d:%s]\n", encoder->err, encoder->err_detail);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec init error: [%d:%s]\n", context->encoder.err, context->encoder.err_detail);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
context->encoder_init = 1;
|
||||
|
||||
// The static threshold imposes a change threshold on blocks below which they will be skipped by the encoder.
|
||||
vpx_codec_control(encoder, VP8E_SET_STATIC_THRESHOLD, 100);
|
||||
vpx_codec_control(&context->encoder, VP8E_SET_STATIC_THRESHOLD, 100);
|
||||
//Set cpu usage, a bit lower than normal (-6) but higher than android (-12)
|
||||
vpx_codec_control(encoder, VP8E_SET_CPUUSED, -8);
|
||||
vpx_codec_control(&context->encoder, VP8E_SET_CPUUSED, -6);
|
||||
// Only one partition
|
||||
// vpx_codec_control(encoder, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
|
||||
// vpx_codec_control(&context->encoder, VP8E_SET_TOKEN_PARTITIONS, VP8_ONE_TOKENPARTITION);
|
||||
// Enable noise reduction
|
||||
vpx_codec_control(encoder, VP8E_SET_NOISE_SENSITIVITY, 0);
|
||||
vpx_codec_control(&context->encoder, VP8E_SET_NOISE_SENSITIVITY, 1);
|
||||
//Set max data rate for Intra frames.
|
||||
// This value controls additional clamping on the maximum size of a keyframe.
|
||||
// It is expressed as a percentage of the average per-frame bitrate, with the
|
||||
// special (and default) value 0 meaning unlimited, or no additional clamping
|
||||
// beyond the codec's built-in algorithm.
|
||||
// For example, to allocate no more than 4.5 frames worth of bitrate to a keyframe, set this to 450.
|
||||
vpx_codec_control(encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, 0);
|
||||
vpx_codec_control(&context->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, 0);
|
||||
}
|
||||
|
||||
if (decoding) {
|
||||
if (context->flags & SWITCH_CODEC_FLAG_DECODE) {
|
||||
vp8_postproc_cfg_t ppcfg;
|
||||
|
||||
if (context->decoder_init) {
|
||||
vpx_codec_destroy(&context->decoder);
|
||||
context->decoder_init = 0;
|
||||
}
|
||||
|
||||
if (vpx_codec_dec_init(&context->decoder, decoder_interface, NULL, VPX_CODEC_USE_POSTPROC) != VPX_CODEC_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec init error: [%d:%s]\n", encoder->err, encoder->err_detail);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec init error: [%d:%s]\n", context->encoder.err, context->encoder.err_detail);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
context->decoder_init = 1;
|
||||
|
||||
// the types of post processing to be done, should be combination of "vp8_postproc_level"
|
||||
ppcfg.post_proc_flag = VP8_DEMACROBLOCK | VP8_DEBLOCK;
|
||||
// the strength of deblocking, valid range [0, 16]
|
||||
ppcfg.deblocking_level = 3;
|
||||
// Set deblocking settings
|
||||
vpx_codec_control(decoder, VP8_SET_POSTPROC, &ppcfg);
|
||||
vpx_codec_control(&context->decoder, VP8_SET_POSTPROC, &ppcfg);
|
||||
|
||||
switch_buffer_create_dynamic(&context->vpx_packet_buffer, 512, 512, 1024000);
|
||||
}
|
||||
@@ -217,6 +203,39 @@ static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
|
||||
{
|
||||
vpx_context_t *context = NULL;
|
||||
int encoding, decoding;
|
||||
|
||||
|
||||
encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
|
||||
decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
|
||||
|
||||
if (!(encoding || decoding) || ((context = switch_core_alloc(codec->memory_pool, sizeof(*context))) == 0)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
memset(context, 0, sizeof(*context));
|
||||
context->flags = flags;
|
||||
codec->private_info = context;
|
||||
|
||||
if (codec_settings) {
|
||||
context->codec_settings = *codec_settings;
|
||||
}
|
||||
|
||||
if (codec->fmtp_in) {
|
||||
codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
|
||||
}
|
||||
|
||||
if (vpx_codec_enc_config_default(encoder_interface, &context->config, 0) != VPX_CODEC_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder config Error\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* http://tools.ietf.org/html/draft-ietf-payload-vp8-10
|
||||
|
||||
The first octets after the RTP header are the VP8 payload descriptor, with the following structure.
|
||||
@@ -313,8 +332,8 @@ static switch_status_t consume_partition(vpx_context_t *context, void *data, uin
|
||||
}
|
||||
|
||||
static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_image_t *img,
|
||||
void *encoded_data, uint32_t *encoded_data_len,
|
||||
unsigned int *flag)
|
||||
void *encoded_data, uint32_t *encoded_data_len,
|
||||
unsigned int *flag)
|
||||
{
|
||||
vpx_context_t *context = (vpx_context_t *)codec->private_info;
|
||||
uint32_t duration = 90000 / FPS;
|
||||
@@ -332,6 +351,8 @@ static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_image_t *
|
||||
|
||||
//d_w and d_h are messed up
|
||||
|
||||
//printf("WTF %d %d\n", img->d_w, img->d_h);
|
||||
|
||||
width = img->w;
|
||||
height = img->h;
|
||||
|
||||
@@ -339,28 +360,31 @@ static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_image_t *
|
||||
//switch_assert(height > 0 && (height % 4 == 0));
|
||||
|
||||
if (context->config.g_w != width || context->config.g_h != height) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VPX reset encoder picture from %dx%d to %dx%d\n", context->config.g_w, context->config.g_h, width, height);
|
||||
context->config.g_w = width;
|
||||
context->config.g_h = height;
|
||||
if (vpx_codec_enc_config_set(&context->encoder, &context->config) != VPX_CODEC_OK) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "VPX reset config error!");
|
||||
context->codec_settings.video.width = width;
|
||||
context->codec_settings.video.height = height;
|
||||
if (context->codec_settings.video.bandwidth) {
|
||||
context->bandwidth = context->codec_settings.video.bandwidth;
|
||||
} else {
|
||||
context->bandwidth = width * height * 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->last_ts == 0) context->last_ts = switch_micro_time_now();
|
||||
if (context->bandwidth > 1250000) {
|
||||
context->bandwidth = 1250000;
|
||||
}
|
||||
|
||||
if ((switch_micro_time_now() - context->last_ts) > 2 * 1000000) {
|
||||
// the config params doesn't seems work for generate regular key frames,
|
||||
// so we do some trick here to force a key frame every 2 sec
|
||||
// vpx_flags = VPX_EFLAG_FORCE_KF;
|
||||
context->last_ts = switch_micro_time_now();
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_NOTICE,
|
||||
"VPX reset encoder picture from %dx%d to %dx%d %u BW\n",
|
||||
context->config.g_w, context->config.g_h, width, height, context->bandwidth);
|
||||
|
||||
init_codec(codec);
|
||||
*flag |= SFF_PICTURE_RESET;
|
||||
context->need_key_frame = 1;
|
||||
}
|
||||
|
||||
if (context->need_key_frame > 0) {
|
||||
// force generate a key frame
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VPX KEYFRAME REQ\n");
|
||||
vpx_flags |= VPX_EFLAG_FORCE_KF;
|
||||
context->last_ts = switch_micro_time_now();
|
||||
context->need_key_frame--;
|
||||
}
|
||||
|
||||
@@ -539,7 +563,7 @@ static switch_status_t switch_vpx_destroy(switch_codec_t *codec)
|
||||
|
||||
if (context) {
|
||||
if ((codec->flags & SWITCH_CODEC_FLAG_ENCODE)) {
|
||||
vpx_codec_destroy(&context->encoder); // TODO fix crash
|
||||
vpx_codec_destroy(&context->encoder);
|
||||
}
|
||||
|
||||
if ((codec->flags & SWITCH_CODEC_FLAG_DECODE)) {
|
||||
|
||||
@@ -102,6 +102,7 @@ struct vlc_video_context {
|
||||
switch_mutex_t *video_mutex;
|
||||
|
||||
switch_core_session_t *session;
|
||||
switch_channel_t *channel;
|
||||
switch_frame_t *aud_frame;
|
||||
switch_frame_t *vid_frame;
|
||||
uint8_t video_packet[1500 + 12];
|
||||
@@ -230,20 +231,11 @@ static void *vlc_video_lock_callback(void *data, void **p_pixels)
|
||||
return NULL; /* picture identifier, not needed here */
|
||||
}
|
||||
|
||||
/* dummy callback so it should be good when no video on channel */
|
||||
static void vlc_video_unlock_dummy_callback(void *data, void *id, void *const *p_pixels)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *)data;
|
||||
assert(id == NULL); /* picture identifier, not needed here */
|
||||
switch_mutex_unlock(context->video_mutex);
|
||||
}
|
||||
|
||||
static void vlc_video_unlock_callback(void *data, void *id, void *const *p_pixels)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *) data;
|
||||
switch_frame_t *frame = context->vid_frame;
|
||||
|
||||
switch_assert(id == NULL); /* picture identifier, not needed here */
|
||||
if (context->channel && !switch_channel_test_flag(context->channel, CF_VIDEO)) return;
|
||||
|
||||
if (!context->img) context->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, context->width, context->height, 0);
|
||||
|
||||
@@ -251,14 +243,11 @@ static void vlc_video_unlock_callback(void *data, void *id, void *const *p_pixel
|
||||
|
||||
yuyv_to_i420(*p_pixels, context->img->img_data, context->width, context->height);
|
||||
|
||||
switch_core_session_write_video_image(context->session, frame, context->img, SWITCH_DEFAULT_VIDEO_SIZE, NULL);
|
||||
|
||||
switch_mutex_unlock(context->video_mutex);
|
||||
}
|
||||
|
||||
static void do_buffer_frame(vlc_video_context_t *context)
|
||||
static void do_buffer_frame(vlc_video_context_t *context, switch_frame_t *frame)
|
||||
{
|
||||
switch_frame_t *frame = context->vid_frame;
|
||||
uint32_t size = sizeof(*frame) + frame->packetlen;
|
||||
|
||||
switch_mutex_lock(context->video_mutex);
|
||||
@@ -277,32 +266,35 @@ static void do_buffer_frame(vlc_video_context_t *context)
|
||||
static void vlc_video_channel_unlock_callback(void *data, void *id, void *const *p_pixels)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *)data;
|
||||
uint32_t flag = 0;
|
||||
switch_frame_t *frame = context->vid_frame;
|
||||
|
||||
|
||||
switch_assert(id == NULL); /* picture identifier, not needed here */
|
||||
|
||||
if (context->channel && !switch_channel_test_flag(context->channel, CF_VIDEO)) return;
|
||||
|
||||
if (!context->img) context->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, context->width, context->height, 0);
|
||||
switch_assert(context->img);
|
||||
|
||||
yuyv_to_i420(*p_pixels, context->img->img_data, context->width, context->height);
|
||||
|
||||
switch_mutex_unlock(context->video_mutex);
|
||||
}
|
||||
|
||||
static void vlc_video_display_callback(void *data, void *id)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *) data;
|
||||
int32_t flag = 0;
|
||||
|
||||
/* VLC wants to display the video */
|
||||
|
||||
if (context->channel && !switch_channel_test_flag(context->channel, CF_VIDEO)) return;
|
||||
|
||||
if (context->video_refresh_req > 0) {
|
||||
flag |= SFF_WAIT_KEY_FRAME;
|
||||
context->video_refresh_req--;
|
||||
}
|
||||
|
||||
switch_core_session_write_video_image(context->session, frame, context->img, SWITCH_DEFAULT_VIDEO_SIZE, &flag);
|
||||
|
||||
switch_mutex_unlock(context->video_mutex);
|
||||
}
|
||||
|
||||
static void vlc_video_display_callback(void *data, void *id)
|
||||
{
|
||||
/* VLC wants to display the video */
|
||||
(void) data;
|
||||
assert(id == NULL);
|
||||
switch_core_session_write_video_image(context->session, context->vid_frame, context->img, SWITCH_DEFAULT_VIDEO_SIZE, NULL);
|
||||
}
|
||||
|
||||
unsigned video_format_setup_callback(void **opaque, char *chroma, unsigned *width, unsigned *height, unsigned *pitches, unsigned *lines)
|
||||
@@ -723,7 +715,9 @@ SWITCH_STANDARD_APP(play_video_function)
|
||||
|
||||
audio_datalen = read_impl.decoded_bytes_per_packet; //codec.implementation->actual_samples_per_second / 1000 * (read_impl.microseconds_per_packet / 1000);
|
||||
|
||||
|
||||
context->session = session;
|
||||
context->channel = channel;
|
||||
context->pool = pool;
|
||||
context->aud_frame = &audio_frame;
|
||||
context->vid_frame = &video_frame;
|
||||
@@ -770,14 +764,8 @@ SWITCH_STANDARD_APP(play_video_function)
|
||||
libvlc_audio_set_format(context->mp, "S16N", read_impl.actual_samples_per_second, read_impl.number_of_channels);
|
||||
libvlc_audio_set_callbacks(context->mp, vlc_play_audio_callback, NULL,NULL,NULL,NULL, (void *) context);
|
||||
|
||||
if (switch_channel_test_flag(channel, CF_VIDEO)) {
|
||||
// libvlc_video_set_format(context->mp, "YUYV", VIDEOWIDTH, VIDEOHEIGHT, VIDEOWIDTH * 2);
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_unlock_callback, vlc_video_display_callback, context);
|
||||
} else {
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_unlock_dummy_callback, vlc_video_display_callback, context);
|
||||
}
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_unlock_callback, vlc_video_display_callback, context);
|
||||
|
||||
// start play
|
||||
if (-1 == libvlc_media_player_play(context->mp)) {
|
||||
@@ -928,6 +916,15 @@ switch_io_routines_t vlc_io_routines = {
|
||||
/*state_run*/ NULL
|
||||
};
|
||||
|
||||
static switch_status_t vlc_channel_img_callback(switch_core_session_t *session, switch_frame_t *frame, switch_image_t *img, void *user_data)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *) user_data;
|
||||
|
||||
do_buffer_frame(context, frame);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status_t setup_tech_pvt(switch_core_session_t *session, const char *path)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
@@ -952,11 +949,12 @@ static switch_status_t setup_tech_pvt(switch_core_session_t *session, const char
|
||||
memset(context, 0, sizeof(vlc_file_context_t));
|
||||
tech_pvt->context = context;
|
||||
|
||||
|
||||
switch_buffer_create_dynamic(&(context->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 8, 0);
|
||||
|
||||
if (switch_channel_test_flag(channel, CF_VIDEO)) {
|
||||
switch_buffer_create_dynamic(&(context->video_buffer), VLC_BUFFER_SIZE * 2, VLC_BUFFER_SIZE * 16, 0);
|
||||
}
|
||||
switch_buffer_create_dynamic(&(context->video_buffer), VLC_BUFFER_SIZE * 2, VLC_BUFFER_SIZE * 16, 0);
|
||||
|
||||
switch_core_session_set_image_write_callback(session, vlc_channel_img_callback, tech_pvt->context);
|
||||
|
||||
if (switch_core_timer_init(&tech_pvt->timer, "soft", 20,
|
||||
8000 / (1000 / 20), pool) != SWITCH_STATUS_SUCCESS) {
|
||||
@@ -969,6 +967,9 @@ static switch_status_t setup_tech_pvt(switch_core_session_t *session, const char
|
||||
context->pool = pool;
|
||||
context->aud_frame = &tech_pvt->read_frame;
|
||||
context->vid_frame = &tech_pvt->read_video_frame;
|
||||
context->vid_frame->packet = context->video_packet;
|
||||
context->vid_frame->data = context->video_packet + 12;
|
||||
|
||||
context->playing = 0;
|
||||
// context->err = 0;
|
||||
|
||||
@@ -1010,14 +1011,10 @@ static switch_status_t setup_tech_pvt(switch_core_session_t *session, const char
|
||||
libvlc_audio_set_format(context->mp, "S16N", 8000, 1);
|
||||
libvlc_audio_set_callbacks(context->mp, vlc_play_audio_callback, NULL,NULL,NULL,NULL, (void *) context);
|
||||
|
||||
if (switch_channel_test_flag(channel, CF_VIDEO)) {
|
||||
// libvlc_video_set_format(context->mp, "YUYV", VIDEOWIDTH, VIDEOHEIGHT, VIDEOWIDTH * 2);
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_channel_unlock_callback, vlc_video_display_callback, context);
|
||||
} else {
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_unlock_dummy_callback, vlc_video_display_callback, context);
|
||||
}
|
||||
|
||||
libvlc_video_set_format_callbacks(context->mp, video_format_setup_callback, video_format_clean_callback);
|
||||
libvlc_video_set_callbacks(context->mp, vlc_video_lock_callback, vlc_video_channel_unlock_callback, vlc_video_display_callback, context);
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
@@ -1026,25 +1023,13 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static switch_status_t vlc_channel_img_callback(switch_core_session_t *session, switch_frame_t *frame, switch_image_t *img, void *user_data)
|
||||
{
|
||||
vlc_video_context_t *context = (vlc_video_context_t *) user_data;
|
||||
|
||||
do_buffer_frame(context);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t channel_on_init(switch_core_session_t *session)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
vlc_private_t *tech_pvt = switch_core_session_get_private(session);
|
||||
//vlc_private_t *tech_pvt = switch_core_session_get_private(session);
|
||||
|
||||
switch_channel_set_state(channel, CS_CONSUME_MEDIA);
|
||||
|
||||
switch_core_session_set_image_write_callback(session, vlc_channel_img_callback, tech_pvt->context);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1096,6 +1081,10 @@ static switch_status_t channel_on_destroy(switch_core_session_t *session)
|
||||
switch_buffer_destroy(&tech_pvt->context->audio_buffer);
|
||||
}
|
||||
|
||||
if (tech_pvt->context->video_buffer) {
|
||||
switch_buffer_destroy(&tech_pvt->context->video_buffer);
|
||||
}
|
||||
|
||||
if (tech_pvt->timer.interval) {
|
||||
switch_core_timer_destroy(&tech_pvt->timer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user