2015-03-17 23:52:01 +08:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
* Copyright ( C ) 2005 - 2015 , 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 >
2015-05-21 15:48:48 -05:00
* Anthony Minessale < anthm @ freeswitch . org >
2015-03-17 23:52:01 +08:00
*
2015-05-21 15:48:48 -05:00
* mod_avformat - - File Formats with libav . org
2015-03-17 23:52:01 +08:00
*
*/
# include <switch.h>
2017-07-14 18:35:23 -05:00
# include "mod_av.h"
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/opt.h>
# include <libavutil/imgutils.h>
# include <libavutil/avstring.h>
# include <libavutil/channel_layout.h>
# include <libavresample/avresample.h>
2015-04-24 00:20:00 +08:00
# include <libswscale/swscale.h>
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
# define SCALE_FLAGS SWS_BICUBIC
2015-09-29 23:00:28 -05:00
# define DFT_RECORD_OFFSET 0
2015-03-17 23:52:01 +08:00
2016-03-09 20:46:34 +08:00
# ifndef AVUTIL_TIMESTAMP_H
# define AVUTIL_TIMESTAMP_H
# define AV_TS_MAX_STRING_SIZE 32
2017-05-19 17:38:08 -05:00
/* App interface */
// a wrapper around a single output AVStream
typedef struct MediaStream {
AVStream * st ;
AVFrame * frame ;
AVFrame * tmp_frame ;
// audio
int channels ;
int sample_rate ;
struct AVAudioResampleContext * resample_ctx ;
//video
int width ;
int height ;
struct SwsContext * sws_ctx ;
int64_t next_pts ;
2017-07-14 18:35:23 -05:00
int active ;
int r ;
2017-05-19 17:38:08 -05:00
} MediaStream ;
typedef struct record_helper_s {
switch_mutex_t * mutex ;
AVFormatContext * fc ;
MediaStream * video_st ;
2017-05-31 12:48:46 -05:00
switch_timer_t * video_timer ;
2017-05-19 17:38:08 -05:00
int in_callback ;
switch_queue_t * video_queue ;
switch_thread_t * video_thread ;
switch_mm_t * mm ;
int finalize ;
2017-05-30 16:34:48 -05:00
switch_file_handle_t * fh ;
2017-06-01 12:51:35 -05:00
switch_time_t record_timer_paused ;
uint64_t last_ts ;
2017-05-19 17:38:08 -05:00
} record_helper_t ;
/* file interface */
struct av_file_context {
switch_memory_pool_t * pool ;
switch_mutex_t * mutex ;
switch_thread_cond_t * cond ;
switch_buffer_t * buf ;
switch_buffer_t * audio_buffer ;
switch_timer_t video_timer ;
int offset ;
int audio_start ;
int aud_ready ;
int vid_ready ;
int audio_ready ;
int closed ;
MediaStream video_st ;
2017-07-14 18:35:23 -05:00
MediaStream audio_st [ 2 ] ;
2017-05-19 17:38:08 -05:00
AVFormatContext * fc ;
AVCodec * audio_codec ;
AVCodec * video_codec ;
int has_audio ;
int has_video ;
2017-07-14 18:35:23 -05:00
2017-05-19 17:38:08 -05:00
record_helper_t eh ;
switch_thread_t * file_read_thread ;
int file_read_thread_running ;
int file_read_thread_started ;
switch_time_t video_start_time ;
switch_image_t * last_img ;
int read_fps ;
switch_time_t last_vid_push ;
int64_t seek_ts ;
switch_bool_t read_paused ;
int errs ;
switch_file_handle_t * handle ;
2017-07-14 18:35:23 -05:00
int16_t * mux_buf ;
switch_size_t mux_buf_len ;
2017-05-19 17:38:08 -05:00
} ;
typedef struct av_file_context av_file_context_t ;
2016-03-09 20:46:34 +08:00
/**
* Fill the provided buffer with a string containing a timestamp
* representation .
*
* @ param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
* @ param ts the timestamp to represent
* @ return the buffer in input
*/
static inline char * av_ts_make_string ( char * buf , int64_t ts )
{
if ( ts = = AV_NOPTS_VALUE ) snprintf ( buf , AV_TS_MAX_STRING_SIZE , " NOPTS " ) ;
else snprintf ( buf , AV_TS_MAX_STRING_SIZE , " % " PRId64 " " , ts ) ;
return buf ;
}
/**
* Convenience macro , the return value should be used only directly in
* function arguments but never stand - alone .
*/
# define av_ts2str(ts) av_ts_make_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts)
/**
* Fill the provided buffer with a string containing a timestamp time
* representation .
*
* @ param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
* @ param ts the timestamp to represent
* @ param tb the timebase of the timestamp
* @ return the buffer in input
*/
static inline char * av_ts_make_time_string ( char * buf , int64_t ts , AVRational * tb )
{
if ( ts = = AV_NOPTS_VALUE ) snprintf ( buf , AV_TS_MAX_STRING_SIZE , " NOPTS " ) ;
else snprintf ( buf , AV_TS_MAX_STRING_SIZE , " %.6g " , av_q2d ( * tb ) * ts ) ;
return buf ;
}
/**
* Convenience macro , the return value should be used only directly in
* function arguments but never stand - alone .
*/
# define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb)
# endif /* AVUTIL_TIMESTAMP_H */
2015-07-21 08:37:42 +08:00
static switch_status_t av_file_close ( switch_file_handle_t * handle ) ;
2015-03-17 23:52:01 +08:00
SWITCH_MODULE_LOAD_FUNCTION ( mod_avformat_load ) ;
2017-07-14 18:35:23 -05:00
static char * const get_error_text ( const int error , char * error_buffer , switch_size_t error_buflen )
2015-03-17 23:52:01 +08:00
{
2017-07-14 18:35:23 -05:00
av_strerror ( error , error_buffer , error_buflen ) ;
2015-04-24 00:20:00 +08:00
return error_buffer ;
2015-03-17 23:52:01 +08:00
}
2017-01-18 04:49:05 +03:00
static void av_unused fill_avframe ( AVFrame * pict , switch_image_t * img )
2015-03-17 23:52:01 +08:00
{
int i ;
uint8_t * y = img - > planes [ 0 ] ;
uint8_t * u = img - > planes [ 1 ] ;
uint8_t * v = img - > planes [ 2 ] ;
/* Y */
for ( i = 0 ; i < pict - > height ; i + + ) {
memcpy ( & pict - > data [ 0 ] [ i * pict - > linesize [ 0 ] ] , y + i * img - > stride [ 0 ] , pict - > width ) ;
}
/* U/V */
for ( i = 0 ; i < pict - > height / 2 ; i + + ) {
memcpy ( & pict - > data [ 1 ] [ i * pict - > linesize [ 1 ] ] , u + i * img - > stride [ 1 ] , pict - > width / 2 ) ;
memcpy ( & pict - > data [ 2 ] [ i * pict - > linesize [ 2 ] ] , v + i * img - > stride [ 2 ] , pict - > width / 2 ) ;
}
}
2017-01-18 04:49:05 +03:00
static void av_unused avframe2img ( AVFrame * pict , switch_image_t * img )
2015-04-24 00:20:00 +08:00
{
2017-03-06 08:43:31 +08:00
int i , j ;
if ( img - > fmt = = SWITCH_IMG_FMT_I420 ) {
if ( pict - > format = = AV_PIX_FMT_YUV420P ) {
switch_I420_copy2 ( pict - > data , pict - > linesize , img - > planes , img - > stride , img - > d_w , img - > d_h ) ;
} else if ( pict - > format = = AV_PIX_FMT_YUVA420P ) {
int linesize [ 3 ] ;
linesize [ 0 ] = pict - > linesize [ 0 ] ;
linesize [ 1 ] = pict - > linesize [ 1 ] ;
linesize [ 2 ] = pict - > linesize [ 2 ] + pict - > linesize [ 0 ] ;
switch_I420_copy2 ( pict - > data , linesize , img - > planes , img - > stride , img - > d_w , img - > d_h ) ;
}
2015-04-24 00:20:00 +08:00
2017-03-06 08:43:31 +08:00
} else if ( img - > fmt = = SWITCH_IMG_FMT_ARGB ) {
if ( pict - > format = = AV_PIX_FMT_YUV420P ) {
switch_rgb_color_t * color = ( switch_rgb_color_t * ) img - > planes [ SWITCH_PLANE_PACKED ] ;
uint8_t * alpha = pict - > data [ 3 ] ;
2015-04-24 00:20:00 +08:00
2017-03-06 08:43:31 +08:00
/*!\brief I420 to ARGB Convertion*/
switch_I420ToARGB ( pict - > data [ 0 ] , pict - > linesize [ 0 ] ,
pict - > data [ 1 ] , pict - > linesize [ 1 ] ,
pict - > data [ 2 ] , pict - > linesize [ 2 ] ,
img - > planes [ SWITCH_PLANE_PACKED ] , img - > stride [ SWITCH_PLANE_PACKED ] ,
img - > d_w , img - > d_h ) ;
for ( j = 0 ; j < img - > d_h ; j + + ) {
for ( i = 0 ; i < img - > d_w ; i + + ) {
color - > a = * alpha + + ;
color + + ;
}
color = ( switch_rgb_color_t * ) ( img - > planes [ SWITCH_PLANE_PACKED ] + img - > stride [ SWITCH_PLANE_PACKED ] * j ) ;
}
} else if ( pict - > format = = AV_PIX_FMT_RGBA ) {
# if SWITCH_BYTE_ORDER == __BIG_ENDIAN
switch_RGBAToARGB ( pict - > data [ 0 ] , pict - > linesize [ 0 ] ,
mg - > planes [ SWITCH_PLANE_PACKED ] , img - > stride [ SWITCH_PLANE_PACKED ] ,
img - > d_w , img - > d_h ) ;
# else
switch_ABGRToARGB ( pict - > data [ 0 ] , pict - > linesize [ 0 ] ,
img - > planes [ SWITCH_PLANE_PACKED ] , img - > stride [ SWITCH_PLANE_PACKED ] ,
img - > d_w , img - > d_h ) ;
# endif
} else if ( pict - > format = = AV_PIX_FMT_BGRA ) {
# if SWITCH_BYTE_ORDER == __BIG_ENDIAN
switch_BGRAToARGB ( pict - > data [ 0 ] , pict - > linesize [ 0 ] ,
, img - > planes [ SWITCH_PLANE_PACKED ] , img - > stride [ SWITCH_PLANE_PACKED ] ,
, img - > d_w , img - > d_h ) ;
# else
switch_ARGBToARGB ( pict - > data [ 0 ] , pict - > linesize [ 0 ] ,
img - > planes [ SWITCH_PLANE_PACKED ] , img - > stride [ SWITCH_PLANE_PACKED ] ,
img - > d_w , img - > d_h ) ;
# endif
}
2015-04-24 00:20:00 +08:00
}
}
2017-01-18 04:49:05 +03:00
static void av_unused avframe2fd ( AVFrame * pict , int fd )
2015-04-24 00:20:00 +08:00
{
int i ;
uint8_t * y = pict - > data [ 0 ] ;
uint8_t * u = pict - > data [ 1 ] ;
uint8_t * v = pict - > data [ 2 ] ;
/* Y */
for ( i = 0 ; i < pict - > height ; i + + ) {
write ( fd , y + i * pict - > linesize [ 0 ] , pict - > width ) ;
}
/* U/V */
for ( i = 0 ; i < pict - > height / 2 ; i + + ) {
write ( fd , u + i * pict - > linesize [ 1 ] , pict - > width / 2 ) ;
}
for ( i = 0 ; i < pict - > height / 2 ; i + + ) {
write ( fd , v + i * pict - > linesize [ 2 ] , pict - > width / 2 ) ;
}
}
2015-03-17 23:52:01 +08:00
static void log_packet ( const AVFormatContext * fmt_ctx , const AVPacket * pkt )
{
2017-07-14 18:35:23 -05:00
if ( mod_av_globals . debug < 2 ) return ;
2015-03-17 23:52:01 +08:00
2017-07-14 18:35:23 -05:00
{
AVRational * time_base = & fmt_ctx - > streams [ pkt - > stream_index ] - > time_base ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d \n " ,
av_ts2str ( pkt - > pts ) , av_ts2timestr ( pkt - > pts , time_base ) ,
av_ts2str ( pkt - > dts ) , av_ts2timestr ( pkt - > dts , time_base ) ,
av_ts2str ( pkt - > duration ) , av_ts2timestr ( pkt - > duration , time_base ) ,
pkt - > stream_index ) ;
}
2015-03-17 23:52:01 +08:00
}
2015-05-04 15:49:22 -04:00
static int mod_avformat_alloc_output_context2 ( AVFormatContext * * avctx , AVOutputFormat * oformat ,
2015-04-24 00:20:00 +08:00
const char * format , const char * filename )
2015-03-17 23:52:01 +08:00
{
2015-04-24 00:20:00 +08:00
AVFormatContext * s = avformat_alloc_context ( ) ;
int ret = 0 ;
* avctx = NULL ;
if ( ! s )
goto nomem ;
if ( ! oformat ) {
if ( format ) {
oformat = av_guess_format ( format , NULL , NULL ) ;
if ( ! oformat ) {
av_log ( s , AV_LOG_ERROR , " Requested output format '%s' is not a suitable output format \n " , format ) ;
ret = AVERROR ( EINVAL ) ;
goto error ;
}
} else {
oformat = av_guess_format ( NULL , filename , NULL ) ;
if ( ! oformat ) {
ret = AVERROR ( EINVAL ) ;
av_log ( s , AV_LOG_ERROR , " Unable to find a suitable output format for '%s' \n " ,
filename ) ;
goto error ;
}
}
}
s - > oformat = oformat ;
if ( s - > oformat - > priv_data_size > 0 ) {
s - > priv_data = av_mallocz ( s - > oformat - > priv_data_size ) ;
if ( ! s - > priv_data )
goto nomem ;
if ( s - > oformat - > priv_class ) {
* ( const AVClass * * ) s - > priv_data = s - > oformat - > priv_class ;
av_opt_set_defaults ( s - > priv_data ) ;
}
} else
s - > priv_data = NULL ;
if ( filename )
av_strlcpy ( s - > filename , filename , sizeof ( s - > filename ) ) ;
* avctx = s ;
return 0 ;
2015-03-17 23:52:01 +08:00
nomem :
2015-04-24 00:20:00 +08:00
av_log ( s , AV_LOG_ERROR , " Out of memory \n " ) ;
ret = AVERROR ( ENOMEM ) ;
2015-03-17 23:52:01 +08:00
error :
2015-04-24 00:20:00 +08:00
avformat_free_context ( s ) ;
return ret ;
2015-03-17 23:52:01 +08:00
}
static int write_frame ( AVFormatContext * fmt_ctx , const AVRational * time_base , AVStream * st , AVPacket * pkt )
{
/* rescale output packet timestamp values from codec to stream timebase */
av_packet_rescale_ts ( pkt , * time_base , st - > time_base ) ;
pkt - > stream_index = st - > index ;
/* Write the compressed frame to the media file. */
2017-07-14 18:35:23 -05:00
log_packet ( fmt_ctx , pkt ) ;
2015-03-17 23:52:01 +08:00
return av_interleaved_write_frame ( fmt_ctx , pkt ) ;
}
/* Add an output stream. */
2017-07-14 18:35:23 -05:00
static switch_status_t add_stream ( av_file_context_t * context , MediaStream * mst , AVFormatContext * fc , AVCodec * * codec , enum AVCodecID codec_id , switch_mm_t * mm )
2015-03-17 23:52:01 +08:00
{
AVCodecContext * c ;
switch_status_t status = SWITCH_STATUS_FALSE ;
2017-09-27 12:58:04 -05:00
//int threads = switch_core_cpu_count();
2015-04-15 12:56:07 -05:00
int buffer_bytes = 2097152 ; /* 2 mb */
2015-05-22 20:27:14 -05:00
int fps = 15 ;
2015-04-15 12:56:07 -05:00
2017-11-02 14:03:21 -05:00
//if (mm->try_hardware_encoder && codec_id == AV_CODEC_ID_H264) {
// *codec = avcodec_find_encoder_by_name("nvenc_h264");
//}
2016-03-23 17:55:46 -05:00
if ( ! * codec ) {
/* find the encoder */
* codec = avcodec_find_encoder ( codec_id ) ;
}
2015-03-17 23:52:01 +08:00
if ( ! ( * codec ) ) {
2017-06-28 12:16:46 -05:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not find encoder \n " ) ;
2015-03-17 23:52:01 +08:00
return status ;
}
2015-04-24 00:20:00 +08:00
mst - > st = avformat_new_stream ( fc , * codec ) ;
if ( ! mst - > st ) {
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate stream \n " ) ;
return status ;
}
2015-04-24 00:20:00 +08:00
mst - > st - > id = fc - > nb_streams - 1 ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
c = mst - > st - > codec ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-09-24 13:55:21 -05:00
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "id:%d den:%d num:%d\n", mst->st->id, mst->st->time_base.den, mst->st->time_base.num);
2015-03-17 23:52:01 +08:00
2017-09-27 12:58:04 -05:00
//if (threads > 4) {
// threads = 4;
// }
2015-03-17 23:52:01 +08:00
switch ( ( * codec ) - > type ) {
case AVMEDIA_TYPE_AUDIO :
c - > sample_fmt = ( * codec ) - > sample_fmts ? ( * codec ) - > sample_fmts [ 0 ] : AV_SAMPLE_FMT_FLTP ;
c - > bit_rate = 128000 ;
2017-07-14 18:35:23 -05:00
c - > sample_rate = mst - > sample_rate = context - > handle - > samplerate ;
2015-04-24 00:20:00 +08:00
c - > channels = mst - > channels ;
2015-03-17 23:52:01 +08:00
c - > channel_layout = av_get_default_channel_layout ( c - > channels ) ;
2015-03-18 17:58:23 -05:00
if ( mm ) {
if ( mm - > ab ) {
c - > bit_rate = mm - > ab * 1024 ;
}
if ( mm - > samplerate ) {
2015-04-24 00:20:00 +08:00
c - > sample_rate = mst - > sample_rate = mm - > samplerate ;
2015-03-18 17:58:23 -05:00
}
}
2017-07-14 18:35:23 -05:00
if ( context & & context - > has_video & & ! context - > handle - > stream_name ) {
mst - > st - > time_base . den = c - > sample_rate ;
mst - > st - > time_base . num = 1 ;
c - > time_base . den = c - > sample_rate ;
c - > time_base . num = 1 ;
} else {
// nothing to do for audio only recording, just leave the time base as is
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "stream_timebase: %d/%d codec_timebase: %d/%d nb_samples: %d\n",
// mst->st->time_base.num, mst->st->time_base.den, c->time_base.num, c->time_base.den, c->frame_size);
}
2015-03-18 17:58:23 -05:00
2015-03-17 23:52:01 +08:00
break ;
case AVMEDIA_TYPE_VIDEO :
2015-04-15 12:56:07 -05:00
if ( mm ) {
if ( mm - > vbuf ) {
buffer_bytes = mm - > vbuf ;
}
2015-09-24 13:55:21 -05:00
if ( mm - > fps ) {
fps = mm - > fps ;
} else {
mm - > fps = fps ;
}
2017-01-06 02:10:15 -05:00
2015-05-22 20:27:14 -05:00
if ( mm - > vw & & mm - > vh ) {
mst - > width = mm - > vw ;
mst - > height = mm - > vh ;
}
2015-04-15 12:56:07 -05:00
}
2015-03-17 23:52:01 +08:00
c - > codec_id = codec_id ;
2015-05-22 20:27:14 -05:00
2015-03-17 23:52:01 +08:00
/* Resolution must be a multiple of two. */
2015-04-24 00:20:00 +08:00
c - > width = mst - > width ;
c - > height = mst - > height ;
2017-07-20 17:35:28 -05:00
c - > bit_rate = mm - > vb * 1024 ;
2017-05-25 12:01:18 -05:00
mst - > st - > time_base . den = 90000 ;
2015-04-24 00:20:00 +08:00
mst - > st - > time_base . num = 1 ;
2017-05-25 12:01:18 -05:00
c - > time_base . den = 90000 ;
2015-03-17 23:52:01 +08:00
c - > time_base . num = 1 ;
2015-04-24 00:20:00 +08:00
c - > gop_size = 25 ; /* emit one intra frame every x frames at mmst */
2015-03-17 23:52:01 +08:00
c - > pix_fmt = AV_PIX_FMT_YUV420P ;
2017-09-27 12:58:04 -05:00
//c->thread_count = threads;
2015-04-15 12:56:07 -05:00
c - > rc_initial_buffer_occupancy = buffer_bytes * 8 ;
2015-04-21 18:50:45 -05:00
if ( codec_id = = AV_CODEC_ID_H264 ) {
c - > ticks_per_frame = 2 ;
2015-08-25 09:02:53 -05:00
2016-03-09 18:01:03 -06:00
c - > flags | = CODEC_FLAG_LOOP_FILTER ; // flags=+loop
c - > me_cmp | = 1 ; // cmp=+chroma, where CHROMA = 1
c - > me_range = 16 ; // me_range=16
c - > max_b_frames = 3 ; // bf=3
2016-02-13 11:18:15 +08:00
2016-03-25 09:53:38 +08:00
av_opt_set_int ( c - > priv_data , " b_strategy " , 1 , 0 ) ;
av_opt_set_int ( c - > priv_data , " motion_est " , ME_HEX , 0 ) ;
av_opt_set_int ( c - > priv_data , " coder " , 1 , 0 ) ;
2015-09-30 11:42:40 -05:00
switch ( mm - > vprofile ) {
case SWITCH_VIDEO_PROFILE_BASELINE :
av_opt_set ( c - > priv_data , " profile " , " baseline " , 0 ) ;
c - > level = 41 ;
break ;
case SWITCH_VIDEO_PROFILE_MAIN :
av_opt_set ( c - > priv_data , " profile " , " main " , 0 ) ;
av_opt_set ( c - > priv_data , " level " , " 5 " , 0 ) ;
2016-03-09 18:01:03 -06:00
c - > level = 5 ;
2015-09-30 11:42:40 -05:00
break ;
case SWITCH_VIDEO_PROFILE_HIGH :
av_opt_set ( c - > priv_data , " profile " , " high " , 0 ) ;
av_opt_set ( c - > priv_data , " level " , " 52 " , 0 ) ;
2016-03-09 18:01:03 -06:00
c - > level = 52 ;
2015-09-30 11:42:40 -05:00
break ;
}
2017-01-06 02:10:15 -05:00
2015-08-25 09:02:53 -05:00
switch ( mm - > vencspd ) {
2015-09-30 11:42:40 -05:00
case SWITCH_VIDEO_ENCODE_SPEED_SLOW :
av_opt_set ( c - > priv_data , " preset " , " veryslow " , 0 ) ;
break ;
case SWITCH_VIDEO_ENCODE_SPEED_MEDIUM :
av_opt_set ( c - > priv_data , " preset " , " medium " , 0 ) ;
break ;
case SWITCH_VIDEO_ENCODE_SPEED_FAST :
2017-07-14 18:35:23 -05:00
//av_opt_set_int(c->priv_data, "intra-refresh", 1, 0);
2015-09-30 11:42:40 -05:00
av_opt_set ( c - > priv_data , " preset " , " veryfast " , 0 ) ;
2017-07-14 18:35:23 -05:00
//av_opt_set(c->priv_data, "tune", "animation+zerolatency", 0);
av_opt_set ( c - > priv_data , " tune " , " fastdecode " , 0 ) ;
2015-09-30 11:42:40 -05:00
break ;
default :
break ;
2015-08-25 09:02:53 -05:00
}
2015-04-21 18:50:45 -05:00
}
2017-07-14 18:35:23 -05:00
if ( mm ) {
if ( mm - > vb ) {
c - > bit_rate = mm - > vb * 1024 ;
}
if ( mm - > keyint ) {
c - > gop_size = mm - > keyint ;
}
}
2016-03-09 18:01:03 -06:00
2016-09-07 16:48:55 -05:00
if ( mm - > cbr ) {
c - > rc_min_rate = c - > bit_rate ;
c - > rc_max_rate = c - > bit_rate ;
c - > rc_buffer_size = c - > bit_rate ;
c - > qcompress = 0 ;
2017-07-14 18:35:23 -05:00
c - > gop_size = mm - > fps * 2 ;
c - > keyint_min = mm - > fps * 2 ;
2016-09-07 16:48:55 -05:00
} else {
c - > gop_size = 250 ; // g=250
c - > keyint_min = 25 ; // keyint_min=25
c - > i_quant_factor = 0.71 ; // i_qfactor=0.71
c - > qcompress = 0.6 ; // qcomp=0.6
c - > qmin = 10 ; // qmin=10
c - > qmax = 31 ; // qmax=31
c - > max_qdiff = 4 ; // qdiff=4
av_opt_set_int ( c - > priv_data , " crf " , 18 , 0 ) ;
}
2016-03-09 18:01:03 -06:00
2017-07-14 18:35:23 -05:00
2015-03-17 23:52:01 +08:00
if ( codec_id = = AV_CODEC_ID_VP8 ) {
av_set_options_string ( c , " quality=realtime " , " = " , " : " ) ;
}
2015-03-18 17:58:23 -05:00
2017-07-14 18:35:23 -05:00
av_opt_set_int ( c - > priv_data , " slice-max-size " , SWITCH_DEFAULT_VIDEO_SIZE , 0 ) ;
2017-05-22 19:58:32 -05:00
c - > colorspace = AVCOL_SPC_RGB ;
c - > color_range = AVCOL_RANGE_JPEG ;
2015-03-18 17:58:23 -05:00
2015-03-17 23:52:01 +08:00
break ;
default :
break ;
}
/* Some formats want stream headers to be separate. */
2015-04-24 00:20:00 +08:00
if ( fc - > oformat - > flags & AVFMT_GLOBALHEADER ) {
2015-03-17 23:52:01 +08:00
c - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
}
2017-07-14 18:35:23 -05:00
mst - > active = 1 ;
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_SUCCESS ;
}
static AVFrame * alloc_picture ( enum AVPixelFormat pix_fmt , int width , int height )
{
AVFrame * picture ;
int ret ;
picture = av_frame_alloc ( ) ;
if ( ! picture ) return NULL ;
picture - > format = pix_fmt ;
picture - > width = width ;
picture - > height = height ;
/* allocate the buffers for the frame data */
ret = av_frame_get_buffer ( picture , 32 ) ;
if ( ret < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate frame data. \n " ) ;
return NULL ;
}
return picture ;
}
2015-04-24 00:20:00 +08:00
static switch_status_t open_video ( AVFormatContext * fc , AVCodec * codec , MediaStream * mst )
2015-03-17 23:52:01 +08:00
{
int ret ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
AVCodecContext * c = mst - > st - > codec ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
switch_status_t status = SWITCH_STATUS_FALSE ;
2017-09-27 12:58:04 -05:00
//int threads = switch_core_cpu_count();
2017-07-20 17:35:28 -05:00
2017-09-27 12:58:04 -05:00
// if (threads > 4) threads = 4;
// c->thread_count = threads;
2015-03-17 23:52:01 +08:00
/* open the codec */
ret = avcodec_open2 ( c , codec , NULL ) ;
if ( ret < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open video codec: %s \n " , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
2015-03-17 23:52:01 +08:00
return status ;
}
/* allocate and init a re-usable frame */
2015-04-24 00:20:00 +08:00
mst - > frame = alloc_picture ( c - > pix_fmt , c - > width , c - > height ) ;
switch_assert ( mst - > frame ) ;
2016-03-10 17:05:55 -06:00
mst - > frame - > pts = 0 ;
2015-03-17 23:52:01 +08:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pix_fmt: %d\n", c->pix_fmt);
switch_assert ( c - > pix_fmt = = AV_PIX_FMT_YUV420P ) ; // always I420 for NOW
return SWITCH_STATUS_SUCCESS ;
}
2015-04-24 00:20:00 +08:00
static switch_status_t open_audio ( AVFormatContext * fc , AVCodec * codec , MediaStream * mst )
2015-03-17 23:52:01 +08:00
{
AVCodecContext * c ;
int ret ;
switch_status_t status = SWITCH_STATUS_FALSE ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
c = mst - > st - > codec ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
ret = avcodec_open2 ( c , codec , NULL ) ;
2016-03-23 20:13:32 +08:00
if ( ret = = AVERROR_EXPERIMENTAL ) {
const AVCodecDescriptor * desc = avcodec_descriptor_get ( c - > codec_id ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Codec [%s] is experimental feature in libavcodec, never mind \n " , desc - > name ) ;
c - > strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL ;
ret = avcodec_open2 ( c , codec , NULL ) ;
}
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
2016-03-23 20:13:32 +08:00
const AVCodecDescriptor * desc = avcodec_descriptor_get ( c - > codec_id ) ;
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open audio codec [%s], error: %s \n " , desc - > name , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
2015-03-17 23:52:01 +08:00
return status ;
}
2015-04-24 00:20:00 +08:00
mst - > frame = av_frame_alloc ( ) ;
switch_assert ( mst - > frame ) ;
2015-03-17 23:52:01 +08:00
2015-04-24 00:20:00 +08:00
mst - > frame - > sample_rate = c - > sample_rate ;
mst - > frame - > format = AV_SAMPLE_FMT_S16 ;
mst - > frame - > channel_layout = c - > channel_layout ;
2015-03-17 23:52:01 +08:00
if ( c - > codec - > capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ) {
2017-07-14 18:35:23 -05:00
//mst->frame->nb_samples = 10000;
mst - > frame - > nb_samples = ( mst - > frame - > sample_rate / 50 ) * c - > channels ;
2015-03-17 23:52:01 +08:00
} else {
2015-04-24 00:20:00 +08:00
mst - > frame - > nb_samples = c - > frame_size ;
2015-03-17 23:52:01 +08:00
}
2017-07-14 18:35:23 -05:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " sample_rate: %d nb_samples: %d \n " , mst - > frame - > sample_rate , mst - > frame - > nb_samples ) ;
2015-03-18 17:58:23 -05:00
if ( c - > sample_fmt ! = AV_SAMPLE_FMT_S16 ) {
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " sample_fmt %d != AV_SAMPLE_FMT_S16, start resampler \n " , c - > sample_fmt ) ;
2015-04-24 00:20:00 +08:00
mst - > resample_ctx = avresample_alloc_context ( ) ;
2015-03-17 23:52:01 +08:00
2015-04-24 00:20:00 +08:00
if ( ! mst - > resample_ctx ) {
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate resampler context \n " ) ;
return status ;
}
/* set options */
2015-04-24 00:20:00 +08:00
av_opt_set_int ( mst - > resample_ctx , " in_channel_count " , c - > channels , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " in_sample_rate " , c - > sample_rate , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " in_sample_fmt " , AV_SAMPLE_FMT_S16 , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " in_channel_layout " , c - > channel_layout , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " out_channel_count " , c - > channels , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " out_sample_rate " , c - > sample_rate , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " out_sample_fmt " , c - > sample_fmt , 0 ) ;
av_opt_set_int ( mst - > resample_ctx , " out_channel_layout " , c - > channel_layout , 0 ) ;
if ( ( ret = avresample_open ( mst - > resample_ctx ) ) < 0 ) {
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to initialize the resampling context \n " ) ;
2015-07-21 08:37:42 +08:00
av_free ( mst - > resample_ctx ) ;
mst - > resample_ctx = NULL ;
2015-03-17 23:52:01 +08:00
return status ;
}
}
2015-04-24 00:20:00 +08:00
ret = av_frame_get_buffer ( mst - > frame , 0 ) ;
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate audio frame. \n " ) ;
return status ;
}
2015-04-24 00:20:00 +08:00
if ( mst - > resample_ctx ) {
mst - > tmp_frame = av_frame_alloc ( ) ;
switch_assert ( mst - > tmp_frame ) ;
2015-03-17 23:52:01 +08:00
2015-04-24 00:20:00 +08:00
mst - > tmp_frame - > sample_rate = c - > sample_rate ;
mst - > tmp_frame - > format = c - > sample_fmt ;
mst - > tmp_frame - > channel_layout = c - > channel_layout ;
mst - > tmp_frame - > nb_samples = mst - > frame - > nb_samples ;
2015-03-17 23:52:01 +08:00
2015-04-24 00:20:00 +08:00
ret = av_frame_get_buffer ( mst - > tmp_frame , 0 ) ;
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate audio frame. \n " ) ;
return status ;
}
}
return SWITCH_STATUS_SUCCESS ;
}
2015-10-07 14:04:33 -05:00
static int flush_video_queue ( switch_queue_t * q , int min )
{
void * pop ;
if ( switch_queue_size ( q ) > min ) {
while ( switch_queue_trypop ( q , & pop ) = = SWITCH_STATUS_SUCCESS ) {
switch_image_t * img = ( switch_image_t * ) pop ;
switch_img_free ( & img ) ;
if ( min & & switch_queue_size ( q ) < = min ) {
break ;
}
}
}
return switch_queue_size ( q ) ;
}
2015-03-17 23:52:01 +08:00
static void * SWITCH_THREAD_FUNC video_thread_run ( switch_thread_t * thread , void * obj )
{
2017-05-19 17:38:08 -05:00
av_file_context_t * context = ( av_file_context_t * ) obj ;
2015-09-30 22:03:33 -05:00
void * pop = NULL ;
2017-05-25 12:01:18 -05:00
switch_image_t * img = NULL ;
2017-05-19 17:38:08 -05:00
int d_w = context - > eh . video_st - > width , d_h = context - > eh . video_st - > height ;
2015-10-07 14:04:33 -05:00
int size = 0 , skip = 0 , skip_freq = 0 , skip_count = 0 , skip_total = 0 , skip_total_count = 0 ;
2017-06-01 12:51:35 -05:00
uint64_t delta_avg = 0 , delta_sum = 0 , delta_i = 0 , delta = 0 ;
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " video thread start \n " ) ;
2017-05-19 17:38:08 -05:00
switch_assert ( context - > eh . video_queue ) ;
2015-03-17 23:52:01 +08:00
for ( ; ; ) {
AVPacket pkt = { 0 } ;
int got_packet ;
2015-09-30 22:03:33 -05:00
int ret = - 1 ;
2015-03-17 23:52:01 +08:00
2015-10-07 14:04:33 -05:00
top :
2017-05-19 17:38:08 -05:00
switch_assert ( context - > eh . video_queue ) ;
if ( switch_queue_pop ( context - > eh . video_queue , & pop ) = = SWITCH_STATUS_SUCCESS ) {
2015-10-01 16:34:05 -05:00
switch_img_free ( & img ) ;
2015-09-30 22:03:33 -05:00
if ( ! pop ) {
goto endfor ;
2015-03-17 23:52:01 +08:00
}
2015-09-30 22:02:50 -05:00
2017-05-19 17:38:08 -05:00
img = ( switch_image_t * ) pop ;
2015-09-30 22:03:33 -05:00
if ( ! d_w ) d_w = img - > d_w ;
if ( ! d_h ) d_h = img - > d_h ;
2017-05-25 12:01:18 -05:00
//if (d_w && d_h && (d_w != img->d_w || d_h != img->d_h)) {
2015-09-30 22:03:33 -05:00
/* scale to match established stream */
2017-05-25 12:01:18 -05:00
// switch_img_fit(&img, d_w, d_h, SWITCH_FIT_SIZE);
//}
2015-09-30 22:03:33 -05:00
} else {
continue ;
2015-09-24 13:55:21 -05:00
}
2015-10-07 14:04:33 -05:00
if ( skip ) {
if ( ( skip_total_count > 0 & & ! - - skip_total_count ) | | + + skip_count > = skip_freq ) {
skip_total_count = skip_total ;
skip_count = 0 ;
skip - - ;
2016-03-09 18:01:03 -06:00
2015-10-07 14:04:33 -05:00
goto top ;
}
} else {
2017-01-06 02:10:15 -05:00
2017-05-19 17:38:08 -05:00
size = switch_queue_size ( context - > eh . video_queue ) ;
2016-09-09 16:48:34 -05:00
2017-05-19 17:38:08 -05:00
if ( size > 5 & & ! context - > eh . finalize ) {
2015-10-07 14:04:33 -05:00
skip = size ;
if ( size > 10 ) {
skip_freq = 3 ;
skip_total = 1 ;
} else {
skip_freq = 2 ;
skip_total = 1 ;
}
}
}
2017-01-06 02:10:15 -05:00
2017-05-19 17:38:08 -05:00
//switch_mutex_lock(context->eh.mutex);
2015-03-17 23:52:01 +08:00
2017-05-19 17:38:08 -05:00
context - > eh . in_callback = 1 ;
2017-01-06 02:10:15 -05:00
av_init_packet ( & pkt ) ;
2015-03-17 23:52:01 +08:00
2017-05-19 17:38:08 -05:00
if ( context - > eh . video_st - > frame ) {
ret = av_frame_make_writable ( context - > eh . video_st - > frame ) ;
2015-03-17 23:52:01 +08:00
}
2016-03-09 18:01:03 -06:00
if ( ret < 0 ) {
continue ;
}
2015-03-17 23:52:01 +08:00
2017-06-01 12:51:35 -05:00
if ( context - > eh . record_timer_paused ) {
context - > eh . last_ts = 0 ;
continue ;
}
2017-05-19 17:38:08 -05:00
fill_avframe ( context - > eh . video_st - > frame , img ) ;
2017-01-06 02:10:15 -05:00
2017-05-19 17:38:08 -05:00
if ( context - > eh . finalize ) {
2016-09-09 16:48:34 -05:00
if ( delta_i & & ! delta_avg ) {
delta_avg = ( int ) ( double ) ( delta_sum / delta_i ) ;
2017-05-19 17:38:08 -05:00
delta_i = 1 ;
2016-09-09 16:48:34 -05:00
delta_sum = delta_avg ;
}
if ( delta_avg ) {
delta = delta_avg ;
2017-05-19 17:38:08 -05:00
} else if ( context - > eh . mm - > fps ) {
delta = 1000 / context - > eh . mm - > fps ;
2016-09-09 16:48:34 -05:00
} else {
delta = 33 ;
}
2016-03-09 18:01:03 -06:00
2017-11-02 14:03:21 -05:00
context - > eh . video_st - > frame - > pts + = delta * 90 ;
2015-03-17 23:52:01 +08:00
} else {
2016-09-09 16:48:34 -05:00
uint64_t delta_tmp ;
2016-02-13 11:18:15 +08:00
2017-05-31 12:48:46 -05:00
switch_core_timer_sync ( context - > eh . video_timer ) ;
2017-06-01 12:51:35 -05:00
delta_tmp = context - > eh . video_timer - > samplecount - context - > eh . last_ts ;
2017-05-19 17:38:08 -05:00
if ( delta_tmp ! = 0 ) {
2016-09-09 16:48:34 -05:00
delta_sum + = delta_tmp ;
delta_i + + ;
2017-05-19 17:38:08 -05:00
if ( delta_i = = UINT64_MAX ) {
delta_i = 1 ;
2016-09-09 16:48:34 -05:00
delta_sum = delta_avg ;
2016-03-09 18:01:03 -06:00
}
2017-05-19 17:38:08 -05:00
if ( ( delta_i % 10 ) = = 0 ) {
delta_avg = ( int ) ( double ) ( delta_sum / delta_i ) ;
}
2017-06-01 12:51:35 -05:00
2017-11-02 14:03:21 -05:00
context - > eh . video_st - > frame - > pts = context - > eh . video_timer - > samplecount * 90 ;
2017-05-19 17:38:08 -05:00
} else {
2017-11-02 14:03:21 -05:00
context - > eh . video_st - > frame - > pts = ( ( context - > eh . video_timer - > samplecount ) * 90 ) + 1 ;
2016-03-08 18:53:55 -06:00
}
2015-03-17 23:52:01 +08:00
}
2016-02-13 11:18:15 +08:00
2017-06-01 12:51:35 -05:00
context - > eh . last_ts = context - > eh . video_st - > frame - > pts ;
2016-03-08 18:53:55 -06:00
2017-07-14 18:35:23 -05:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pts: %" SWITCH_INT64_T_FMT "\n", context->eh.video_st->frame->pts);
2015-03-17 23:52:01 +08:00
/* encode the image */
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
ret = avcodec_encode_video2 ( context - > eh . video_st - > st - > codec , & pkt , context - > eh . video_st - > frame , & got_packet ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Encoding Error %d \n " , ret ) ;
continue ;
}
if ( got_packet ) {
2017-05-19 17:38:08 -05:00
switch_mutex_lock ( context - > eh . mutex ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
ret = write_frame ( context - > eh . fc , & context - > eh . video_st - > st - > codec - > time_base , context - > eh . video_st - > st , & pkt ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
switch_mutex_unlock ( context - > eh . mutex ) ;
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2015-03-17 23:52:01 +08:00
}
2017-05-19 17:38:08 -05:00
context - > eh . in_callback = 0 ;
//switch_mutex_unlock(context->eh.mutex);
2015-03-17 23:52:01 +08:00
}
2015-09-24 13:55:21 -05:00
endfor :
2016-03-09 00:02:52 -06:00
for ( ; ; ) {
AVPacket pkt = { 0 } ;
int got_packet = 0 ;
int ret = 0 ;
av_init_packet ( & pkt ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
ret = avcodec_encode_video2 ( context - > eh . video_st - > st - > codec , & pkt , NULL , & got_packet ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2016-03-09 00:02:52 -06:00
if ( ret < 0 ) {
break ;
} else if ( got_packet ) {
2017-05-19 17:38:08 -05:00
switch_mutex_lock ( context - > eh . mutex ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
ret = write_frame ( context - > eh . fc , & context - > eh . video_st - > st - > codec - > time_base , context - > eh . video_st - > st , & pkt ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-05-19 17:38:08 -05:00
switch_mutex_unlock ( context - > eh . mutex ) ;
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2016-03-09 00:02:52 -06:00
if ( ret < 0 ) break ;
2016-03-23 20:00:24 +08:00
} else {
break ;
2016-03-09 00:02:52 -06:00
}
}
2017-05-19 17:38:08 -05:00
while ( switch_queue_trypop ( context - > eh . video_queue , & pop ) = = SWITCH_STATUS_SUCCESS ) {
2015-03-17 23:52:01 +08:00
if ( ! pop ) break ;
img = ( switch_image_t * ) pop ;
switch_img_free ( & img ) ;
}
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " video thread done \n " ) ;
return NULL ;
}
2015-04-24 00:20:00 +08:00
static void close_stream ( AVFormatContext * fc , MediaStream * mst )
2015-03-17 23:52:01 +08:00
{
2017-07-14 18:35:23 -05:00
if ( ! mst - > active ) return ;
2015-07-21 08:37:42 +08:00
if ( mst - > resample_ctx ) avresample_free ( & mst - > resample_ctx ) ;
2015-04-24 00:20:00 +08:00
if ( mst - > sws_ctx ) sws_freeContext ( mst - > sws_ctx ) ;
if ( mst - > frame ) av_frame_free ( & mst - > frame ) ;
if ( mst - > tmp_frame ) av_frame_free ( & mst - > tmp_frame ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-06-28 12:16:46 -05:00
if ( mst - > st & & mst - > st - > codec ) {
avcodec_close ( mst - > st - > codec ) ;
}
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
2017-07-14 18:35:23 -05:00
mst - > active = 0 ;
2015-03-17 23:52:01 +08:00
}
static int is_device ( const AVClass * avclass )
{
2015-05-21 15:48:48 -05:00
# if defined (AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT)
2015-03-17 23:52:01 +08:00
if ( ! avclass ) return 0 ;
2017-07-14 18:35:23 -05:00
return avclass - > category = = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT | |
avclass - > category = = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT | |
avclass - > category = = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT | |
avclass - > category = = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT | |
avclass - > category = = AV_CLASS_CATEGORY_DEVICE_OUTPUT | |
2015-03-17 23:52:01 +08:00
avclass - > category = = AV_CLASS_CATEGORY_DEVICE_INPUT ;
2017-07-14 18:35:23 -05:00
# endif
2015-03-17 23:52:01 +08:00
2017-07-14 18:35:23 -05:00
return 0 ;
2015-03-17 23:52:01 +08:00
}
void show_formats ( switch_stream_handle_t * stream ) {
AVInputFormat * ifmt = NULL ;
AVOutputFormat * ofmt = NULL ;
const char * last_name ;
// int is_dev;
stream - > write_function ( stream , " ============= File Formats ==============================: \n "
" D. = Demuxing supported \n "
" .M = Muxing supported \n "
" ---------------------- \n " ) ;
last_name = " 000 " ;
for ( ; ; ) {
int decode = 0 ;
int encode = 0 ;
int is_dev = 0 ;
const char * name = NULL ;
const char * long_name = NULL ;
while ( ( ofmt = av_oformat_next ( ofmt ) ) ) {
is_dev = is_device ( ofmt - > priv_class ) ;
if ( ( name = = NULL | | strcmp ( ofmt - > name , name ) < 0 ) & &
strcmp ( ofmt - > name , last_name ) > 0 ) {
name = ofmt - > name ;
long_name = ofmt - > long_name ;
encode = 1 ;
}
}
while ( ( ifmt = av_iformat_next ( ifmt ) ) ) {
is_dev = is_device ( ifmt - > priv_class ) ;
if ( ( name = = NULL | | strcmp ( ifmt - > name , name ) < 0 ) & &
strcmp ( ifmt - > name , last_name ) > 0 ) {
name = ifmt - > name ;
long_name = ifmt - > long_name ;
encode = 0 ;
}
if ( name & & strcmp ( ifmt - > name , name ) = = 0 ) decode = 1 ;
}
if ( name = = NULL ) break ;
last_name = name ;
stream - > write_function ( stream , " %s%s%s %-15s %s \n " ,
is_dev ? " * " : " " ,
decode ? " D " : " " ,
encode ? " M " : " " ,
name , long_name ? long_name : " " ) ;
}
}
2015-08-26 08:48:23 +08:00
static void mod_avformat_destroy_output_context ( av_file_context_t * context )
{
2017-06-28 12:16:46 -05:00
close_stream ( context - > fc , & context - > video_st ) ;
2017-07-14 18:35:23 -05:00
close_stream ( context - > fc , & context - > audio_st [ 0 ] ) ;
close_stream ( context - > fc , & context - > audio_st [ 1 ] ) ;
2015-08-26 08:48:23 +08:00
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 0 ] . resample_ctx ) {
avresample_free ( & context - > audio_st [ 0 ] . resample_ctx ) ;
}
if ( context - > audio_st [ 1 ] . resample_ctx ) {
avresample_free ( & context - > audio_st [ 1 ] . resample_ctx ) ;
2015-08-26 08:48:23 +08:00
}
avformat_close_input ( & context - > fc ) ;
2017-06-28 12:16:46 -05:00
context - > fc = NULL ;
2017-07-14 18:35:23 -05:00
context - > audio_st [ 0 ] . st = NULL ;
context - > audio_st [ 1 ] . st = NULL ;
2017-06-28 12:16:46 -05:00
context - > video_st . st = NULL ;
2015-08-26 08:48:23 +08:00
}
2015-04-24 00:20:00 +08:00
static switch_status_t open_input_file ( av_file_context_t * context , switch_file_handle_t * handle , const char * filename )
{
2015-04-26 15:59:54 -05:00
AVCodec * audio_codec = NULL ;
2015-09-30 09:23:30 +02:00
AVCodec * video_codec = NULL ;
2017-03-06 08:43:31 +08:00
AVDictionary * opts = NULL ;
2015-04-24 00:20:00 +08:00
int error ;
2017-07-14 18:35:23 -05:00
int i , idx = 0 ;
2015-04-24 00:20:00 +08:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2017-03-06 08:43:31 +08:00
// av_dict_set(&opts, "c:v", "libvpx", 0);
2015-04-24 00:20:00 +08:00
/** Open the input file to read from it. */
2017-03-06 08:43:31 +08:00
if ( ( error = avformat_open_input ( & context - > fc , filename , NULL , NULL ) ) < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open input file '%s' (error '%s') \n " , filename , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2015-04-24 00:20:00 +08:00
switch_goto_status ( SWITCH_STATUS_FALSE , err ) ;
}
2016-02-13 11:18:15 +08:00
handle - > seekable = context - > fc - > iformat - > read_seek2 ? 1 : ( context - > fc - > iformat - > read_seek ? 1 : 0 ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " file %s is %sseekable \n " , filename , handle - > seekable ? " " : " not " ) ;
2015-04-24 00:20:00 +08:00
/** Get information on the input file (number of streams etc.). */
2017-03-06 08:43:31 +08:00
if ( ( error = avformat_find_stream_info ( context - > fc , opts ? & opts : NULL ) ) < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open find stream info (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2017-03-06 08:43:31 +08:00
if ( opts ) av_dict_free ( & opts ) ;
2015-04-24 00:20:00 +08:00
switch_goto_status ( SWITCH_STATUS_FALSE , err ) ;
}
2017-03-06 08:43:31 +08:00
if ( opts ) av_dict_free ( & opts ) ;
2015-04-24 00:20:00 +08:00
av_dump_format ( context - > fc , 0 , filename , 0 ) ;
for ( i = 0 ; i < context - > fc - > nb_streams ; i + + ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
if ( context - > fc - > streams [ i ] - > codec - > codec_type = = AVMEDIA_TYPE_AUDIO & & context - > has_audio < 2 & & idx < 2 ) {
context - > audio_st [ idx + + ] . st = context - > fc - > streams [ i ] ;
context - > has_audio + + ;
2015-04-24 00:20:00 +08:00
} else if ( context - > fc - > streams [ i ] - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO & & ! context - > has_video ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
context - > video_st . st = context - > fc - > streams [ i ] ;
2015-05-05 16:02:26 +08:00
if ( switch_test_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) ) {
context - > has_video = 1 ;
2016-02-18 00:10:07 +08:00
handle - > duration = av_rescale_q ( context - > video_st . st - > duration , context - > video_st . st - > time_base , AV_TIME_BASE_Q ) ;
2015-05-05 16:02:26 +08:00
}
2017-03-15 15:28:08 -05:00
if ( context - > video_st . st - > avg_frame_rate . num ) {
handle - > mm . source_fps = ceil ( av_q2d ( context - > video_st . st - > avg_frame_rate ) ) ;
} else {
handle - > mm . source_fps = 25 ;
}
2015-09-30 11:42:40 -05:00
context - > read_fps = ( int ) handle - > mm . source_fps ;
2015-04-24 00:20:00 +08:00
}
}
/** Find a decoder for the audio stream. */
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
if ( context - > has_audio & & ! ( audio_codec = avcodec_find_decoder ( context - > audio_st [ 0 ] . st - > codec - > codec_id ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Could not find input codec %d \n " , context - > audio_st [ 0 ] . st - > codec - > codec_id ) ;
2015-04-24 00:20:00 +08:00
context - > has_audio = 0 ;
}
if ( context - > has_video & & ! ( video_codec = avcodec_find_decoder ( context - > video_st . st - > codec - > codec_id ) ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not find input codec %d \n " , context - > video_st . st - > codec - > codec_id ) ;
context - > has_video = 0 ;
}
2017-07-14 18:35:23 -05:00
if ( context - > has_audio & & ( error = avcodec_open2 ( context - > audio_st [ 0 ] . st - > codec , audio_codec , NULL ) ) < 0 ) {
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open input audio codec (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
context - > has_audio = 0 ;
}
if ( context - > has_audio = = 2 & & ( error = avcodec_open2 ( context - > audio_st [ 1 ] . st - > codec , audio_codec , NULL ) ) < 0 ) {
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open input audio codec channel 2 (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
if ( context - > audio_st [ 0 ] . st - > codec ) {
avcodec_close ( context - > audio_st [ 0 ] . st - > codec ) ;
}
2015-04-24 00:20:00 +08:00
context - > has_audio = 0 ;
}
if ( context - > has_video & & ( error = avcodec_open2 ( context - > video_st . st - > codec , video_codec , NULL ) ) < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open input codec (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2015-04-24 00:20:00 +08:00
context - > has_video = 0 ;
}
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
2017-07-14 18:35:23 -05:00
context - > video_st . active = 1 ;
2015-04-24 00:20:00 +08:00
// printf("has audio:%d has_video:%d\n", context->has_audio, context->has_video);
if ( ( ! context - > has_audio ) & & ( ! context - > has_video ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Neither audio nor video stream found in file %s \n " , filename ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , err ) ;
}
if ( context - > has_audio ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
AVCodecContext * c [ 2 ] = { NULL } ;
c [ 0 ] = context - > audio_st [ 0 ] . st - > codec ;
if ( context - > audio_st [ 1 ] . st & & context - > audio_st [ 1 ] . st - > codec ) {
c [ 1 ] = context - > audio_st [ 1 ] . st - > codec ;
}
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
context - > audio_st [ 0 ] . frame = av_frame_alloc ( ) ;
switch_assert ( context - > audio_st [ 0 ] . frame ) ;
context - > audio_st [ 0 ] . active = 1 ;
2015-04-26 15:59:54 -05:00
2017-07-14 18:35:23 -05:00
if ( c [ 1 ] ) {
context - > audio_st [ 1 ] . frame = av_frame_alloc ( ) ;
switch_assert ( context - > audio_st [ 1 ] . frame ) ;
}
if ( c [ 0 ] & & c [ 1 ] ) {
context - > audio_st [ 0 ] . channels = 1 ;
context - > audio_st [ 1 ] . channels = 1 ;
} else {
handle - > channels = c [ 0 ] - > channels > 2 ? 2 : c [ 0 ] - > channels ;
context - > audio_st [ 0 ] . channels = handle - > channels ;
}
2015-04-24 00:20:00 +08:00
2017-07-14 18:35:23 -05:00
context - > audio_st [ 0 ] . sample_rate = handle - > samplerate ;
context - > audio_st [ 1 ] . sample_rate = handle - > samplerate ;
2015-04-24 00:20:00 +08:00
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 0 ] . st - > codec - > sample_fmt ! = AV_SAMPLE_FMT_S16 ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
int x ;
for ( x = 0 ; x < context - > has_audio & & x < 2 & & c [ x ] ; x + + ) {
AVAudioResampleContext * resample_ctx = avresample_alloc_context ( ) ;
2015-04-24 00:20:00 +08:00
2017-07-14 18:35:23 -05:00
if ( resample_ctx ) {
int ret ;
av_opt_set_int ( resample_ctx , " in_channel_count " , c [ x ] - > channels , 0 ) ;
av_opt_set_int ( resample_ctx , " in_sample_rate " , c [ x ] - > sample_rate , 0 ) ;
av_opt_set_int ( resample_ctx , " in_sample_fmt " , c [ x ] - > sample_fmt , 0 ) ;
av_opt_set_int ( resample_ctx , " in_channel_layout " ,
( c [ x ] - > channel_layout = = 0 & & c [ x ] - > channels = = 2 ) ? AV_CH_LAYOUT_STEREO : c [ x ] - > channel_layout , 0 ) ;
av_opt_set_int ( resample_ctx , " out_channel_count " , handle - > channels , 0 ) ;
av_opt_set_int ( resample_ctx , " out_sample_rate " , handle - > samplerate , 0 ) ;
av_opt_set_int ( resample_ctx , " out_sample_fmt " , AV_SAMPLE_FMT_S16 , 0 ) ;
av_opt_set_int ( resample_ctx , " out_channel_layout " , handle - > channels = = 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO , 0 ) ;
if ( ( ret = avresample_open ( resample_ctx ) ) < 0 ) {
char errbuf [ 1024 ] ;
av_strerror ( ret , errbuf , 1024 ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Failed to initialize the resampling context, ret=%d: %s \n " , ret , errbuf ) ;
av_free ( resample_ctx ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , err ) ;
}
context - > audio_st [ x ] . resample_ctx = resample_ctx ;
}
2015-04-24 00:20:00 +08:00
}
}
}
2016-11-08 13:45:47 -06:00
if ( ! context - > has_video ) {
switch_clear_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) ;
2017-03-16 10:00:33 -05:00
} else {
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-03-16 10:00:33 -05:00
switch ( context - > video_st . st - > codec - > pix_fmt ) {
2017-03-16 14:54:37 -05:00
case AV_PIX_FMT_YUVA420P :
case AV_PIX_FMT_RGBA :
case AV_PIX_FMT_ARGB :
case AV_PIX_FMT_BGRA :
context - > handle - > mm . fmt = SWITCH_IMG_FMT_ARGB ;
break ;
2017-03-16 10:00:33 -05:00
default :
context - > handle - > mm . fmt = SWITCH_IMG_FMT_I420 ;
2017-03-16 14:54:37 -05:00
break ;
2017-03-16 10:00:33 -05:00
}
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-03-16 14:54:37 -05:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG ,
" Opening file in mode: %s \n " , context - > handle - > mm . fmt = = SWITCH_IMG_FMT_ARGB ? " ARGB " : " I420 " ) ;
2016-11-08 13:45:47 -06:00
}
2015-04-24 00:20:00 +08:00
return status ;
err :
2015-07-21 08:37:42 +08:00
/*
if ( context - > has_video ) close_stream ( context - > fc , & context - > video_st ) ;
if ( context - > has_audio ) close_stream ( context - > fc , & context - > audio_st ) ;
2015-04-24 00:20:00 +08:00
if ( context - > fc ) avformat_close_input ( & context - > fc ) ;
2015-07-21 08:37:42 +08:00
*/
2015-04-24 00:20:00 +08:00
return status ;
}
2016-02-23 22:01:38 -06:00
//#define ALT_WAY
# define AUDIO_BUF_SEC 5
2015-04-24 00:20:00 +08:00
static void * SWITCH_THREAD_FUNC file_read_thread_run ( switch_thread_t * thread , void * obj )
{
av_file_context_t * context = ( av_file_context_t * ) obj ;
AVPacket pkt = { 0 } ;
int got_data = 0 ;
int error ;
2015-09-30 22:03:33 -05:00
int sync = 0 ;
2016-02-23 22:01:38 -06:00
int eof = 0 ;
2015-04-24 00:20:00 +08:00
2016-08-06 02:25:11 +00:00
switch_mutex_lock ( context - > mutex ) ;
context - > file_read_thread_started = 1 ;
2015-04-24 00:20:00 +08:00
context - > file_read_thread_running = 1 ;
2016-08-06 02:25:11 +00:00
switch_thread_cond_signal ( context - > cond ) ;
switch_mutex_unlock ( context - > mutex ) ;
2015-04-24 00:20:00 +08:00
2016-02-23 22:01:38 -06:00
while ( context - > file_read_thread_running & & ! context - > closed ) {
int vid_frames = 0 ;
2016-02-13 11:18:15 +08:00
if ( context - > seek_ts > = 0 ) {
int stream_id = - 1 ;
switch_mutex_lock ( context - > mutex ) ;
switch_buffer_zero ( context - > audio_buffer ) ;
switch_mutex_unlock ( context - > mutex ) ;
2017-02-21 20:09:32 -06:00
2016-02-13 11:18:15 +08:00
// if (context->has_audio) stream_id = context->audio_st.st->index;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " seeking to % " SWITCH_INT64_T_FMT " \n " , context - > seek_ts ) ;
2016-03-25 16:37:34 +00:00
avformat_seek_file ( context - > fc , stream_id , 0 , context - > seek_ts , INT64_MAX , 0 ) ;
2016-03-27 16:08:40 +08:00
context - > seek_ts = - 2 ;
2016-02-13 11:18:15 +08:00
2017-05-15 17:28:04 -05:00
if ( context - > has_video ) {
2017-06-30 11:28:49 -05:00
void * pop ;
2017-05-15 17:28:04 -05:00
context - > video_st . next_pts = 0 ;
context - > video_start_time = 0 ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-05-15 17:28:04 -05:00
avcodec_flush_buffers ( context - > video_st . st - > codec ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-06-30 11:28:49 -05:00
while ( switch_queue_trypop ( context - > eh . video_queue , & pop ) = = SWITCH_STATUS_SUCCESS ) {
switch_image_t * img ;
if ( ! pop ) break ;
img = ( switch_image_t * ) pop ;
switch_img_free ( & img ) ;
}
2017-05-15 17:28:04 -05:00
}
2016-02-13 11:18:15 +08:00
}
2016-02-23 22:01:38 -06:00
if ( context - > has_video ) {
vid_frames = switch_queue_size ( context - > eh . video_queue ) ;
}
2017-02-21 20:09:32 -06:00
2017-07-14 18:35:23 -05:00
if ( switch_buffer_inuse ( context - > audio_buffer ) > AUDIO_BUF_SEC * context - > handle - > samplerate * context - > handle - > channels * 2 & &
2016-02-23 22:01:38 -06:00
( ! context - > has_video | | vid_frames > 5 ) ) {
switch_yield ( context - > has_video ? 1000 : 10000 ) ;
2015-04-24 00:20:00 +08:00
continue ;
}
2016-02-23 22:01:38 -06:00
2017-02-21 20:09:32 -06:00
2015-04-24 00:20:00 +08:00
av_init_packet ( & pkt ) ;
pkt . data = NULL ;
pkt . size = 0 ;
2016-10-24 08:10:36 +08:00
if ( ( error = av_read_frame ( context - > fc , & pkt ) ) < 0 ) {
2016-02-23 22:01:38 -06:00
if ( error = = AVERROR_EOF ) {
2016-05-11 14:49:21 +08:00
if ( ! context - > has_video ) break ;
2016-02-23 22:01:38 -06:00
eof = 1 ;
/* just make sure*/
pkt . data = NULL ;
pkt . size = 0 ;
pkt . stream_index = context - > video_st . st - > index ;
} else {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not read frame (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2016-02-23 22:01:38 -06:00
break ;
}
2015-04-24 00:20:00 +08:00
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "stream: %d, pkt size %d\n", pkt.stream_index, pkt.size);
if ( context - > has_video & & pkt . stream_index = = context - > video_st . st - > index ) {
2016-02-23 22:01:38 -06:00
AVFrame * vframe ;
2015-04-24 00:20:00 +08:00
switch_image_t * img ;
2015-09-30 22:03:33 -05:00
if ( ! sync ) {
switch_buffer_zero ( context - > audio_buffer ) ;
sync = 1 ;
}
2016-02-23 22:01:38 -06:00
again :
vframe = av_frame_alloc ( ) ;
2015-04-24 00:20:00 +08:00
switch_assert ( vframe ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
if ( ( error = avcodec_decode_video2 ( context - > video_st . st - > codec , vframe , & got_data , & pkt ) ) < 0 ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not decode frame (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2015-04-24 00:20:00 +08:00
av_frame_free ( & vframe ) ;
break ;
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, pts: %lld dts: %lld\n", pkt.size, pkt.pts, pkt.dts);
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2015-04-24 00:20:00 +08:00
2015-09-30 22:03:33 -05:00
//if (switch_queue_size(context->eh.video_queue) > 300) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Dropping frames\n");
// av_frame_free(&vframe);
// continue;
//}
2017-03-06 08:43:31 +08:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got_data=%d, error=%d\n", got_data, error);
2015-04-24 00:20:00 +08:00
2016-02-23 22:01:38 -06:00
if ( got_data & & error > = 0 ) {
2017-03-06 08:43:31 +08:00
switch_img_fmt_t fmt = SWITCH_IMG_FMT_I420 ;
2017-03-15 15:28:08 -05:00
if ( (
2017-03-06 08:43:31 +08:00
vframe - > format = = AV_PIX_FMT_YUVA420P | |
vframe - > format = = AV_PIX_FMT_RGBA | |
vframe - > format = = AV_PIX_FMT_ARGB | |
vframe - > format = = AV_PIX_FMT_BGRA ) ) {
fmt = SWITCH_IMG_FMT_ARGB ;
} else if ( vframe - > format ! = AV_PIX_FMT_YUV420P ) {
2015-04-24 00:20:00 +08:00
AVFrame * frm = vframe ;
int ret ;
if ( ! context - > video_st . sws_ctx ) {
context - > video_st . sws_ctx =
sws_getContext ( frm - > width , frm - > height ,
frm - > format ,
frm - > width , frm - > height ,
AV_PIX_FMT_YUV420P ,
SCALE_FLAGS , NULL , NULL , NULL ) ;
if ( ! context - > video_st . sws_ctx ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Cannot init sws context \n " ) ;
av_frame_free ( & frm ) ;
continue ;
}
}
vframe = av_frame_alloc ( ) ;
switch_assert ( vframe ) ;
vframe - > format = AV_PIX_FMT_YUV420P ;
vframe - > width = frm - > width ;
vframe - > height = frm - > height ;
vframe - > pts = frm - > pts ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
vframe - > pkt_pts = frm - > pkt_pts ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
vframe - > pkt_dts = frm - > pkt_dts ;
ret = av_frame_get_buffer ( vframe , 32 ) ;
switch_assert ( ret > = 0 ) ;
ret = sws_scale ( context - > video_st . sws_ctx , ( const uint8_t * const * ) frm - > data , frm - > linesize ,
0 , frm - > height , vframe - > data , vframe - > linesize ) ;
av_frame_free ( & frm ) ;
if ( ret < = 0 ) {
av_frame_free ( & vframe ) ;
continue ;
}
}
2017-03-16 10:00:33 -05:00
2017-03-15 15:28:08 -05:00
context - > handle - > mm . fmt = fmt ;
2015-04-24 00:20:00 +08:00
2017-03-06 08:43:31 +08:00
img = switch_img_alloc ( NULL , fmt , vframe - > width , vframe - > height , 1 ) ;
2017-02-21 15:52:53 -06:00
2015-04-24 00:20:00 +08:00
if ( img ) {
2016-02-13 11:18:15 +08:00
int64_t * pts = malloc ( sizeof ( int64_t ) ) ;
2015-04-24 00:20:00 +08:00
if ( pts ) {
2016-02-23 22:01:38 -06:00
# ifdef ALT_WAY
int diff ;
int sleep = 66000 ;
# endif
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
* pts = vframe - > pkt_pts ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
avframe2img ( vframe , img ) ;
img - > user_priv = pts ;
2017-01-06 02:10:15 -05:00
2016-02-23 22:01:38 -06:00
# ifdef ALT_WAY
diff = sleep - ( switch_time_now ( ) - context - > last_vid_push ) ;
2017-01-06 02:10:15 -05:00
2016-02-23 22:01:38 -06:00
if ( diff > 0 & & diff < = sleep ) {
switch_core_timer_next ( & context - > video_timer ) ;
} else {
switch_core_timer_sync ( & context - > video_timer ) ;
}
2017-01-06 02:10:15 -05:00
# endif
2016-02-23 22:01:38 -06:00
2016-02-29 17:00:42 -06:00
context - > vid_ready = 1 ;
2015-04-24 00:20:00 +08:00
switch_queue_push ( context - > eh . video_queue , img ) ;
2016-02-23 22:01:38 -06:00
context - > last_vid_push = switch_time_now ( ) ;
2017-05-18 13:42:40 -05:00
2015-04-24 00:20:00 +08:00
}
}
}
2017-03-06 08:43:31 +08:00
2015-07-21 08:37:42 +08:00
av_frame_free ( & vframe ) ;
2016-02-23 22:01:38 -06:00
if ( eof ) {
if ( got_data ) {
goto again ; // to get all delayed video frames in decoder
} else {
break ;
}
}
2015-04-24 00:20:00 +08:00
continue ;
2017-07-14 18:35:23 -05:00
} else if ( context - > has_audio & & pkt . stream_index = = context - > audio_st [ 0 ] . st - > index ) {
2015-04-24 00:20:00 +08:00
AVFrame in_frame = { { 0 } } ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
if ( ( error = avcodec_decode_audio4 ( context - > audio_st [ 0 ] . st - > codec , & in_frame , & got_data , & pkt ) ) < 0 ) {
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not decode frame (error '%s') \n " , get_error_text ( error , ebuf , sizeof ( ebuf ) ) ) ;
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2015-04-24 00:20:00 +08:00
break ;
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "pkt: %d, decodedddd: %d pts: %lld dts: %lld\n", pkt.size, error, pkt.pts, pkt.dts);
2016-03-25 09:53:38 +08:00
av_packet_unref ( & pkt ) ;
2015-04-24 00:20:00 +08:00
if ( got_data ) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got data frm->format: %d samples: %d\n", in_frame.format, in_frame.nb_samples);
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 0 ] . resample_ctx ) {
int out_samples = avresample_get_out_samples ( context - > audio_st [ 0 ] . resample_ctx , in_frame . nb_samples ) ;
2015-04-24 00:20:00 +08:00
int ret ;
uint8_t * data [ 2 ] = { 0 } ;
2017-07-14 18:35:23 -05:00
data [ 0 ] = malloc ( out_samples * context - > audio_st [ 0 ] . channels * 2 ) ;
2015-04-24 00:20:00 +08:00
switch_assert ( data [ 0 ] ) ;
2017-07-14 18:35:23 -05:00
ret = avresample_convert ( context - > audio_st [ 0 ] . resample_ctx , data , 0 , out_samples ,
2015-04-24 00:20:00 +08:00
in_frame . data , 0 , in_frame . nb_samples ) ;
2017-07-14 18:35:23 -05:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "out_samples: %d ret: %d delay: %d buffer: %zu\n", out_samples, ret, avresample_get_delay(context->audio_st[0].resample_ctx), switch_buffer_inuse(context->audio_buffer));
2015-04-24 00:20:00 +08:00
if ( ret ) {
switch_mutex_lock ( context - > mutex ) ;
2017-07-14 18:35:23 -05:00
switch_buffer_write ( context - > audio_buffer , data [ 0 ] , ret * 2 * context - > audio_st [ 0 ] . channels ) ;
2015-04-24 00:20:00 +08:00
switch_mutex_unlock ( context - > mutex ) ;
}
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "decoded samples: %d\n", ret);
free ( data [ 0 ] ) ;
2017-07-14 18:35:23 -05:00
// if (ret == 0 && avresample_get_delay(context->audio_st[0].resample_ctx)) {
2015-04-24 00:20:00 +08:00
// frameP = NULL;
// goto again;
// }
} else {
2015-05-21 15:48:48 -05:00
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "this block is not tested samples: %d\n", in_frame.nb_samples);
2015-04-24 00:20:00 +08:00
switch_mutex_lock ( context - > mutex ) ;
2017-07-14 18:35:23 -05:00
switch_buffer_write ( context - > audio_buffer , in_frame . data [ 0 ] , in_frame . nb_samples * 2 * context - > audio_st [ 0 ] . channels ) ;
2015-04-24 00:20:00 +08:00
switch_mutex_unlock ( context - > mutex ) ;
}
}
}
}
if ( context - > has_video ) switch_queue_push ( context - > eh . video_queue , NULL ) ;
context - > file_read_thread_running = 0 ;
return NULL ;
}
2015-03-17 23:52:01 +08:00
static switch_status_t av_file_open ( switch_file_handle_t * handle , const char * path )
{
2015-07-21 08:37:42 +08:00
av_file_context_t * context = NULL ;
2015-03-17 23:52:01 +08:00
char * ext ;
const char * tmp = NULL ;
AVOutputFormat * fmt ;
const char * format = NULL ;
int ret ;
char file [ 1024 ] ;
2017-08-31 08:11:11 +08:00
int disable_write_buffer = 0 ;
2015-07-21 08:37:42 +08:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2015-03-17 23:52:01 +08:00
switch_set_string ( file , path ) ;
2017-01-06 02:10:15 -05:00
2017-08-30 19:26:19 -05:00
if ( handle - > stream_name ) {
disable_write_buffer = 1 ;
}
2015-03-17 23:52:01 +08:00
if ( ( ext = strrchr ( ( char * ) path , ' . ' ) ) = = 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Invalid Format \n " ) ;
return SWITCH_STATUS_GENERR ;
} else if ( handle - > stream_name & & ( ! strcasecmp ( handle - > stream_name , " rtmp " ) | | ! strcasecmp ( handle - > stream_name , " youtube " ) ) ) {
format = " flv " ;
2017-04-05 16:32:04 -05:00
// meh really silly format for the user / pass libav.....
if ( handle - > mm . auth_username & & handle - > mm . auth_password ) {
switch_snprintf ( file , sizeof ( file ) , " rtmp://%s pubUser=%s pubPasswd=%s flashver=FMLE/3.0 " , path , handle - > mm . auth_username , handle - > mm . auth_password ) ;
} else {
switch_snprintf ( file , sizeof ( file ) , " rtmp://%s " , path ) ;
}
} else if ( handle - > stream_name & & ! strcasecmp ( handle - > stream_name , " rtsp " ) ) {
format = " rtsp " ;
switch_snprintf ( file , sizeof ( file ) , " rtsp://%s " , path ) ;
2017-08-31 08:11:11 +08:00
disable_write_buffer = 1 ;
2015-03-17 23:52:01 +08:00
}
2017-04-05 16:32:04 -05:00
2015-03-17 23:52:01 +08:00
ext + + ;
if ( ( context = ( av_file_context_t * ) switch_core_alloc ( handle - > memory_pool , sizeof ( av_file_context_t ) ) ) = = 0 ) {
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_MEMERR , end ) ;
2015-03-17 23:52:01 +08:00
}
memset ( context , 0 , sizeof ( av_file_context_t ) ) ;
2015-07-21 08:37:42 +08:00
handle - > private_info = context ;
context - > pool = handle - > memory_pool ;
2016-02-13 11:18:15 +08:00
context - > seek_ts = - 1 ;
2015-04-09 18:18:45 -05:00
context - > offset = DFT_RECORD_OFFSET ;
2017-02-21 15:52:53 -06:00
context - > handle = handle ;
2017-03-06 08:43:31 +08:00
if ( handle - > params ) {
if ( ( tmp = switch_event_get_header ( handle - > params , " av_video_offset " ) ) ) {
context - > offset = atoi ( tmp ) ;
}
2015-03-17 23:52:01 +08:00
}
switch_mutex_init ( & context - > mutex , SWITCH_MUTEX_NESTED , handle - > memory_pool ) ;
2016-08-06 02:25:11 +00:00
switch_thread_cond_create ( & context - > cond , handle - > memory_pool ) ;
2015-04-01 19:36:18 -05:00
switch_buffer_create_dynamic ( & context - > audio_buffer , 512 , 512 , 0 ) ;
2015-03-17 23:52:01 +08:00
2015-07-07 11:50:07 -04:00
if ( ! context - > audio_buffer ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not allocate buffer for %s \n " , path ) ;
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_MEMERR , end ) ;
2015-07-07 11:50:07 -04:00
}
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " sample rate: %d, channels: %d \n " , handle - > samplerate , handle - > channels ) ;
av_register_all ( ) ;
2015-04-24 00:20:00 +08:00
if ( switch_test_flag ( handle , SWITCH_FILE_FLAG_READ ) ) {
if ( open_input_file ( context , handle , path ) ! = SWITCH_STATUS_SUCCESS ) {
//clean up;
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_GENERR , end ) ;
2015-04-24 00:20:00 +08:00
}
if ( context - > has_video ) {
2017-05-18 13:42:40 -05:00
switch_queue_create ( & context - > eh . video_queue , context - > read_fps , handle - > memory_pool ) ;
2015-04-24 00:20:00 +08:00
switch_mutex_init ( & context - > eh . mutex , SWITCH_MUTEX_NESTED , handle - > memory_pool ) ;
2017-05-25 12:01:18 -05:00
switch_core_timer_init ( & context - > video_timer , " soft " , ( int ) ( 1000.0f / context - > read_fps ) , 1 , context - > pool ) ;
2015-04-24 00:20:00 +08:00
}
{
switch_threadattr_t * thd_attr = NULL ;
switch_threadattr_create ( & thd_attr , handle - > memory_pool ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
switch_thread_create ( & context - > file_read_thread , thd_attr , file_read_thread_run , context , handle - > memory_pool ) ;
}
return SWITCH_STATUS_SUCCESS ;
}
2015-05-04 15:49:22 -04:00
mod_avformat_alloc_output_context2 ( & context - > fc , NULL , format , ( char * ) file ) ;
2015-04-24 00:20:00 +08:00
if ( ! context - > fc ) {
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Could not deduce output format from file extension \n " ) ;
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_GENERR , end ) ;
2015-03-17 23:52:01 +08:00
}
2015-04-24 00:20:00 +08:00
fmt = context - > fc - > oformat ;
2015-03-17 23:52:01 +08:00
2017-07-10 12:38:34 -05:00
if ( handle - > params & & ( tmp = switch_event_get_header ( handle - > params , " av_audio_codec " ) ) ) {
2017-06-30 12:32:04 -05:00
if ( ( context - > audio_codec = avcodec_find_encoder_by_name ( tmp ) ) ) {
fmt - > audio_codec = context - > audio_codec - > id ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " specified audio codec %s %s [%s] \n " ,
tmp , context - > audio_codec - > name , context - > audio_codec - > long_name ) ;
2017-07-11 12:45:57 -05:00
if ( ! strcasecmp ( tmp , " pcm_mulaw " ) ) {
2017-07-14 18:35:23 -05:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " specified audio codec requires 8000hz \n " ) ;
2017-07-11 12:45:57 -05:00
handle - > mm . samplerate = 8000 ;
handle - > mm . ab = 64 ;
2017-07-14 18:35:23 -05:00
if ( ! switch_event_get_header ( handle - > params , " channelsplit " ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " specified audio codec requires mono \n " ) ;
handle - > real_channels = handle - > channels ;
handle - > channels = 1 ;
handle - > mm . channels = 1 ;
}
2017-07-11 12:45:57 -05:00
}
}
}
if ( handle - > params & & ( tmp = switch_event_get_header ( handle - > params , " av_video_codec " ) ) ) {
if ( ( context - > video_codec = avcodec_find_encoder_by_name ( tmp ) ) ) {
fmt - > video_codec = context - > video_codec - > id ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " specified video codec %s %s [%s] \n " ,
tmp , context - > video_codec - > name , context - > video_codec - > long_name ) ;
2017-06-30 12:32:04 -05:00
}
}
2017-07-10 12:38:34 -05:00
if ( ! strcasecmp ( ext , " wav " ) | | ( handle - > params & & switch_true ( switch_event_get_header ( handle - > params , " av_record_audio_only " ) ) ) ) {
2017-06-30 12:32:04 -05:00
context - > has_video = 0 ;
switch_clear_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) ;
}
2015-03-17 23:52:01 +08:00
/* open the output file, if needed */
if ( ! ( fmt - > flags & AVFMT_NOFILE ) ) {
2015-04-24 00:20:00 +08:00
ret = avio_open ( & context - > fc - > pb , file , AVIO_FLAG_WRITE ) ;
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Could not open '%s': %s \n " , file , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_GENERR , end ) ;
2015-03-17 23:52:01 +08:00
}
} else {
avformat_network_init ( ) ;
}
2015-04-01 19:36:18 -05:00
if ( handle - > mm . samplerate ) {
2017-07-11 12:45:57 -05:00
handle - > samplerate = handle - > mm . samplerate ;
} else {
2015-04-01 19:36:18 -05:00
handle - > mm . samplerate = handle - > samplerate ;
}
if ( ! handle - > mm . ab ) {
handle - > mm . ab = 128 ;
}
2016-09-07 16:48:55 -05:00
if ( ! handle - > mm . vb ) {
handle - > mm . vb = switch_calc_bitrate ( handle - > mm . vw , handle - > mm . vh , 1 , handle - > mm . fps ) ;
}
2016-03-09 18:01:03 -06:00
2017-07-11 12:45:57 -05:00
if ( switch_test_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) & & fmt - > video_codec ! = AV_CODEC_ID_NONE ) {
2015-03-17 23:52:01 +08:00
const AVCodecDescriptor * desc ;
2017-07-11 12:45:57 -05:00
if ( ( handle - > stream_name & & ( ! strcasecmp ( handle - > stream_name , " rtmp " ) | | ! strcasecmp ( handle - > stream_name , " youtube " ) ) ) ) {
2016-02-13 11:18:15 +08:00
2015-03-17 23:52:01 +08:00
if ( fmt - > video_codec ! = AV_CODEC_ID_H264 ) {
fmt - > video_codec = AV_CODEC_ID_H264 ; // force H264
}
2016-03-09 18:01:03 -06:00
2015-04-20 15:35:58 -05:00
fmt - > audio_codec = AV_CODEC_ID_AAC ;
2015-04-21 18:50:45 -05:00
handle - > samplerate = 44100 ;
2015-03-18 17:58:23 -05:00
handle - > mm . samplerate = 44100 ;
handle - > mm . ab = 128 ;
2017-07-14 18:35:23 -05:00
handle - > mm . cbr = 1 ;
handle - > mm . vencspd = SWITCH_VIDEO_ENCODE_SPEED_FAST ;
2015-09-30 11:42:40 -05:00
handle - > mm . vprofile = SWITCH_VIDEO_PROFILE_BASELINE ;
2015-03-18 17:58:23 -05:00
2015-04-20 15:35:58 -05:00
if ( ! handle - > mm . vb & & handle - > mm . vw & & handle - > mm . vh ) {
2015-03-18 17:58:23 -05:00
switch ( handle - > mm . vh ) {
case 240 :
handle - > mm . vb = 400 ;
break ;
case 360 :
handle - > mm . vb = 750 ;
break ;
case 480 :
handle - > mm . vb = 1000 ;
break ;
case 720 :
handle - > mm . vb = 2500 ;
break ;
case 1080 :
handle - > mm . vb = 4500 ;
break ;
default :
2016-03-09 18:01:03 -06:00
handle - > mm . vb = switch_calc_bitrate ( handle - > mm . vw , handle - > mm . vh , 1 , handle - > mm . fps ) ;
2015-03-18 17:58:23 -05:00
break ;
}
}
2016-03-08 18:53:55 -06:00
if ( handle - > stream_name & & handle - > mm . fps > 0.0f ) {
2015-03-18 17:58:23 -05:00
handle - > mm . keyint = ( int ) 2.0f * handle - > mm . fps ;
}
2015-03-17 23:52:01 +08:00
}
desc = avcodec_descriptor_get ( fmt - > video_codec ) ;
2015-07-15 12:50:36 -04:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " use video codec: [%d] %s (%s) \n " , fmt - > video_codec , desc - > name , desc - > long_name ) ;
2015-03-17 23:52:01 +08:00
}
if ( fmt - > audio_codec ! = AV_CODEC_ID_NONE ) {
2017-07-14 18:35:23 -05:00
const char * issplit = 0 ;
2015-03-17 23:52:01 +08:00
2017-07-14 18:35:23 -05:00
context - > audio_st [ 0 ] . channels = handle - > channels ;
context - > audio_st [ 0 ] . channels = handle - > channels ;
context - > audio_st [ 1 ] . sample_rate = handle - > samplerate ;
context - > audio_st [ 1 ] . sample_rate = handle - > samplerate ;
2016-03-09 18:01:03 -06:00
2017-07-14 18:35:23 -05:00
if ( handle - > channels > 1 & & handle - > params & & ( issplit = switch_event_get_header ( handle - > params , " channelsplit " ) ) ) {
int lr = ( ! strcasecmp ( issplit , " lr " ) | | switch_true ( issplit ) ) ;
int rl = ! strcasecmp ( issplit , " rl " ) ;
if ( lr | | rl ) {
context - > audio_st [ 0 ] . channels = 1 ;
context - > audio_st [ 1 ] . channels = 1 ;
add_stream ( context , & context - > audio_st [ 0 ] , context - > fc , & context - > audio_codec , fmt - > audio_codec , & handle - > mm ) ;
add_stream ( context , & context - > audio_st [ 1 ] , context - > fc , & context - > audio_codec , fmt - > audio_codec , & handle - > mm ) ;
}
if ( lr ) {
context - > audio_st [ 0 ] . r = 1 ;
} else if ( rl ) {
context - > audio_st [ 1 ] . r = 1 ;
}
}
if ( ! context - > audio_st [ 0 ] . active ) {
add_stream ( context , & context - > audio_st [ 0 ] , context - > fc , & context - > audio_codec , fmt - > audio_codec , & handle - > mm ) ;
}
if ( open_audio ( context - > fc , context - > audio_codec , & context - > audio_st [ 0 ] ) ! = SWITCH_STATUS_SUCCESS ) {
2015-07-21 08:37:42 +08:00
switch_goto_status ( SWITCH_STATUS_GENERR , end ) ;
2015-03-17 23:52:01 +08:00
}
context - > has_audio = 1 ;
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 1 ] . active ) {
if ( open_audio ( context - > fc , context - > audio_codec , & context - > audio_st [ 1 ] ) ! = SWITCH_STATUS_SUCCESS ) {
switch_goto_status ( SWITCH_STATUS_GENERR , end ) ;
}
context - > has_audio + + ;
}
2015-03-17 23:52:01 +08:00
}
2015-04-24 00:20:00 +08:00
av_dump_format ( context - > fc , 0 , file , 1 ) ;
2015-03-17 23:52:01 +08:00
handle - > format = 0 ;
handle - > sections = 0 ;
handle - > seekable = 0 ;
handle - > speed = 0 ;
handle - > pos = 0 ;
2017-08-31 08:11:11 +08:00
if ( disable_write_buffer ) {
handle - > pre_buffer_datalen = 0 ;
}
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Opening File [%s] %dhz %s \n " ,
2017-07-14 18:35:23 -05:00
file , handle - > samplerate , context - > has_video ? " with VIDEO " : " " ) ;
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_SUCCESS ;
2015-07-21 08:37:42 +08:00
end :
2017-04-10 13:15:59 -05:00
if ( ! context ) {
return status ;
}
if ( context - > fc ) {
2015-08-26 08:48:23 +08:00
mod_avformat_destroy_output_context ( context ) ;
2015-07-21 08:37:42 +08:00
}
2015-09-24 13:55:21 -05:00
if ( context - > video_timer . interval ) {
switch_core_timer_destroy ( & context - > video_timer ) ;
}
2015-07-21 08:37:42 +08:00
if ( context - > audio_buffer ) {
switch_buffer_destroy ( & context - > audio_buffer ) ;
}
2015-03-17 23:52:01 +08:00
2015-07-21 08:37:42 +08:00
return status ;
2015-03-17 23:52:01 +08:00
}
static switch_status_t av_file_truncate ( switch_file_handle_t * handle , int64_t offset )
{
return SWITCH_STATUS_FALSE ;
}
2015-09-30 22:03:33 -05:00
static switch_status_t av_file_write ( switch_file_handle_t * handle , void * data , size_t * len )
{
uint32_t datalen = 0 ;
switch_status_t status = SWITCH_STATUS_SUCCESS ;
// uint8_t buf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }, *bp = buf;
// uint32_t encoded_rate;
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
// uint32_t size = 0;
uint32_t bytes ;
int inuse ;
2017-11-02 14:03:21 -05:00
int sample_start ;
2016-02-23 22:01:38 -06:00
if ( ! switch_test_flag ( handle , SWITCH_FILE_FLAG_WRITE ) ) {
return SWITCH_STATUS_FALSE ;
}
2015-09-30 22:03:33 -05:00
if ( ! context - > vid_ready ) {
2016-05-03 09:22:53 +08:00
if ( switch_test_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) ) {
switch_buffer_zero ( context - > audio_buffer ) ;
return status ;
} else if ( ! context - > aud_ready ) { // audio only recording
int ret = avformat_write_header ( context - > fc , NULL ) ;
if ( ret < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error occurred when opening output file: %s \n " , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
2016-05-03 09:22:53 +08:00
return SWITCH_STATUS_FALSE ;
}
context - > aud_ready = 1 ;
}
2015-09-30 22:03:33 -05:00
}
if ( data & & len ) {
datalen = * len * 2 * handle - > channels ;
if ( context - > offset ) {
char buf [ SWITCH_RECOMMENDED_BUFFER_SIZE ] = { 0 } ;
switch_size_t samples = * len ;
int fps = handle - > samplerate / samples ;
int lead_frames = ( context - > offset * fps ) / 1000 ;
for ( int x = 0 ; x < lead_frames ; x + + ) {
switch_buffer_write ( context - > audio_buffer , buf , datalen ) ;
}
context - > offset = 0 ;
}
switch_buffer_write ( context - > audio_buffer , data , datalen ) ;
}
2017-01-06 02:10:15 -05:00
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
bytes = context - > audio_st [ 0 ] . frame - > nb_samples * 2 * context - > handle - > channels ; //context->audio_st[0].st->codec->channels;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-09-30 22:03:33 -05:00
2017-07-14 18:35:23 -05:00
//{
// int inuse = switch_buffer_inuse(context->audio_buffer);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "inuse: %d samples: %d bytes: %d\n", inuse, context->audio_st[0].frame->nb_samples, bytes);
//}
2015-09-30 22:03:33 -05:00
2016-03-09 00:02:52 -06:00
if ( context - > closed ) {
inuse = switch_buffer_inuse ( context - > audio_buffer ) ;
if ( inuse < bytes ) {
char buf [ SWITCH_RECOMMENDED_BUFFER_SIZE ] = { 0 } ;
switch_buffer_write ( context - > audio_buffer , buf , bytes - inuse ) ;
}
}
2017-01-06 02:10:15 -05:00
2017-07-14 18:35:23 -05:00
while ( ( inuse = switch_buffer_inuse ( context - > audio_buffer ) ) > = bytes ) {
AVPacket pkt [ 2 ] = { { 0 } } ;
int got_packet [ 2 ] = { 0 } ;
int j = 0 , ret = - 1 , audio_stream_count = 1 ;
AVFrame * use_frame = NULL ;
av_init_packet ( & pkt [ 0 ] ) ;
av_init_packet ( & pkt [ 1 ] ) ;
2017-01-06 02:10:15 -05:00
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 1 ] . active ) {
switch_size_t len = 0 ;
int i = 0 , j = 0 ;
int l = 0 , r = 1 ;
2017-01-06 02:10:15 -05:00
2017-07-14 18:35:23 -05:00
if ( ! context - > mux_buf | | context - > mux_buf_len < bytes / 2 ) {
context - > mux_buf_len = bytes / 2 ;
context - > mux_buf = ( int16_t * ) realloc ( ( void * ) context - > mux_buf , context - > mux_buf_len * 2 ) ;
2015-09-30 22:03:33 -05:00
}
2017-07-14 18:35:23 -05:00
audio_stream_count = 2 ;
len = switch_buffer_read ( context - > audio_buffer , ( uint8_t * ) context - > mux_buf , bytes ) ;
2017-05-31 12:48:46 -05:00
2017-07-14 18:35:23 -05:00
if ( context - > audio_st [ 0 ] . r ) {
l = 1 ;
r = 0 ;
}
for ( i = 0 ; i < len / 4 ; i + + ) {
* ( ( int16_t * ) context - > audio_st [ l ] . frame - > data [ 0 ] + i ) = context - > mux_buf [ j + + ] ;
* ( ( int16_t * ) context - > audio_st [ r ] . frame - > data [ 0 ] + i ) = context - > mux_buf [ j + + ] ;
}
2015-09-30 22:03:33 -05:00
} else {
2017-07-14 18:35:23 -05:00
switch_buffer_read ( context - > audio_buffer , context - > audio_st [ 0 ] . frame - > data [ 0 ] , bytes ) ;
}
2017-11-02 14:03:21 -05:00
/* Sync all audio stream timestamps to video timer */
if ( context - > video_timer . interval ) {
switch_core_timer_sync ( & context - > video_timer ) ;
sample_start = context - > video_timer . samplecount * ( handle - > samplerate / 1000 ) ;
2017-07-14 18:35:23 -05:00
2017-11-02 14:03:21 -05:00
for ( j = 0 ; j < audio_stream_count ; j + + ) {
context - > audio_st [ j ] . next_pts = sample_start ;
}
}
2017-07-14 18:35:23 -05:00
for ( j = 0 ; j < audio_stream_count ; j + + ) {
av_frame_make_writable ( context - > audio_st [ j ] . frame ) ;
use_frame = context - > audio_st [ j ] . frame ;
if ( context - > audio_st [ j ] . resample_ctx ) {
int out_samples = avresample_get_out_samples ( context - > audio_st [ j ] . resample_ctx , context - > audio_st [ j ] . frame - > nb_samples ) ;
av_frame_make_writable ( context - > audio_st [ j ] . tmp_frame ) ;
/* convert to destination format */
ret = avresample_convert ( context - > audio_st [ j ] . resample_ctx ,
context - > audio_st [ j ] . tmp_frame - > data , 0 , out_samples ,
( uint8_t * * ) context - > audio_st [ j ] . frame - > data , 0 , context - > audio_st [ j ] . frame - > nb_samples ) ;
if ( ret < 0 ) {
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error while converting %d samples, error text: %s \n " ,
context - > audio_st [ j ] . frame - > nb_samples , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
continue ;
}
use_frame = context - > audio_st [ j ] . tmp_frame ;
}
2017-11-02 14:03:21 -05:00
use_frame - > pts = context - > audio_st [ j ] . next_pts ;
2017-07-14 18:35:23 -05:00
// context->audio_st[j].next_pts = use_frame->pts + use_frame->nb_samples;
2015-09-30 22:03:33 -05:00
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
ret = avcodec_encode_audio2 ( context - > audio_st [ j ] . st - > codec , & pkt [ j ] , use_frame , & got_packet [ j ] ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-09-30 22:03:33 -05:00
2017-11-02 14:03:21 -05:00
context - > audio_st [ j ] . next_pts + = use_frame - > nb_samples ;
2017-07-14 18:35:23 -05:00
}
2015-09-30 22:03:33 -05:00
if ( ret < 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING , " Error encoding audio frame: %d \n " , ret ) ;
continue ;
}
2017-05-30 16:34:48 -05:00
2017-07-14 18:35:23 -05:00
for ( j = 0 ; j < audio_stream_count ; j + + ) {
if ( got_packet [ j ] ) {
if ( context - > mutex ) switch_mutex_lock ( context - > mutex ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
ret = write_frame ( context - > fc , & context - > audio_st [ j ] . st - > codec - > time_base , context - > audio_st [ j ] . st , & pkt [ j ] ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2017-07-14 18:35:23 -05:00
if ( context - > mutex ) switch_mutex_unlock ( context - > mutex ) ;
if ( ret < 0 ) {
context - > errs + + ;
if ( ( context - > errs % 10 ) = = 0 ) {
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error while writing audio frame: %s \n " , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
}
//switch_goto_status(SWITCH_STATUS_FALSE, end);
} else {
context - > errs = 0 ;
2017-06-27 13:06:27 -05:00
}
2017-02-08 13:54:38 -06:00
2017-07-14 18:35:23 -05:00
if ( context - > errs > 1000 ) {
status = SWITCH_STATUS_FALSE ;
goto end ;
}
2015-09-30 22:03:33 -05:00
}
}
2015-06-19 18:21:48 -05:00
}
2017-07-14 18:35:23 -05:00
end :
2015-09-30 22:03:33 -05:00
return status ;
2015-06-19 18:21:48 -05:00
}
2016-03-07 19:59:03 -06:00
static switch_status_t av_file_command ( switch_file_handle_t * handle , switch_file_command_t command )
{
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
2017-06-01 12:51:35 -05:00
uint32_t offset = 0 ;
2016-03-07 19:59:03 -06:00
switch ( command ) {
case SCFC_FLUSH_AUDIO :
2017-01-06 02:10:15 -05:00
switch_mutex_lock ( context - > mutex ) ;
2016-03-07 19:59:03 -06:00
switch_buffer_zero ( context - > audio_buffer ) ;
2017-01-06 02:10:15 -05:00
switch_mutex_unlock ( context - > mutex ) ;
2016-03-07 19:59:03 -06:00
break ;
2016-03-27 16:08:40 +08:00
case SCFC_PAUSE_READ :
if ( context - > read_paused ) {
context - > read_paused = SWITCH_FALSE ;
context - > video_st . next_pts = 0 ;
context - > video_start_time = 0 ;
} else {
context - > read_paused = SWITCH_TRUE ;
}
break ;
2017-05-30 16:34:48 -05:00
case SCFC_PAUSE_WRITE :
context - > vid_ready = 0 ;
2017-06-01 12:51:35 -05:00
context - > eh . record_timer_paused = switch_micro_time_now ( ) ;
2017-05-30 16:34:48 -05:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " %s pause write \n " , handle - > file_path ) ;
break ;
case SCFC_RESUME_WRITE :
2017-06-01 12:51:35 -05:00
if ( context - > eh . record_timer_paused ) {
context - > eh . last_ts = 0 ;
offset = ( uint32_t ) ( switch_micro_time_now ( ) - context - > eh . record_timer_paused ) ;
context - > video_timer . start + = offset ;
switch_core_timer_sync ( & context - > video_timer ) ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " %s resume write \n " , handle - > file_path ) ;
context - > eh . record_timer_paused = 0 ;
}
2017-05-30 16:34:48 -05:00
break ;
2016-03-07 19:59:03 -06:00
default :
break ;
}
return SWITCH_STATUS_SUCCESS ;
}
2015-03-17 23:52:01 +08:00
static switch_status_t av_file_close ( switch_file_handle_t * handle )
{
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
switch_status_t status ;
2016-02-23 22:01:38 -06:00
context - > closed = 1 ;
2016-03-08 18:53:55 -06:00
context - > eh . finalize = 1 ;
2016-02-23 22:01:38 -06:00
2015-03-17 23:52:01 +08:00
if ( context - > eh . video_queue ) {
2017-05-19 17:38:08 -05:00
if ( ! switch_test_flag ( handle , SWITCH_FILE_FLAG_WRITE ) ) {
flush_video_queue ( context - > eh . video_queue , 0 ) ;
switch_queue_push ( context - > eh . video_queue , NULL ) ;
switch_queue_term ( context - > eh . video_queue ) ;
} else {
switch_queue_push ( context - > eh . video_queue , NULL ) ;
}
2015-03-17 23:52:01 +08:00
}
if ( context - > eh . video_thread ) {
switch_thread_join ( & status , context - > eh . video_thread ) ;
}
2016-03-09 00:02:52 -06:00
if ( switch_test_flag ( handle , SWITCH_FILE_FLAG_WRITE ) ) {
av_file_write ( handle , NULL , NULL ) ;
}
2017-03-15 18:03:53 -05:00
if ( context - > file_read_thread_running ) {
2015-04-24 00:20:00 +08:00
context - > file_read_thread_running = 0 ;
2017-03-15 18:03:53 -05:00
}
2017-03-16 10:00:33 -05:00
2017-03-15 18:03:53 -05:00
if ( context - > file_read_thread ) {
2015-04-24 00:20:00 +08:00
switch_thread_join ( & status , context - > file_read_thread ) ;
2017-03-15 18:03:53 -05:00
context - > file_read_thread = NULL ;
2015-04-24 00:20:00 +08:00
}
2015-03-17 23:52:01 +08:00
2017-05-19 17:38:08 -05:00
if ( context - > eh . video_queue ) {
flush_video_queue ( context - > eh . video_queue , 0 ) ;
}
2015-04-24 00:20:00 +08:00
if ( context - > fc ) {
2017-07-14 18:35:23 -05:00
if ( ( context - > aud_ready | | context - > has_video ) & & switch_test_flag ( handle , SWITCH_FILE_FLAG_WRITE ) ) {
2017-06-28 12:16:46 -05:00
av_write_trailer ( context - > fc ) ;
}
2017-07-14 18:35:23 -05:00
2015-08-26 08:48:23 +08:00
mod_avformat_destroy_output_context ( context ) ;
2015-03-17 23:52:01 +08:00
}
2015-09-24 13:55:21 -05:00
if ( context - > video_timer . interval ) {
switch_core_timer_destroy ( & context - > video_timer ) ;
}
2015-05-05 16:02:26 +08:00
switch_img_free ( & context - > last_img ) ;
2015-03-17 23:52:01 +08:00
switch_buffer_destroy ( & context - > audio_buffer ) ;
2017-07-14 18:35:23 -05:00
switch_safe_free ( context - > mux_buf ) ;
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t av_file_seek ( switch_file_handle_t * handle , unsigned int * cur_sample , int64_t samples , int whence )
{
2016-02-13 11:18:15 +08:00
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
if ( whence = = SEEK_SET ) {
handle - > pos = handle - > offset_pos = samples ;
}
context - > seek_ts = samples / handle - > native_rate * AV_TIME_BASE ;
* cur_sample = context - > seek_ts ;
2017-03-16 17:43:09 -05:00
context - > closed = 0 ;
2016-11-07 12:14:25 -06:00
if ( ! context - > file_read_thread_running ) {
switch_threadattr_t * thd_attr = NULL ;
2017-03-15 18:03:53 -05:00
if ( context - > file_read_thread ) {
switch_status_t status ;
switch_thread_join ( & status , context - > file_read_thread ) ;
context - > file_read_thread = NULL ;
}
2016-11-07 12:14:25 -06:00
switch_threadattr_create ( & thd_attr , handle - > memory_pool ) ;
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
switch_thread_create ( & context - > file_read_thread , thd_attr , file_read_thread_run , context , handle - > memory_pool ) ;
}
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_FALSE ;
}
static switch_status_t av_file_read ( switch_file_handle_t * handle , void * data , size_t * len )
{
2015-04-24 00:20:00 +08:00
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
int size ;
2017-07-14 18:35:23 -05:00
size_t need = * len * 2 * context - > audio_st [ 0 ] . channels ;
2015-04-24 00:20:00 +08:00
2017-03-16 14:54:37 -05:00
if ( ! context - > has_audio & & context - > has_video & & context - > file_read_thread_running ) {
2015-04-24 00:20:00 +08:00
memset ( data , 0 , * len * handle - > channels * 2 ) ;
return SWITCH_STATUS_SUCCESS ;
}
2016-08-06 02:25:11 +00:00
switch_mutex_lock ( context - > mutex ) ;
while ( ! context - > file_read_thread_started ) {
switch_thread_cond_wait ( context - > cond , context - > mutex ) ;
}
switch_mutex_unlock ( context - > mutex ) ;
2017-03-16 17:43:09 -05:00
if ( context - > closed | | ( ! context - > file_read_thread_running & & switch_buffer_inuse ( context - > audio_buffer ) = = 0 ) ) {
2015-04-24 00:20:00 +08:00
* len = 0 ;
return SWITCH_STATUS_FALSE ;
}
2016-02-23 22:01:38 -06:00
while ( context - > has_video & & ! context - > vid_ready & & ! context - > closed ) {
switch_yield ( 1000 ) ;
}
2015-04-24 00:20:00 +08:00
switch_mutex_lock ( context - > mutex ) ;
2016-03-07 19:59:03 -06:00
size = switch_buffer_read ( context - > audio_buffer , data , need ) ;
2015-04-24 00:20:00 +08:00
switch_mutex_unlock ( context - > mutex ) ;
if ( size = = 0 ) {
2015-06-19 20:37:28 -05:00
size_t blank = ( handle - > samplerate / 20 ) * 2 * handle - > real_channels ;
2017-01-06 02:10:15 -05:00
2015-06-19 20:37:28 -05:00
if ( need > blank ) {
need = blank ;
}
memset ( data , 0 , need ) ;
* len = need / 2 / handle - > real_channels ;
} else {
2017-07-14 18:35:23 -05:00
* len = size / context - > audio_st [ 0 ] . channels / 2 ;
2015-04-24 00:20:00 +08:00
}
handle - > pos + = * len ;
handle - > sample_count + = * len ;
return * len = = 0 ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS ;
2015-03-17 23:52:01 +08:00
}
2016-02-23 22:01:38 -06:00
# ifdef ALT_WAY
static switch_status_t av_file_read_video ( switch_file_handle_t * handle , switch_frame_t * frame , switch_video_read_flag_t flags )
{
void * pop ;
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
switch_status_t status ;
if ( ! context - > has_video | | context - > closed ) return SWITCH_STATUS_FALSE ;
if ( ( flags & SVR_CHECK ) ) {
return SWITCH_STATUS_BREAK ;
}
if ( ( flags & SVR_FLUSH ) ) {
flush_video_queue ( context - > eh . video_queue , 1 ) ;
}
2017-01-06 02:10:15 -05:00
2016-02-23 22:01:38 -06:00
if ( ( flags & SVR_BLOCK ) ) {
status = switch_queue_pop ( context - > eh . video_queue , & pop ) ;
} else {
status = switch_queue_trypop ( context - > eh . video_queue , & pop ) ;
}
if ( status = = SWITCH_STATUS_SUCCESS ) {
if ( ! pop ) {
return SWITCH_STATUS_FALSE ;
}
context - > vid_ready = 1 ;
frame - > img = ( switch_image_t * ) pop ;
2017-02-21 15:52:53 -06:00
2017-02-21 20:09:32 -06:00
if ( frame - > img ) {
if ( frame - > img & & context - > handle - > mm . scale_w & & context - > handle - > mm . scale_h ) {
if ( frame - > img - > d_w ! = context - > handle - > mm . scale_w | | frame - > img - > d_h ! = context - > handle - > mm . scale_h ) {
switch_img_fit ( & frame - > img , context - > handle - > mm . scale_w , context - > handle - > mm . scale_h , SWITCH_FIT_SIZE ) ;
}
2017-02-21 15:52:53 -06:00
}
2017-02-21 20:09:32 -06:00
context - > vid_ready = 1 ;
}
2016-02-23 22:01:38 -06:00
return SWITCH_STATUS_SUCCESS ;
}
return ( flags & SVR_FLUSH ) ? SWITCH_STATUS_BREAK : status ;
}
2017-01-06 02:10:15 -05:00
# else
2016-02-23 22:01:38 -06:00
2015-09-30 22:03:33 -05:00
static switch_status_t av_file_read_video ( switch_file_handle_t * handle , switch_frame_t * frame , switch_video_read_flag_t flags )
2015-03-17 23:52:01 +08:00
{
2015-09-30 22:02:50 -05:00
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
2015-09-30 22:03:33 -05:00
void * pop ;
MediaStream * mst = & context - > video_st ;
AVStream * st = mst - > st ;
int ticks = 0 ;
2016-02-23 22:01:38 -06:00
int64_t max_delta = 1 * AV_TIME_BASE ; // 1 second
2015-09-30 22:03:33 -05:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
double fl_to = 0.02 ;
int do_fl = 0 ;
2016-03-29 18:41:43 -05:00
int smaller_ts = context - > read_fps ;
2015-03-17 23:52:01 +08:00
2015-09-30 22:03:33 -05:00
if ( ! context - > has_video ) return SWITCH_STATUS_FALSE ;
2015-09-30 22:02:50 -05:00
2015-09-30 22:03:33 -05:00
if ( ( flags & SVR_CHECK ) ) {
return SWITCH_STATUS_BREAK ;
2015-03-17 23:52:01 +08:00
}
2016-03-29 18:41:43 -05:00
if ( handle - > mm . fps > 0 & & handle - > mm . fps < smaller_ts ) {
smaller_ts = handle - > mm . fps ;
}
fl_to = ( 1000 / smaller_ts ) * 1000 ;
2015-09-30 22:03:33 -05:00
//printf("WTF %d (%f)\n",switch_queue_size(context->eh.video_queue), fl_to);
if ( flags & SVR_FLUSH ) {
2016-02-23 22:01:38 -06:00
max_delta = fl_to ;
2015-09-30 22:03:33 -05:00
do_fl = 1 ;
2015-05-05 16:02:26 +08:00
}
2016-03-27 16:08:40 +08:00
if ( ! context - > file_read_thread_running & & switch_queue_size ( context - > eh . video_queue ) = = 0 ) {
return SWITCH_STATUS_FALSE ;
}
2017-06-30 11:28:49 -05:00
if ( context - > read_paused | | context - > seek_ts = = - 2 ) {
2016-03-27 16:08:40 +08:00
int sanity = 10 ;
if ( context - > seek_ts = = - 2 ) { // just seeked, try read a new img
again1 :
status = switch_queue_trypop ( context - > eh . video_queue , & pop ) ;
if ( pop & & status = = SWITCH_STATUS_SUCCESS ) {
context - > seek_ts = - 1 ;
switch_img_free ( & context - > last_img ) ;
context - > last_img = ( switch_image_t * ) pop ;
switch_img_copy ( context - > last_img , & frame - > img ) ;
context - > vid_ready = 1 ;
return SWITCH_STATUS_SUCCESS ;
}
if ( context - > last_img ) { // repeat the last img
switch_img_copy ( context - > last_img , & frame - > img ) ;
context - > vid_ready = 1 ;
context - > seek_ts = - 1 ;
return SWITCH_STATUS_SUCCESS ;
}
if ( ( flags & SVR_BLOCK ) & & sanity - - > 0 ) {
switch_yield ( 10000 ) ;
goto again1 ;
}
return SWITCH_STATUS_BREAK ;
}
if ( context - > last_img ) { // repeat the last img
if ( ( flags & SVR_BLOCK ) ) switch_yield ( 100000 ) ;
switch_img_copy ( context - > last_img , & frame - > img ) ;
context - > vid_ready = 1 ;
return SWITCH_STATUS_SUCCESS ;
}
if ( ( flags & SVR_BLOCK ) ) {
status = switch_queue_pop ( context - > eh . video_queue , & pop ) ;
} else {
status = switch_queue_trypop ( context - > eh . video_queue , & pop ) ;
}
if ( pop & & status = = SWITCH_STATUS_SUCCESS ) {
context - > last_img = ( switch_image_t * ) pop ;
switch_img_copy ( context - > last_img , & frame - > img ) ;
context - > vid_ready = 1 ;
return SWITCH_STATUS_SUCCESS ;
}
return SWITCH_STATUS_BREAK ;
}
2017-02-28 17:44:16 -06:00
#if 0
2015-09-30 22:03:33 -05:00
if ( context - > last_img ) {
if ( mst - > next_pts & & ( switch_time_now ( ) - mst - > next_pts > max_delta ) ) {
switch_img_free ( & context - > last_img ) ; // too late
} else if ( mst - > next_pts & & ( switch_time_now ( ) - mst - > next_pts > - 10000 ) ) {
frame - > img = context - > last_img ;
context - > last_img = NULL ;
2016-02-23 22:01:38 -06:00
context - > vid_ready = 1 ;
2015-09-30 22:03:33 -05:00
return SWITCH_STATUS_SUCCESS ;
}
2015-09-30 22:02:50 -05:00
2016-02-23 22:01:38 -06:00
if ( ! ( flags & SVR_BLOCK ) & & ! do_fl ) {
if ( ! mst - > next_pts ) {
frame - > img = context - > last_img ;
context - > last_img = NULL ;
context - > vid_ready = 1 ;
return SWITCH_STATUS_SUCCESS ;
}
return SWITCH_STATUS_BREAK ;
}
2015-09-30 22:02:50 -05:00
}
2017-02-28 17:44:16 -06:00
# endif
2015-09-30 22:02:50 -05:00
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-09-30 22:03:33 -05:00
if ( st - > codec - > time_base . num ) {
ticks = st - > parser ? st - > parser - > repeat_pict + 1 : st - > codec - > ticks_per_frame ;
// mst->next_pts += ((int64_t)AV_TIME_BASE * st->codec->time_base.num * ticks) / st->codec->time_base.den;
2015-04-24 00:20:00 +08:00
}
2015-09-30 22:03:33 -05:00
if ( ! context - > video_start_time ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " start: % " SWITCH_INT64_T_FMT " ticks: %d ticks_per_frame: %d st num:%d st den:%d codec num:%d codec den:%d start: % " SWITCH_TIME_T_FMT " , duration:% " SWITCH_INT64_T_FMT " nb_frames:% " SWITCH_INT64_T_FMT " q2d:%f \n " ,
context - > video_start_time , ticks , st - > codec - > ticks_per_frame , st - > time_base . num , st - > time_base . den , st - > codec - > time_base . num , st - > codec - > time_base . den ,
st - > start_time , st - > duration , st - > nb_frames , av_q2d ( st - > time_base ) ) ;
2015-04-24 00:20:00 +08:00
}
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-04-24 00:20:00 +08:00
2015-09-30 22:03:33 -05:00
again :
2015-09-30 03:32:41 -05:00
2017-02-28 17:44:16 -06:00
if ( context - > last_img ) {
pop = ( void * ) context - > last_img ;
context - > last_img = NULL ;
status = SWITCH_STATUS_SUCCESS ;
2015-04-24 00:20:00 +08:00
} else {
2017-02-28 17:44:16 -06:00
if ( ( flags & SVR_BLOCK ) ) {
status = switch_queue_pop ( context - > eh . video_queue , & pop ) ;
} else {
status = switch_queue_trypop ( context - > eh . video_queue , & pop ) ;
}
2015-04-24 00:20:00 +08:00
}
2015-09-30 22:03:33 -05:00
if ( pop & & status = = SWITCH_STATUS_SUCCESS ) {
switch_image_t * img = ( switch_image_t * ) pop ;
2016-02-23 22:01:38 -06:00
int64_t pts ;
int64_t now = switch_time_now ( ) ;
2015-09-30 22:03:33 -05:00
pts = av_rescale_q ( * ( ( uint64_t * ) img - > user_priv ) , st - > time_base , AV_TIME_BASE_Q ) ;
2016-02-23 22:01:38 -06:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "pkt_pts: %lld pts: %lld queue size: %u\n", *((uint64_t *)img->user_priv), pts, switch_queue_size(context->eh.video_queue));
2016-02-18 00:10:07 +08:00
handle - > vpos = pts ;
2015-09-30 22:03:33 -05:00
if ( ! context - > video_start_time ) {
context - > video_start_time = now - pts ;
2016-02-23 22:01:38 -06:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_DEBUG , " set start time: % " SWITCH_INT64_T_FMT " now: % " SWITCH_INT64_T_FMT " pts: % " SWITCH_INT64_T_FMT " \n " , context - > video_start_time , now , pts ) ;
2015-04-24 00:20:00 +08:00
}
2015-09-30 22:03:33 -05:00
if ( st - > time_base . num = = 0 ) {
mst - > next_pts = 0 ;
} else {
2016-02-23 22:01:38 -06:00
// int64_t last_pts = mst->next_pts;
2015-09-30 22:03:33 -05:00
mst - > next_pts = context - > video_start_time + pts ;
2016-02-23 22:01:38 -06:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "pts: %" SWITCH_INT64_T_FMT " last_pts: %" SWITCH_INT64_T_FMT " delta: %" SWITCH_INT64_T_FMT " frame_pts: %" SWITCH_INT64_T_FMT " nextpts: %" SWITCH_INT64_T_FMT ", num: %d, den:%d num:%d den:%d sleep: %" SWITCH_INT64_T_FMT "\n",
// pts, last_pts, mst->next_pts - last_pts, *((uint64_t *)img->user_priv), mst->next_pts, st->time_base.num, st->time_base.den, st->codec->time_base.num, st->codec->time_base.den, mst->next_pts - now);
2015-09-30 22:03:33 -05:00
}
2016-02-23 22:01:38 -06:00
if ( pts = = 0 | | context - > video_start_time = = 0 ) mst - > next_pts = 0 ;
2015-09-30 22:03:33 -05:00
2016-02-23 22:01:38 -06:00
if ( ( mst - > next_pts & & ( now - mst - > next_pts ) > max_delta ) ) {
2016-03-29 18:41:43 -05:00
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "picture is too late, off: %" SWITCH_INT64_T_FMT " max delta: %" SWITCH_INT64_T_FMT " queue size:%u fps:%u/%0.2f\n", (int64_t)(now - mst->next_pts), max_delta, switch_queue_size(context->eh.video_queue), context->read_fps, handle->mm.fps);
2015-09-30 22:03:33 -05:00
switch_img_free ( & img ) ;
2016-03-29 18:41:43 -05:00
//max_delta = AV_TIME_BASE;
2017-01-06 02:10:15 -05:00
2015-09-30 22:03:33 -05:00
if ( switch_queue_size ( context - > eh . video_queue ) > 0 ) {
2016-02-23 22:01:38 -06:00
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF again\n");
2015-09-30 22:03:33 -05:00
goto again ;
} else if ( ! ( flags & SVR_BLOCK ) & & ! do_fl ) {
mst - > next_pts = 0 ;
2016-02-23 22:01:38 -06:00
context - > video_start_time = 0 ;
2015-09-30 22:03:33 -05:00
return SWITCH_STATUS_BREAK ;
2017-01-06 02:10:15 -05:00
}
2015-09-30 22:03:33 -05:00
}
2017-02-28 17:44:16 -06:00
if ( ( flags & SVR_BLOCK ) ) {
2016-02-23 22:01:38 -06:00
while ( switch_micro_time_now ( ) - mst - > next_pts < - 10000 ) {
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "yield, delta=%" SWITCH_INT64_T_FMT "\n", switch_micro_time_now() - mst->next_pts);
switch_yield ( 1000 ) ;
2015-09-30 22:03:33 -05:00
}
frame - > img = img ;
do_fl = 0 ;
} else {
2016-02-23 22:01:38 -06:00
if ( switch_micro_time_now ( ) - mst - > next_pts > - 10000 ) {
2015-09-30 22:03:33 -05:00
frame - > img = img ;
} else {
context - > last_img = img ;
return SWITCH_STATUS_BREAK ;
}
}
2017-01-06 02:10:15 -05:00
2015-09-30 22:03:33 -05:00
} else {
return SWITCH_STATUS_BREAK ;
2015-04-24 00:20:00 +08:00
}
2016-02-23 22:01:38 -06:00
if ( frame - > img ) {
2017-02-21 20:09:32 -06:00
if ( frame - > img & & context - > handle - > mm . scale_w & & context - > handle - > mm . scale_h ) {
if ( frame - > img - > d_w ! = context - > handle - > mm . scale_w | | frame - > img - > d_h ! = context - > handle - > mm . scale_h ) {
2017-03-14 17:18:00 -05:00
switch_img_fit ( & frame - > img , context - > handle - > mm . scale_w , context - > handle - > mm . scale_h , SWITCH_FIT_SCALE ) ;
2017-02-21 20:09:32 -06:00
}
}
2016-02-23 22:01:38 -06:00
context - > vid_ready = 1 ;
}
2017-06-30 11:28:49 -05:00
if ( ( flags & SVR_BLOCK ) ) {
if ( ! frame - > img ) context - > closed = 1 ;
return frame - > img ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE ;
} else {
return frame - > img ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_BREAK ;
}
2015-03-17 23:52:01 +08:00
}
2016-02-23 22:01:38 -06:00
# endif
2015-03-17 23:52:01 +08:00
static switch_status_t av_file_write_video ( switch_file_handle_t * handle , switch_frame_t * frame )
{
switch_status_t status = SWITCH_STATUS_SUCCESS ;
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
2017-07-14 18:35:23 -05:00
if ( ! switch_test_flag ( handle , SWITCH_FILE_FLAG_VIDEO ) ) {
return SWITCH_STATUS_FALSE ;
}
2015-03-17 23:52:01 +08:00
if ( ! frame - > img ) {
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
if ( ! context - > has_video ) {
context - > video_st . width = frame - > img - > d_w ;
context - > video_st . height = frame - > img - > d_h ;
context - > video_st . next_pts = switch_time_now ( ) / 1000 ;
2017-07-14 18:35:23 -05:00
if ( add_stream ( context , & context - > video_st , context - > fc , & context - > video_codec , context - > fc - > oformat - > video_codec , & handle - > mm ) = = SWITCH_STATUS_SUCCESS & &
2015-04-24 00:20:00 +08:00
open_video ( context - > fc , context - > video_codec , & context - > video_st ) = = SWITCH_STATUS_SUCCESS ) {
2015-03-17 23:52:01 +08:00
char codec_str [ 256 ] ;
int ret ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_OFF ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
avcodec_string ( codec_str , sizeof ( codec_str ) , context - > video_st . st - > codec , 1 ) ;
2017-06-30 17:28:56 -05:00
GCC_DIAG_ON ( deprecated - declarations )
2015-03-17 23:52:01 +08:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " use video codec implementation %s \n " , codec_str ) ;
context - > has_video = 1 ;
2015-04-24 00:20:00 +08:00
// av_dump_format(context->fc, 0, "/tmp/av.mp4", 1);
2015-03-17 23:52:01 +08:00
2015-04-24 00:20:00 +08:00
ret = avformat_write_header ( context - > fc , NULL ) ;
2015-03-17 23:52:01 +08:00
if ( ret < 0 ) {
2017-07-14 18:35:23 -05:00
char ebuf [ 255 ] = " " ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error occurred when opening output file: %s \n " , get_error_text ( ret , ebuf , sizeof ( ebuf ) ) ) ;
2015-03-17 23:52:01 +08:00
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Error adding video stream \n " ) ;
switch_goto_status ( SWITCH_STATUS_FALSE , end ) ;
}
}
if ( context - > has_video ) {
switch_image_t * img = NULL ;
2017-05-19 17:38:08 -05:00
if ( ! context - > eh . video_thread ) {
switch_threadattr_t * thd_attr = NULL ;
switch_mutex_init ( & context - > mutex , SWITCH_MUTEX_NESTED , handle - > memory_pool ) ;
context - > eh . mutex = context - > mutex ;
context - > eh . video_st = & context - > video_st ;
context - > eh . fc = context - > fc ;
context - > eh . mm = & handle - > mm ;
switch_queue_create ( & context - > eh . video_queue , SWITCH_CORE_QUEUE_LEN , handle - > memory_pool ) ;
switch_threadattr_create ( & thd_attr , handle - > memory_pool ) ;
//switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
switch_threadattr_stacksize_set ( thd_attr , SWITCH_THREAD_STACKSIZE ) ;
2017-11-02 14:03:21 -05:00
switch_core_timer_init ( & context - > video_timer , " soft " , 1 , 1 , context - > pool ) ;
2017-05-31 12:48:46 -05:00
context - > eh . video_timer = & context - > video_timer ;
2017-07-14 18:35:23 -05:00
context - > audio_st [ 0 ] . frame - > pts = 0 ;
context - > audio_st [ 0 ] . next_pts = 0 ;
2017-05-19 17:38:08 -05:00
switch_thread_create ( & context - > eh . video_thread , thd_attr , video_thread_run , context , handle - > memory_pool ) ;
}
2015-03-17 23:52:01 +08:00
switch_img_copy ( frame - > img , & img ) ;
switch_queue_push ( context - > eh . video_queue , img ) ;
2017-05-19 17:38:08 -05:00
if ( ! context - > vid_ready ) {
switch_mutex_lock ( context - > mutex ) ;
switch_buffer_zero ( context - > audio_buffer ) ;
switch_mutex_unlock ( context - > mutex ) ;
context - > vid_ready = 1 ;
}
2015-03-17 23:52:01 +08:00
2017-05-19 17:38:08 -05:00
}
2015-03-17 23:52:01 +08:00
end :
return status ;
}
2015-09-30 22:03:33 -05:00
2015-03-17 23:52:01 +08:00
static switch_status_t av_file_set_string ( switch_file_handle_t * handle , switch_audio_col_t col , const char * string )
{
2015-06-19 18:00:43 -05:00
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
if ( context - > fc ) {
const char * field = switch_parse_audio_col ( col ) ;
if ( field ) {
av_dict_set ( & context - > fc - > metadata , field , string , 0 ) ;
return SWITCH_STATUS_SUCCESS ;
}
}
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_FALSE ;
}
static switch_status_t av_file_get_string ( switch_file_handle_t * handle , switch_audio_col_t col , const char * * string )
{
2015-06-19 18:00:43 -05:00
av_file_context_t * context = ( av_file_context_t * ) handle - > private_info ;
if ( context - > fc ) {
AVDictionaryEntry * tag = NULL ;
const char * field = switch_parse_audio_col ( col ) ;
if ( field & & ( tag = av_dict_get ( context - > fc - > metadata , field , tag , 0 ) ) ) {
* string = tag - > value ;
return SWITCH_STATUS_SUCCESS ;
}
}
2015-03-17 23:52:01 +08:00
return SWITCH_STATUS_FALSE ;
}
static char * supported_formats [ SWITCH_MAX_CODECS ] = { 0 } ;
2015-05-21 15:48:48 -05:00
static const char modname [ ] = " mod_av " ;
2015-03-17 23:52:01 +08:00
SWITCH_MODULE_LOAD_FUNCTION ( mod_avformat_load )
{
switch_file_interface_t * file_interface ;
int i = 0 ;
supported_formats [ i + + ] = " av " ;
supported_formats [ i + + ] = " rtmp " ;
2017-04-05 16:32:04 -05:00
supported_formats [ i + + ] = " rtsp " ;
2015-03-17 23:52:01 +08:00
supported_formats [ i + + ] = " mp4 " ;
2016-11-08 13:45:47 -06:00
supported_formats [ i + + ] = " m4a " ;
2015-04-24 00:20:00 +08:00
supported_formats [ i + + ] = " mov " ;
2016-11-11 16:44:59 -06:00
supported_formats [ i + + ] = " mkv " ;
supported_formats [ i + + ] = " webm " ;
2017-06-30 12:32:04 -05:00
//supported_formats[i++] = "wav";
2015-03-17 23:52:01 +08:00
file_interface = ( switch_file_interface_t * ) switch_loadable_module_create_interface ( * module_interface , SWITCH_FILE_INTERFACE ) ;
file_interface - > interface_name = modname ;
file_interface - > extens = supported_formats ;
file_interface - > file_open = av_file_open ;
file_interface - > file_close = av_file_close ;
file_interface - > file_truncate = av_file_truncate ;
file_interface - > file_read = av_file_read ;
file_interface - > file_write = av_file_write ;
file_interface - > file_read_video = av_file_read_video ;
file_interface - > file_write_video = av_file_write_video ;
file_interface - > file_seek = av_file_seek ;
file_interface - > file_set_string = av_file_set_string ;
file_interface - > file_get_string = av_file_get_string ;
2016-03-07 19:59:03 -06:00
file_interface - > file_command = av_file_command ;
2015-03-17 23:52:01 +08: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 :
*/