Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch
Commits
4af7e7c6
提交
4af7e7c6
authored
7月 24, 2014
作者:
Seven Du
提交者:
Michael Jerris
5月 28, 2015
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
FS-7514: vlc video
上级
90818f13
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
1195 行增加
和
31 行删除
+1195
-31
mod_vlc.c
src/mod/formats/mod_vlc/mod_vlc.c
+1195
-31
没有找到文件。
src/mod/formats/mod_vlc/mod_vlc.c
浏览文件 @
4af7e7c6
/*
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
...
...
@@ -22,8 +22,9 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
*
* William King william.king@quentustech.com
* Seven Du <dujinfang@gmail.com>
*
* mod_vlc.c -- VLC streaming module
*
...
...
@@ -37,9 +38,13 @@
* To stream from a call(channel) out to a remote destination:
* vlc://#transcode{acodec=vorb,channels=1,samplerate=16000}:standard{access=http,mux=ogg,dst=:8080/thing.ogg}
*
* To playback a video file
* play_video /tmp/test.mp4
*
* Notes:
*
* Requires at least libvlc version 1.2
* Audio Requires at least libvlc version 1.2
* Video Requires at least libvlc 2.0.2
*
*/
#include <switch.h>
...
...
@@ -56,10 +61,12 @@ typedef int (*imem_get_t)(void *data, const char *cookie,
size_t
*
,
void
**
);
typedef
void
(
*
imem_release_t
)(
void
*
data
,
const
char
*
cookie
,
size_t
,
void
*
);
/* Change valud to -vvv for vlc related debug. Be careful since vlc is at least as verbose as FS about logging */
const
char
*
vlc_args
=
""
;
/* Change value to -vvv for vlc related debug. Be careful since vlc is at least as verbose as FS about logging */
const
char
*
vlc_args
[]
=
{
""
};
// const char *vlc_args[] = {"--network-caching", "500"};
libvlc_instance_t
*
read_inst
;
switch_endpoint_interface_t
*
vlc_endpoint_interface
=
NULL
;
struct
vlc_file_context
{
libvlc_media_player_t
*
mp
;
...
...
@@ -81,10 +88,92 @@ struct vlc_file_context {
typedef
struct
vlc_file_context
vlc_file_context_t
;
struct
vlc_video_context
{
libvlc_media_player_t
*
mp
;
libvlc_media_t
*
m
;
switch_mutex_t
*
audio_mutex
;
switch_file_handle_t
fh
;
switch_memory_pool_t
*
pool
;
switch_thread_cond_t
*
started
;
switch_buffer_t
*
audio_buffer
;
switch_buffer_t
*
video_buffer
;
int
playing
;
switch_mutex_t
*
video_mutex
;
switch_core_session_t
*
session
;
switch_frame_t
*
aud_frame
;
switch_frame_t
*
vid_frame
;
uint8_t
video_packet
[
1500
+
12
];
void
*
raw_yuyv_data
;
void
*
raw_i420_data
;
uint32_t
last_video_ts
;
switch_payload_t
pt
;
uint32_t
seq
;
int
width
;
int
height
;
int
force_width
;
int
force_height
;
int
new_frame
;
// next frame is a new frame(new timestamp)
};
typedef
struct
vlc_video_context
vlc_video_context_t
;
SWITCH_MODULE_SHUTDOWN_FUNCTION
(
mod_vlc_shutdown
);
SWITCH_MODULE_LOAD_FUNCTION
(
mod_vlc_load
);
SWITCH_MODULE_DEFINITION
(
mod_vlc
,
mod_vlc_load
,
mod_vlc_shutdown
,
NULL
);
void
yuyv_to_i420
(
uint8_t
*
pixels
,
void
*
out_buffer
,
int
src_width
,
int
src_height
)
{
uint8_t
*
Y
,
*
U
,
*
V
;
int
h
,
w
;
Y
=
out_buffer
;
U
=
Y
+
src_width
*
src_height
;
V
=
U
+
(
src_width
*
src_height
>>
2
);
if
(
0
)
{
//split
uint8_t
*
p
,
*
q
,
*
k
;
p
=
pixels
;
q
=
pixels
;
k
=
pixels
;
for
(
h
=
0
;
h
<
src_height
/
2
;
++
h
)
{
for
(
w
=
0
;
w
<
src_width
;
w
+=
4
)
{
p
[
0
]
=
q
[
0
];
p
[
1
]
=
q
[
1
];
p
[
2
]
=
q
[
4
];
p
[
3
]
=
q
[
3
];
p
+=
4
;
q
+=
8
;
}
memcpy
(
p
,
k
,
src_width
);
k
+=
src_width
*
2
;
p
+=
src_width
;
q
+=
src_width
*
2
;
}
memcpy
(
k
,
pixels
,
src_width
*
src_height
);
}
for
(
h
=
0
;
h
<
src_height
;
++
h
)
{
for
(
w
=
0
;
w
<
src_width
;
++
w
)
{
Y
[
w
]
=
pixels
[
2
*
w
];
if
(
w
%
2
==
0
&&
h
%
2
==
0
)
{
U
[
w
/
2
]
=
pixels
[
2
*
w
+
1
];
V
[
w
/
2
]
=
pixels
[
2
*
w
+
3
];
}
}
pixels
=
pixels
+
src_width
*
2
;
Y
=
Y
+
src_width
;
if
(
h
%
2
==
0
)
{
U
=
U
+
(
src_width
>>
1
);
V
=
V
+
(
src_width
>>
1
);
}
}
}
static
void
vlc_mediaplayer_error_callback
(
const
libvlc_event_t
*
event
,
void
*
data
)
{
vlc_file_context_t
*
context
=
(
vlc_file_context_t
*
)
data
;
...
...
@@ -111,7 +200,7 @@ static void vlc_media_state_callback(const libvlc_event_t * event, void * data)
void
vlc_auto_play_callback
(
void
*
data
,
const
void
*
samples
,
unsigned
count
,
int64_t
pts
)
{
vlc_file_context_t
*
context
=
(
vlc_file_context_t
*
)
data
;
switch_mutex_lock
(
context
->
audio_mutex
);
if
(
context
->
audio_buffer
)
{
if
(
!
switch_buffer_write
(
context
->
audio_buffer
,
samples
,
count
*
2
*
context
->
channels
))
{
...
...
@@ -119,13 +208,276 @@ void vlc_auto_play_callback(void *data, const void *samples, unsigned count, int
}
}
if
(
!
context
->
playing
)
{
if
(
!
context
->
playing
)
{
context
->
playing
=
1
;
switch_thread_cond_signal
(
context
->
started
);
}
}
switch_mutex_unlock
(
context
->
audio_mutex
);
}
#define FORCE_SPLIT 1
void
vlc_play_audio_callback
(
void
*
data
,
const
void
*
samples
,
unsigned
count
,
int64_t
pts
)
{
vlc_video_context_t
*
context
=
(
vlc_video_context_t
*
)
data
;
switch_size_t
bytes
;
switch_mutex_lock
(
context
->
audio_mutex
);
bytes
=
switch_buffer_inuse
(
context
->
audio_buffer
);
if
(
bytes
>
VLC_BUFFER_SIZE
*
4
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"Buffer overflow %d
\n
"
,
(
int
)
bytes
);
switch_buffer_toss
(
context
->
audio_buffer
,
bytes
-
VLC_BUFFER_SIZE
);
}
switch_buffer_write
(
context
->
audio_buffer
,
samples
,
count
*
2
);
if
(
!
context
->
playing
)
{
context
->
playing
=
1
;
if
(
context
->
started
)
switch_thread_cond_signal
(
context
->
started
);
}
switch_mutex_unlock
(
context
->
audio_mutex
);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "VLC callback, play audio: %d \n", count);
}
static
void
*
vlc_video_lock_callback
(
void
*
data
,
void
**
p_pixels
)
{
vlc_video_context_t
*
context
=
(
vlc_video_context_t
*
)
data
;
switch_mutex_lock
(
context
->
video_mutex
);
if
(
!
context
->
raw_yuyv_data
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"no yuyv data
\n
"
);
return
NULL
;
}
*
p_pixels
=
context
->
raw_yuyv_data
;
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
;
uint32_t
decoded_data_len
;
uint32_t
flag
=
0
;
uint32_t
encoded_data_len
=
1500
;
uint32_t
encoded_rate
=
0
;
switch_codec_t
*
codec
=
switch_core_session_get_video_write_codec
(
context
->
session
);
switch_assert
(
id
==
NULL
);
/* picture identifier, not needed here */
switch_assert
(
codec
);
yuyv_to_i420
(
*
p_pixels
,
context
->
raw_i420_data
,
context
->
width
,
context
->
height
);
codec
->
enc_picture
.
width
=
context
->
width
;
codec
->
enc_picture
.
height
=
context
->
height
;
decoded_data_len
=
context
->
width
*
context
->
height
*
3
/
2
;
switch_core_codec_encode
(
codec
,
NULL
,
context
->
raw_i420_data
,
decoded_data_len
,
0
,
frame
->
data
,
&
encoded_data_len
,
&
encoded_rate
,
&
flag
);
while
(
encoded_data_len
)
{
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "encoded: %s [%d] flag=%d ts=%u\n", codec->implementation->iananame, encoded_data_len, flag, context->last_video_ts);
frame
->
datalen
=
encoded_data_len
;
frame
->
packetlen
=
frame
->
datalen
+
12
;
frame
->
m
=
flag
&
SFF_MARKER
?
1
:
0
;
// frame->timestamp = context->ts;
// if (frame->m) context->ts += 90000 / FPS;
if
(
!
context
->
last_video_ts
)
{
context
->
last_video_ts
=
switch_micro_time_now
()
/
1000
;
frame
->
timestamp
=
context
->
last_video_ts
;
}
if
(
context
->
new_frame
)
{
int
delta
=
switch_micro_time_now
()
/
1000
-
context
->
last_video_ts
;
frame
->
timestamp
+=
delta
*
90
;
context
->
last_video_ts
=
switch_micro_time_now
()
/
1000
;
context
->
new_frame
=
0
;
}
if
(
frame
->
m
)
{
// next frame is a new frame
context
->
new_frame
=
1
;
}
if
(
1
)
{
/* set correct mark and ts */
switch_rtp_hdr_t
*
rtp
=
(
switch_rtp_hdr_t
*
)
frame
->
packet
;
memset
(
rtp
,
0
,
12
);
rtp
->
version
=
2
;
rtp
->
m
=
frame
->
m
;
rtp
->
ts
=
htonl
(
frame
->
timestamp
);
rtp
->
ssrc
=
(
uint32_t
)
((
intptr_t
)
rtp
+
(
uint32_t
)
switch_epoch_time_now
(
NULL
));
switch_set_flag
(
frame
,
SFF_RAW_RTP
);
}
switch_set_flag
(
frame
,
SFF_RAW_RTP
);
switch_set_flag
(
frame
,
SFF_PROXY_PACKET
);
switch_core_session_write_video_frame
(
context
->
session
,
frame
,
SWITCH_IO_FLAG_NONE
,
0
);
encoded_data_len
=
1500
;
switch_core_codec_encode
(
codec
,
NULL
,
NULL
,
0
,
0
,
frame
->
data
,
&
encoded_data_len
,
&
encoded_rate
,
&
flag
);
}
switch_mutex_unlock
(
context
->
video_mutex
);
}
static
void
do_buffer_frame
(
vlc_video_context_t
*
context
)
{
switch_frame_t
*
frame
=
context
->
vid_frame
;
uint32_t
size
=
sizeof
(
*
frame
)
+
frame
->
packetlen
;
switch_mutex_lock
(
context
->
video_mutex
);
if
(
switch_buffer_inuse
(
context
->
video_buffer
)
>
VLC_BUFFER_SIZE
*
10
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"buffer overflow %d
\n
"
,
(
int
)
switch_buffer_inuse
(
context
->
video_buffer
));
switch_buffer_zero
(
context
->
video_buffer
);
}
switch_buffer_write
(
context
->
video_buffer
,
&
size
,
sizeof
(
uint32_t
));
switch_buffer_write
(
context
->
video_buffer
,
frame
,
sizeof
(
*
frame
));
switch_buffer_write
(
context
->
video_buffer
,
frame
->
packet
,
frame
->
packetlen
);
switch_mutex_unlock
(
context
->
video_mutex
);
}
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
decoded_data_len
;
uint32_t
flag
=
0
;
uint32_t
encoded_data_len
=
1500
;
uint32_t
encoded_rate
=
0
;
switch_codec_t
*
codec
=
switch_core_session_get_video_write_codec
(
context
->
session
);
switch_frame_t
*
frame
=
context
->
vid_frame
;
switch_assert
(
id
==
NULL
);
/* picture identifier, not needed here */
switch_assert
(
codec
);
yuyv_to_i420
(
*
p_pixels
,
context
->
raw_i420_data
,
context
->
width
,
context
->
height
);
codec
->
enc_picture
.
width
=
context
->
width
;
codec
->
enc_picture
.
height
=
context
->
height
;
encoded_data_len
=
1500
;
decoded_data_len
=
context
->
width
*
context
->
height
*
3
/
2
;
frame
->
packet
=
context
->
video_packet
;
frame
->
data
=
context
->
video_packet
+
12
;
switch_core_codec_encode
(
codec
,
NULL
,
context
->
raw_i420_data
,
decoded_data_len
,
0
,
frame
->
data
,
&
encoded_data_len
,
&
encoded_rate
,
&
flag
);
while
(
encoded_data_len
)
{
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "encoded: %s [%d] flag=%d ts=%u\n", codec->implementation->iananame, encoded_data_len, flag, context->ts);
frame
->
datalen
=
encoded_data_len
;
frame
->
packetlen
=
frame
->
datalen
+
12
;
frame
->
m
=
flag
&
SFF_MARKER
?
1
:
0
;
if
(
!
context
->
last_video_ts
)
{
context
->
last_video_ts
=
switch_micro_time_now
()
/
1000
;
frame
->
timestamp
=
context
->
last_video_ts
;
}
if
(
context
->
new_frame
)
{
int
delta
=
switch_micro_time_now
()
/
1000
-
context
->
last_video_ts
;
frame
->
timestamp
+=
delta
*
90
;
context
->
last_video_ts
=
switch_micro_time_now
()
/
1000
;
context
->
new_frame
=
0
;
}
if
(
frame
->
m
)
{
// next frame is a new frame
context
->
new_frame
=
1
;
}
if
(
1
)
{
/* set correct mark and ts */
switch_rtp_hdr_t
*
rtp
=
(
switch_rtp_hdr_t
*
)
frame
->
packet
;
memset
(
rtp
,
0
,
12
);
rtp
->
version
=
2
;
rtp
->
m
=
frame
->
m
;
rtp
->
ts
=
htonl
(
frame
->
timestamp
);
rtp
->
ssrc
=
(
uint32_t
)
((
intptr_t
)
rtp
+
(
uint32_t
)
switch_epoch_time_now
(
NULL
));
switch_set_flag
(
frame
,
SFF_RAW_RTP
);
}
switch_set_flag
(
frame
,
SFF_RAW_RTP
);
switch_set_flag
(
frame
,
SFF_PROXY_PACKET
);
do_buffer_frame
(
context
);
encoded_data_len
=
1500
;
switch_core_codec_encode
(
codec
,
NULL
,
NULL
,
0
,
0
,
frame
->
data
,
&
encoded_data_len
,
&
encoded_rate
,
&
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
);
}
unsigned
video_format_setup_callback
(
void
**
opaque
,
char
*
chroma
,
unsigned
*
width
,
unsigned
*
height
,
unsigned
*
pitches
,
unsigned
*
lines
)
{
vlc_video_context_t
*
context
=
(
vlc_video_context_t
*
)
(
*
opaque
);
unsigned
frame_size
;
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"chroma: %s, width: %u, height: %u, pitches: %u, lines: %u
\n
"
,
chroma
,
*
width
,
*
height
,
*
pitches
,
*
lines
);
/* You have to use YUYV here or it will crash */
switch_set_string
(
chroma
,
"YUYV"
);
if
(
context
->
force_width
&&
context
->
force_height
)
{
/* resize */
*
width
=
context
->
force_width
;
*
height
=
context
->
force_height
;
}
*
pitches
=
(
*
width
)
*
2
;
*
lines
=
(
*
height
);
context
->
width
=
*
width
;
context
->
height
=
*
height
;
frame_size
=
(
*
width
)
*
(
*
height
)
*
4
*
2
;
context
->
raw_yuyv_data
=
malloc
(
frame_size
);
if
(
context
->
raw_yuyv_data
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"memory error
\n
"
);
return
0
;
}
context
->
raw_i420_data
=
malloc
(
frame_size
);
if
(
context
->
raw_i420_data
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"memory error
\n
"
);
free
(
context
->
raw_yuyv_data
);
return
0
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"chroma: %s, width: %u, height: %u, pitches: %u, lines: %u
\n
"
,
chroma
,
*
width
,
*
height
,
*
pitches
,
*
lines
);
return
1
;
}
void
video_format_clean_callback
(
void
*
opaque
)
{
vlc_video_context_t
*
context
=
(
vlc_video_context_t
*
)
opaque
;
switch_safe_free
(
context
->
raw_yuyv_data
);
switch_safe_free
(
context
->
raw_i420_data
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"cleanup
\n
"
);
}
int
vlc_imem_get_callback
(
void
*
data
,
const
char
*
cookie
,
int64_t
*
dts
,
int64_t
*
pts
,
unsigned
*
flags
,
size_t
*
size
,
void
**
output
)
{
vlc_file_context_t
*
context
=
(
vlc_file_context_t
*
)
data
;
...
...
@@ -182,14 +534,17 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p
switch_thread_cond_create
(
&
(
context
->
started
),
context
->
pool
);
if
(
switch_test_flag
(
handle
,
SWITCH_FILE_FLAG_READ
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"VLC open %s for reading
\n
"
,
path
);
/* Determine if this is a url or a path */
/* TODO: Change this so that it tries local files first, and then if it fails try location. */
if
(
!
strncmp
(
context
->
path
,
"http"
,
4
)){
context
->
m
=
libvlc_media_new_location
(
read_inst
,
context
->
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is http %s
\n
"
,
context
->
path
);
}
else
if
(
!
strncmp
(
context
->
path
,
"rtp"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
context
->
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is rtp %s
\n
"
,
context
->
path
);
}
else
if
(
!
strncmp
(
context
->
path
,
"mms"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
context
->
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is mms %s
\n
"
,
context
->
path
);
...
...
@@ -205,12 +560,12 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC error opening %s for reading
\n
"
,
path
);
return
SWITCH_STATUS_GENERR
;
}
context
->
playing
=
0
;
context
->
err
=
0
;
context
->
mp
=
libvlc_media_player_new_from_media
(
context
->
m
);
if
(
!
handle
->
samplerate
)
{
handle
->
samplerate
=
16000
;
}
...
...
@@ -229,10 +584,10 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p
libvlc_audio_set_callbacks
(
context
->
mp
,
vlc_auto_play_callback
,
NULL
,
NULL
,
NULL
,
NULL
,
(
void
*
)
context
);
libvlc_media_player_play
(
context
->
mp
);
}
else
if
(
switch_test_flag
(
handle
,
SWITCH_FILE_FLAG_WRITE
))
{
const
char
*
opts
[
10
]
=
{
vlc_args
,
*
vlc_args
,
switch_mprintf
(
"--sout=%s"
,
path
)
};
int
opts_count
=
10
;
...
...
@@ -241,6 +596,7 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p
handle
->
samplerate
=
16000
;
context
->
samplerate
=
handle
->
samplerate
;
context
->
channels
=
handle
->
channels
;
opts
[
2
]
=
switch_mprintf
(
"--imem-get=%ld"
,
vlc_imem_get_callback
);
opts
[
3
]
=
switch_mprintf
(
"--imem-release=%ld"
,
vlc_imem_release_callback
);
...
...
@@ -270,7 +626,7 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p
}
handle
->
private_info
=
context
;
return
SWITCH_STATUS_SUCCESS
;
}
...
...
@@ -279,7 +635,7 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s
vlc_file_context_t
*
context
=
handle
->
private_info
;
size_t
bytes
=
*
len
*
sizeof
(
int16_t
)
*
handle
->
channels
,
read
;
libvlc_state_t
status
;
if
(
!
context
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC read handle context is NULL
\n
"
);
return
SWITCH_STATUS_GENERR
;
...
...
@@ -308,12 +664,12 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s
}
switch_mutex_unlock
(
context
->
audio_mutex
);
switch_mutex_lock
(
context
->
audio_mutex
);
read
=
switch_buffer_read
(
context
->
audio_buffer
,
data
,
bytes
);
switch_mutex_unlock
(
context
->
audio_mutex
);
status
=
libvlc_media_get_state
(
context
->
m
);
status
=
libvlc_media_get_state
(
context
->
m
);
if
(
!
read
&&
(
status
==
libvlc_Stopped
||
status
==
libvlc_Ended
||
status
==
libvlc_Error
))
{
return
SWITCH_STATUS_FALSE
;
...
...
@@ -321,18 +677,17 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s
read
=
2
;
memset
(
data
,
0
,
read
);
}
if
(
read
)
{
*
len
=
read
/
2
/
handle
->
channels
;
}
if
(
read
)
*
len
=
read
/
2
;
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
vlc_file_write
(
switch_file_handle_t
*
handle
,
void
*
data
,
size_t
*
len
)
{
vlc_file_context_t
*
context
=
handle
->
private_info
;
size_t
bytes
=
*
len
*
sizeof
(
int16_t
)
*
handle
->
channels
;
size_t
bytes
=
*
len
*
sizeof
(
int16_t
);
switch_mutex_lock
(
context
->
audio_mutex
);
context
->
samples
+=
*
len
;
...
...
@@ -372,8 +727,8 @@ static switch_status_t vlc_file_close(switch_file_handle_t *handle)
if
(
context
->
mp
)
libvlc_media_player_stop
(
context
->
mp
);
if
(
context
->
m
)
if
(
context
->
m
)
libvlc_media_release
(
context
->
m
);
if
(
context
->
inst_out
!=
NULL
)
...
...
@@ -382,11 +737,813 @@ static switch_status_t vlc_file_close(switch_file_handle_t *handle)
return
SWITCH_STATUS_SUCCESS
;
}
SWITCH_STANDARD_APP
(
play_video_function
)
{
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_memory_pool_t
*
pool
=
switch_core_session_get_pool
(
session
);
switch_frame_t
audio_frame
=
{
0
},
video_frame
=
{
0
};
switch_codec_t
codec
=
{
0
},
vid_codec
=
{
0
},
*
read_vid_codec
;
switch_timer_t
timer
=
{
0
};
switch_payload_t
pt
=
0
;
switch_dtmf_t
dtmf
=
{
0
};
switch_frame_t
*
read_frame
;
switch_codec_implementation_t
read_impl
=
{
0
};
switch_core_session_message_t
msg
=
{
0
};
vlc_video_context_t
*
context
;
char
*
path
=
(
char
*
)
data
;
const
char
*
tmp
;
switch_size_t
audio_datalen
;
switch_channel_set_flag
(
channel
,
CF_VIDEO_PASSIVE
);
context
=
switch_core_session_alloc
(
session
,
sizeof
(
vlc_video_context_t
));
switch_assert
(
context
);
memset
(
context
,
0
,
sizeof
(
vlc_file_context_t
));
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"vlc_force_width"
)))
{
context
->
force_width
=
atoi
(
tmp
);
}
if
((
tmp
=
switch_channel_get_variable
(
channel
,
"vlc_force_height"
)))
{
context
->
force_height
=
atoi
(
tmp
);
}
switch_buffer_create_dynamic
(
&
(
context
->
audio_buffer
),
VLC_BUFFER_SIZE
,
VLC_BUFFER_SIZE
*
8
,
0
);
/*
if (0) { // SPS PPS
unsigned char outbuf[1024] = { 0 };
unsigned char *in, *out = outbuf;
switch_size_t ilen, olen = 0;
sprintf((char *)out, "packetization-mode=1;profile-level-id=42c01f;sprop-parameter-sets=");
out += strlen((char *)out);
for (int i = 0; i < nal_count; ++i) {
switch (nals[i].i_type) {
case NAL_SPS:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sps %d: %d %d %d %d %x\n", nals[i].i_payload, nals[i].p_payload[0], nals[i].p_payload[1], nals[i].p_payload[2], nals[i].p_payload[3], nals[i].p_payload[4]);
in = nals[i].p_payload + 4; // skipe 00 00 00 01
ilen = nals[i].i_payload - 4;
switch_b64_encode(in, ilen, out, 512);
break;
case NAL_PPS:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pps %d: %d %d %d %d %x\n", nals[i].i_payload, nals[i].p_payload[0], nals[i].p_payload[1], nals[i].p_payload[2], nals[i].p_payload[3], nals[i].p_payload[4]);
in = nals[i].p_payload + 4; // skip 00 00 00 01
ilen = nals[i].i_payload - 4;
olen = (switch_size_t)strlen((char *)out);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d\n", 512);
if (olen > 0) {
out += olen;
*out++ = ',';
}
switch_b64_encode(in, ilen, out, olen);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "props:%s\n", outbuf);
break;
default:
break;
}
}
switch_channel_set_variable(channel, "sip_force_video_fmtp", (char *)outbuf);
}
*/
switch_channel_answer
(
channel
);
switch_core_session_get_read_impl
(
session
,
&
read_impl
);
switch_core_session_receive_message
(
session
,
&
msg
);
if
((
read_vid_codec
=
switch_core_session_get_video_read_codec
(
session
)))
{
pt
=
read_vid_codec
->
agreed_pt
;
}
context
->
pt
=
pt
;
audio_frame
.
codec
=
&
codec
;
video_frame
.
codec
=
read_vid_codec
;
video_frame
.
packet
=
context
->
video_packet
;
video_frame
.
data
=
context
->
video_packet
+
12
;
switch_channel_set_variable
(
channel
,
SWITCH_PLAYBACK_TERMINATOR_USED
,
""
);
if
(
switch_core_timer_init
(
&
timer
,
"soft"
,
read_impl
.
microseconds_per_packet
/
1000
,
read_impl
.
samples_per_packet
,
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Timer Activation Fail
\n
"
);
switch_channel_set_variable
(
channel
,
SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE
,
"Timer activation failed!"
);
goto
end
;
}
if
(
switch_core_codec_init
(
&
codec
,
"L16"
,
NULL
,
read_impl
.
actual_samples_per_second
,
read_impl
.
microseconds_per_packet
/
1000
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Audio Codec Activation Success
\n
"
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Audio Codec Activation Fail
\n
"
);
switch_channel_set_variable
(
channel
,
SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE
,
"Audio codec activation failed"
);
goto
end
;
}
if
(
switch_core_codec_init
(
&
vid_codec
,
"H264"
,
NULL
,
0
,
0
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Video Codec Activation Success
\n
"
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Video Codec Activation Fail
\n
"
);
switch_channel_set_variable
(
channel
,
SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE
,
"Video codec activation failed"
);
goto
end
;
}
audio_datalen
=
codec
.
implementation
->
actual_samples_per_second
/
1000
*
(
read_impl
.
microseconds_per_packet
/
1000
);
context
->
session
=
session
;
context
->
pool
=
pool
;
context
->
aud_frame
=
&
audio_frame
;
context
->
vid_frame
=
&
video_frame
;
context
->
playing
=
0
;
// context->err = 0;
switch_mutex_init
(
&
context
->
audio_mutex
,
SWITCH_MUTEX_NESTED
,
context
->
pool
);
switch_mutex_init
(
&
context
->
video_mutex
,
SWITCH_MUTEX_NESTED
,
context
->
pool
);
switch_thread_cond_create
(
&
(
context
->
started
),
context
->
pool
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"VLC open %s for reading
\n
"
,
path
);
/* Determine if this is a url or a path */
/* TODO: Change this so that it tries local files first, and then if it fails try location. */
if
(
!
strncmp
(
path
,
"http"
,
4
)){
context
->
m
=
libvlc_media_new_location
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is http %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"rtp"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is rtp %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"mms"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is mms %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"rtsp"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is rtsp %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"/"
,
1
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is file %s
\n
"
,
path
);
}
else
{
context
->
m
=
libvlc_media_new_location
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is unknown type %s
\n
"
,
path
);
}
if
(
context
->
m
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC error opening %s for reading
\n
"
,
data
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
return
;
}
context
->
mp
=
libvlc_media_player_new_from_media
(
context
->
m
);
libvlc_audio_set_format
(
context
->
mp
,
"S16N"
,
read_impl
.
actual_samples_per_second
,
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_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
);
}
// start play
if
(
-
1
==
libvlc_media_player_play
(
context
->
mp
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC error playing %s
\n
"
,
path
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
};
// switch_core_service_session_av(session, SWITCH_FALSE, SWITCH_TRUE);
while
(
switch_channel_ready
(
channel
))
{
switch_core_timer_next
(
&
timer
);
switch_core_session_read_frame
(
session
,
&
read_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
if
(
switch_channel_test_flag
(
channel
,
CF_BREAK
))
{
switch_channel_clear_flag
(
channel
,
CF_BREAK
);
break
;
}
switch_ivr_parse_all_events
(
session
);
//check for dtmf interrupts
if
(
switch_channel_has_dtmf
(
channel
))
{
const
char
*
terminators
=
switch_channel_get_variable
(
channel
,
SWITCH_PLAYBACK_TERMINATORS_VARIABLE
);
switch_channel_dequeue_dtmf
(
channel
,
&
dtmf
);
if
(
terminators
&&
!
strcasecmp
(
terminators
,
"none"
))
{
terminators
=
NULL
;
}
if
(
terminators
&&
strchr
(
terminators
,
dtmf
.
digit
))
{
char
sbuf
[
2
]
=
{
dtmf
.
digit
,
'\0'
};
switch_channel_set_variable
(
channel
,
SWITCH_PLAYBACK_TERMINATOR_USED
,
sbuf
);
break
;
}
}
{
libvlc_state_t
status
=
libvlc_media_get_state
(
context
->
m
);
if
(
status
==
libvlc_Ended
||
status
==
libvlc_Error
||
status
==
libvlc_Stopped
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"VLC done. status = %d
\n
"
,
status
);
break
;
}
}
if
(
switch_buffer_inuse
(
context
->
audio_buffer
)
>=
audio_datalen
*
2
)
{
const
void
*
decoded_data
;
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d %d\n", (int)switch_buffer_inuse(context->audio_buffer), (int)audio_datalen * 2);
switch_buffer_peek_zerocopy
(
context
->
audio_buffer
,
&
decoded_data
);
audio_frame
.
data
=
(
void
*
)
decoded_data
;
audio_frame
.
datalen
=
audio_datalen
*
2
;
audio_frame
.
buflen
=
audio_datalen
*
2
;
switch_core_session_write_frame
(
context
->
session
,
&
audio_frame
,
SWITCH_IO_FLAG_NONE
,
0
);
switch_buffer_toss
(
context
->
audio_buffer
,
audio_datalen
*
2
);
}
}
switch_yield
(
50000
);
if
(
context
->
mp
)
libvlc_media_player_stop
(
context
->
mp
);
if
(
context
->
m
)
libvlc_media_release
(
context
->
m
);
context
->
playing
=
0
;
switch_core_thread_session_end
(
session
);
switch_channel_set_variable
(
channel
,
SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE
,
"OK"
);
end
:
if
(
context
->
audio_buffer
)
{
switch_buffer_destroy
(
&
context
->
audio_buffer
);
}
if
(
timer
.
interval
)
{
switch_core_timer_destroy
(
&
timer
);
}
if
(
switch_core_codec_ready
(
&
codec
))
{
switch_core_codec_destroy
(
&
codec
);
}
if
(
switch_core_codec_ready
(
&
vid_codec
))
{
switch_core_codec_destroy
(
&
vid_codec
);
}
switch_channel_clear_flag
(
channel
,
CF_VIDEO_PASSIVE
);
}
static
switch_status_t
channel_on_init
(
switch_core_session_t
*
session
);
static
switch_status_t
channel_on_consume_media
(
switch_core_session_t
*
session
);
static
switch_status_t
channel_on_destroy
(
switch_core_session_t
*
session
);
static
switch_call_cause_t
vlc_outgoing_channel
(
switch_core_session_t
*
session
,
switch_event_t
*
var_event
,
switch_caller_profile_t
*
outbound_profile
,
switch_core_session_t
**
new_session
,
switch_memory_pool_t
**
pool
,
switch_originate_flag_t
flags
,
switch_call_cause_t
*
cancel_cause
);
static
switch_status_t
vlc_read_frame
(
switch_core_session_t
*
session
,
switch_frame_t
**
frame
,
switch_io_flag_t
flags
,
int
stream_id
);
static
switch_status_t
vlc_write_frame
(
switch_core_session_t
*
session
,
switch_frame_t
*
frame
,
switch_io_flag_t
flags
,
int
stream_id
);
static
switch_status_t
vlc_read_video_frame
(
switch_core_session_t
*
session
,
switch_frame_t
**
frame
,
switch_io_flag_t
flags
,
int
stream_id
);
static
switch_status_t
vlc_receive_message
(
switch_core_session_t
*
session
,
switch_core_session_message_t
*
msg
);
static
switch_status_t
vlc_kill_channel
(
switch_core_session_t
*
session
,
int
sig
);
typedef
struct
{
switch_core_session_t
*
session
;
switch_channel_t
*
channel
;
switch_codec_t
read_codec
,
write_codec
;
switch_codec_t
video_codec
;
switch_frame_t
read_frame
;
switch_frame_t
read_video_frame
;
void
*
audio_data
[
SWITCH_RECOMMENDED_BUFFER_SIZE
];
void
*
video_data
[
SWITCH_RECOMMENDED_BUFFER_SIZE
*
20
];
const
char
*
destination_number
;
vlc_video_context_t
*
context
;
switch_timer_t
timer
;
}
vlc_private_t
;
switch_state_handler_table_t
vlc_state_handlers
=
{
/*on_init */
channel_on_init
,
/*on_routing */
NULL
,
/*on_execute */
NULL
,
/*on_hangup*/
NULL
,
/*on_exchange_media*/
NULL
,
/*on_soft_execute*/
NULL
,
/*on_consume_media*/
channel_on_consume_media
,
/*on_hibernate*/
NULL
,
/*on_reset*/
NULL
,
/*on_park*/
NULL
,
/*on_reporting*/
NULL
,
/*on_destroy*/
channel_on_destroy
};
switch_io_routines_t
vlc_io_routines
=
{
/*outgoing_channel*/
vlc_outgoing_channel
,
/*read_frame*/
vlc_read_frame
,
/*write_frame*/
vlc_write_frame
,
/*kill_channel*/
vlc_kill_channel
,
/*send_dtmf*/
NULL
,
/*receive_message*/
vlc_receive_message
,
/*receive_event*/
NULL
,
/*state_change*/
NULL
,
/*read_video_frame*/
vlc_read_video_frame
,
/*write_video_frame*/
NULL
,
/*state_run*/
NULL
};
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
);
switch_memory_pool_t
*
pool
=
switch_core_session_get_pool
(
session
);
// switch_dtmf_t dtmf = { 0 };
vlc_video_context_t
*
context
;
vlc_private_t
*
tech_pvt
;
switch_status_t
status
=
SWITCH_STATUS_SUCCESS
;
tech_pvt
=
switch_core_session_alloc
(
session
,
sizeof
*
tech_pvt
);
switch_assert
(
tech_pvt
);
tech_pvt
->
session
=
session
;
tech_pvt
->
channel
=
channel
;
tech_pvt
->
destination_number
=
switch_core_session_strdup
(
session
,
path
);
switch_core_session_set_private
(
session
,
tech_pvt
);
context
=
switch_core_session_alloc
(
session
,
sizeof
(
vlc_video_context_t
));
switch_assert
(
context
);
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
);
}
if
(
switch_core_timer_init
(
&
tech_pvt
->
timer
,
"soft"
,
20
,
8000
/
(
1000
/
20
),
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"Timer Activation Fail
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
fail
;
}
context
->
session
=
session
;
context
->
pool
=
pool
;
context
->
aud_frame
=
&
tech_pvt
->
read_frame
;
context
->
vid_frame
=
&
tech_pvt
->
read_video_frame
;
context
->
playing
=
0
;
// context->err = 0;
switch_mutex_init
(
&
context
->
audio_mutex
,
SWITCH_MUTEX_NESTED
,
context
->
pool
);
switch_mutex_init
(
&
context
->
video_mutex
,
SWITCH_MUTEX_NESTED
,
context
->
pool
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC open %s for reading
\n
"
,
path
);
/* Determine if this is a url or a path */
/* TODO: Change this so that it tries local files first, and then if it fails try location. */
if
(
!
strncmp
(
path
,
"http"
,
4
)){
context
->
m
=
libvlc_media_new_location
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is http %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"rtp"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is rtp %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"mms"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is mms %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"rtsp"
,
3
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is rtsp %s
\n
"
,
path
);
}
else
if
(
!
strncmp
(
path
,
"/"
,
1
)){
context
->
m
=
libvlc_media_new_path
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is file %s
\n
"
,
path
);
}
else
{
context
->
m
=
libvlc_media_new_location
(
read_inst
,
path
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_NOTICE
,
"VLC Path is unknown type %s
\n
"
,
path
);
}
if
(
context
->
m
==
NULL
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC error opening %s for reading
\n
"
,
path
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
);
goto
fail
;
}
context
->
mp
=
libvlc_media_player_new_from_media
(
context
->
m
);
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
);
}
return
SWITCH_STATUS_SUCCESS
;
fail:
return
SWITCH_STATUS_FALSE
;
}
static
switch_status_t
channel_on_init
(
switch_core_session_t
*
session
)
{
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
switch_channel_set_state
(
channel
,
CS_CONSUME_MEDIA
);
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
channel_on_consume_media
(
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
);
// return SWITCH_STATUS_SUCCESS;
switch_assert
(
tech_pvt
&&
tech_pvt
->
context
);
// // start play
// if (-1 == libvlc_media_player_play(tech_pvt->context->mp)) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error playing %s\n", tech_pvt->destination_number);
// return SWITCH_STATUS_FALSE;
// };
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
channel_on_destroy
(
switch_core_session_t
*
session
)
{
vlc_private_t
*
tech_pvt
=
switch_core_session_get_private
(
session
);
switch_assert
(
tech_pvt
&&
tech_pvt
->
context
);
if
((
tech_pvt
=
switch_core_session_get_private
(
session
)))
{
if
(
tech_pvt
->
read_codec
.
implementation
)
{
switch_core_codec_destroy
(
&
tech_pvt
->
read_codec
);
}
if
(
tech_pvt
->
write_codec
.
implementation
)
{
switch_core_codec_destroy
(
&
tech_pvt
->
write_codec
);
}
}
switch_yield
(
50000
);
if
(
tech_pvt
->
context
->
mp
)
libvlc_media_player_stop
(
tech_pvt
->
context
->
mp
);
if
(
tech_pvt
->
context
->
m
)
libvlc_media_release
(
tech_pvt
->
context
->
m
);
tech_pvt
->
context
->
playing
=
0
;
if
(
tech_pvt
->
context
->
audio_buffer
)
{
switch_buffer_destroy
(
&
tech_pvt
->
context
->
audio_buffer
);
}
if
(
tech_pvt
->
timer
.
interval
)
{
switch_core_timer_destroy
(
&
tech_pvt
->
timer
);
}
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_call_cause_t
vlc_outgoing_channel
(
switch_core_session_t
*
session
,
switch_event_t
*
var_event
,
switch_caller_profile_t
*
outbound_profile
,
switch_core_session_t
**
new_session
,
switch_memory_pool_t
**
pool
,
switch_originate_flag_t
flags
,
switch_call_cause_t
*
cancel_cause
)
{
switch_channel_t
*
channel
;
char
name
[
256
];
vlc_private_t
*
tech_pvt
=
NULL
;
switch_caller_profile_t
*
caller_profile
;
// const char *err;
switch_assert
(
vlc_endpoint_interface
);
if
(
session
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"session: %s
\n
"
,
switch_core_session_get_uuid
(
session
));
}
if
(
!
(
*
new_session
=
switch_core_session_request
(
vlc_endpoint_interface
,
SWITCH_CALL_DIRECTION_OUTBOUND
,
0
,
pool
)))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Couldn't request session.
\n
"
);
goto
fail
;
}
channel
=
switch_core_session_get_channel
(
*
new_session
);
snprintf
(
name
,
sizeof
(
name
),
"vlc/%s"
,
outbound_profile
->
destination_number
);
switch_channel_set_name
(
channel
,
name
);
switch_channel_set_flag
(
channel
,
CF_VIDEO
);
if
(
setup_tech_pvt
(
*
new_session
,
outbound_profile
->
destination_number
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Error steup tech_pvt!
\n
"
);
goto
fail
;
}
tech_pvt
=
switch_core_session_get_private
(
*
new_session
);
caller_profile
=
switch_caller_profile_clone
(
*
new_session
,
outbound_profile
);
switch_channel_set_caller_profile
(
channel
,
caller_profile
);
if
(
switch_core_codec_init
(
&
tech_pvt
->
read_codec
,
"L16"
,
NULL
,
8000
,
20
,
1
,
/*SWITCH_CODEC_FLAG_ENCODE |*/
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
tech_pvt
->
session
))
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't load codec?
\n
"
);
goto
fail
;
}
if
(
switch_core_codec_init
(
&
tech_pvt
->
write_codec
,
"L16"
,
NULL
,
8000
,
20
,
1
,
SWITCH_CODEC_FLAG_ENCODE
/*| SWITCH_CODEC_FLAG_DECODE*/
,
NULL
,
switch_core_session_get_pool
(
tech_pvt
->
session
))
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't load codec?
\n
"
);
goto
fail
;
}
if
(
switch_core_codec_init
(
&
tech_pvt
->
video_codec
,
"H264"
,
NULL
,
90000
,
0
,
1
,
SWITCH_CODEC_FLAG_ENCODE
|
SWITCH_CODEC_FLAG_DECODE
,
NULL
,
switch_core_session_get_pool
(
tech_pvt
->
session
))
==
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
*
new_session
),
SWITCH_LOG_DEBUG
,
"Video Codec Activation Success
\n
"
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
*
new_session
),
SWITCH_LOG_ERROR
,
"Video Codec Activation Fail
\n
"
);
goto
fail
;
}
if
(
switch_core_session_set_read_codec
(
*
new_session
,
&
tech_pvt
->
read_codec
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't set read codec?
\n
"
);
goto
fail
;
}
if
(
switch_core_session_set_write_codec
(
*
new_session
,
&
tech_pvt
->
write_codec
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't set write codec?
\n
"
);
goto
fail
;
}
if
(
switch_core_session_set_video_read_codec
(
*
new_session
,
&
tech_pvt
->
video_codec
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't set read codec?
\n
"
);
goto
fail
;
}
if
(
switch_core_session_set_video_write_codec
(
*
new_session
,
&
tech_pvt
->
video_codec
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Can't set write codec?
\n
"
);
goto
fail
;
}
switch_channel_set_state
(
channel
,
CS_INIT
);
if
(
switch_core_session_thread_launch
(
*
new_session
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Couldn't start session thread.
\n
"
);
goto
fail
;
}
switch_channel_mark_answered
(
channel
);
// start play
if
(
-
1
==
libvlc_media_player_play
(
tech_pvt
->
context
->
mp
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"VLC error playing %s
\n
"
,
tech_pvt
->
destination_number
);
goto
fail
;
};
return
SWITCH_CAUSE_SUCCESS
;
fail:
if
(
tech_pvt
)
{
if
(
tech_pvt
->
read_codec
.
implementation
)
{
switch_core_codec_destroy
(
&
tech_pvt
->
read_codec
);
}
if
(
tech_pvt
->
write_codec
.
implementation
)
{
switch_core_codec_destroy
(
&
tech_pvt
->
write_codec
);
}
if
(
tech_pvt
->
video_codec
.
implementation
)
{
switch_core_codec_destroy
(
&
tech_pvt
->
video_codec
);
}
}
if
(
*
new_session
)
{
switch_core_session_destroy
(
new_session
);
}
return
SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER
;
}
static
switch_status_t
vlc_read_frame
(
switch_core_session_t
*
session
,
switch_frame_t
**
frame
,
switch_io_flag_t
flags
,
int
stream_id
)
{
vlc_private_t
*
tech_pvt
;
switch_channel_t
*
channel
;
switch_status_t
status
=
SWITCH_STATUS_SUCCESS
;
libvlc_state_t
vlc_status
;
vlc_video_context_t
*
context
;
switch_size_t
audio_datalen
=
160
;
channel
=
switch_core_session_get_channel
(
session
);
assert
(
channel
!=
NULL
);
tech_pvt
=
switch_core_session_get_private
(
session
);
assert
(
tech_pvt
!=
NULL
);
switch_yield
(
20000
);
// goto cng;
context
=
tech_pvt
->
context
;
assert
(
context
);
vlc_status
=
libvlc_media_get_state
(
context
->
m
);
if
(
vlc_status
==
libvlc_Ended
||
vlc_status
==
libvlc_Error
||
vlc_status
==
libvlc_Stopped
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"VLC done. status = %d
\n
"
,
status
);
switch_channel_hangup
(
channel
,
SWITCH_CAUSE_SUCCESS
);
return
SWITCH_STATUS_SUCCESS
;
}
switch_mutex_lock
(
context
->
audio_mutex
);
if
(
switch_buffer_inuse
(
context
->
audio_buffer
)
>=
audio_datalen
*
2
)
{
tech_pvt
->
read_frame
.
data
=
tech_pvt
->
audio_data
;
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d %d\n", (int)switch_buffer_inuse(context->audio_buffer), (int)audio_datalen * 2);
switch_buffer_read
(
context
->
audio_buffer
,
tech_pvt
->
read_frame
.
data
,
audio_datalen
*
2
);
tech_pvt
->
read_frame
.
datalen
=
audio_datalen
*
2
;
tech_pvt
->
read_frame
.
buflen
=
audio_datalen
*
2
;
tech_pvt
->
read_frame
.
flags
&=
~
SFF_CNG
;
tech_pvt
->
read_frame
.
codec
=
&
tech_pvt
->
read_codec
;
*
frame
=
&
tech_pvt
->
read_frame
;
switch_mutex_unlock
(
context
->
audio_mutex
);
return
SWITCH_STATUS_SUCCESS
;
}
switch_mutex_unlock
(
context
->
audio_mutex
);
goto
cng
;
cng:
*
frame
=
&
tech_pvt
->
read_frame
;
tech_pvt
->
read_frame
.
codec
=
&
tech_pvt
->
read_codec
;
tech_pvt
->
read_frame
.
flags
|=
SFF_CNG
;
tech_pvt
->
read_frame
.
datalen
=
0
;
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "read cng frame\n");
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
vlc_write_frame
(
switch_core_session_t
*
session
,
switch_frame_t
*
frame
,
switch_io_flag_t
flags
,
int
stream_id
)
{
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
vlc_read_video_frame
(
switch_core_session_t
*
session
,
switch_frame_t
**
frame
,
switch_io_flag_t
flags
,
int
stream_id
)
{
switch_channel_t
*
channel
;
vlc_private_t
*
tech_pvt
;
vlc_video_context_t
*
context
;
uint32_t
size
;
channel
=
switch_core_session_get_channel
(
session
);
switch_assert
(
channel
!=
NULL
);
tech_pvt
=
switch_core_session_get_private
(
session
);
switch_assert
(
tech_pvt
!=
NULL
);
context
=
tech_pvt
->
context
;
switch_assert
(
tech_pvt
->
context
);
switch_mutex_lock
(
context
->
video_mutex
);
while
(
switch_buffer_inuse
(
context
->
video_buffer
)
<
sizeof
(
uint32_t
))
{
switch_mutex_unlock
(
context
->
video_mutex
);
switch_yield
(
10000
);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "waiting for video\n");
switch_mutex_lock
(
context
->
video_mutex
);
}
switch_buffer_read
(
context
->
video_buffer
,
&
size
,
sizeof
(
uint32_t
));
if
(
size
==
0
)
{
goto
unlock
;
}
if
(
switch_buffer_inuse
(
context
->
video_buffer
)
<
sizeof
(
switch_frame_t
))
{
goto
unlock
;
}
switch_buffer_read
(
context
->
video_buffer
,
&
tech_pvt
->
read_video_frame
,
sizeof
(
switch_frame_t
));
if
(
switch_buffer_inuse
(
context
->
video_buffer
)
<
tech_pvt
->
read_video_frame
.
packetlen
)
{
goto
unlock
;
}
switch_buffer_read
(
context
->
video_buffer
,
tech_pvt
->
video_data
,
tech_pvt
->
read_video_frame
.
packetlen
);
tech_pvt
->
read_video_frame
.
packet
=
tech_pvt
->
video_data
;
tech_pvt
->
read_video_frame
.
data
=
tech_pvt
->
video_data
+
12
;
switch_mutex_unlock
(
context
->
video_mutex
);
*
frame
=
&
tech_pvt
->
read_video_frame
;
switch_set_flag
(
*
frame
,
SFF_RAW_RTP
);
switch_clear_flag
(
*
frame
,
SFF_CNG
);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "read video %d\n", (*frame)->packetlen);
return
SWITCH_STATUS_SUCCESS
;
goto
cng
;
unlock:
switch_mutex_unlock
(
context
->
video_mutex
);
cng:
*
frame
=
&
tech_pvt
->
read_frame
;
tech_pvt
->
read_frame
.
codec
=
&
tech_pvt
->
read_codec
;
tech_pvt
->
read_frame
.
flags
|=
SFF_CNG
;
tech_pvt
->
read_frame
.
datalen
=
0
;
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
vlc_receive_message
(
switch_core_session_t
*
session
,
switch_core_session_message_t
*
msg
)
{
vlc_private_t
*
tech_pvt
=
switch_core_session_get_private
(
session
);
assert
(
tech_pvt
!=
NULL
);
switch
(
msg
->
message_id
)
{
case
SWITCH_MESSAGE_INDICATE_DEBUG_MEDIA
:
break
;
case
SWITCH_MESSAGE_INDICATE_AUDIO_SYNC
:
break
;
case
SWITCH_MESSAGE_INDICATE_JITTER_BUFFER
:
break
;
default:
break
;
}
return
SWITCH_STATUS_SUCCESS
;
}
static
switch_status_t
vlc_kill_channel
(
switch_core_session_t
*
session
,
int
sig
)
{
vlc_private_t
*
tech_pvt
=
switch_core_session_get_private
(
session
);
switch_channel_t
*
channel
=
switch_core_session_get_channel
(
session
);
if
(
!
tech_pvt
)
return
SWITCH_STATUS_FALSE
;
if
(
!
tech_pvt
->
context
)
return
SWITCH_STATUS_FALSE
;
switch
(
sig
)
{
case
SWITCH_SIG_BREAK
:
case
SWITCH_SIG_KILL
:
if
(
switch_channel_test_flag
(
channel
,
CF_VIDEO
))
{
uint32_t
size
=
0
;
switch_buffer_write
(
tech_pvt
->
context
->
video_buffer
,
&
size
,
sizeof
(
size
));
}
break
;
default:
break
;
}
return
SWITCH_STATUS_SUCCESS
;
}
/* Macro expands to: switch_status_t mod_vlc_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION
(
mod_vlc_load
)
{
switch_file_interface_t
*
file_interface
;
switch_application_interface_t
*
app_interface
;
/* connect my internal structure to the blank pointer passed to me */
*
module_interface
=
switch_loadable_module_create_module_interface
(
pool
,
modname
);
...
...
@@ -400,14 +1557,21 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load)
file_interface
->
file_read
=
vlc_file_read
;
file_interface
->
file_write
=
vlc_file_write
;
vlc_endpoint_interface
=
switch_loadable_module_create_interface
(
*
module_interface
,
SWITCH_ENDPOINT_INTERFACE
);
vlc_endpoint_interface
->
interface_name
=
"vlc"
;
vlc_endpoint_interface
->
io_routines
=
&
vlc_io_routines
;
vlc_endpoint_interface
->
state_handler
=
&
vlc_state_handlers
;
/* load the vlc engine. */
read_inst
=
libvlc_new
(
1
,
&
vlc_args
);
read_inst
=
libvlc_new
(
sizeof
(
vlc_args
)
/
sizeof
(
char
*
),
vlc_args
);
if
(
!
read_inst
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CRIT
,
"FAILED TO LOAD
\n
"
);
return
SWITCH_STATUS_GENERR
;
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CRIT
,
"FAILED TO LOAD
\n
"
);
return
SWITCH_STATUS_GENERR
;
}
SWITCH_ADD_APP
(
app_interface
,
"play_video"
,
"play an videofile"
,
"play an video file"
,
play_video_function
,
"<file>"
,
SAF_SUPPORT_NOMEDIA
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"Initialized VLC instance
\n
"
);
/* indicate that the module should continue to be loaded */
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论