2009-05-01 09:31:59 -04:00
/*
2017-02-26 09:32:37 -05:00
* Copyright ( C ) 2015 - 17 Espen Jurgensen
2009-05-01 09:31:59 -04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
2017-02-26 09:32:37 -05:00
# include <stdbool.h>
2009-05-01 09:31:59 -04:00
# include <string.h>
# include <unistd.h>
2010-01-09 07:42:59 -05:00
2009-05-01 09:31:59 -04:00
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
2017-09-16 17:01:42 -04:00
# include <libavfilter/avfilter.h>
2015-10-09 17:58:27 -04:00
# include <libavfilter/buffersink.h>
# include <libavfilter/buffersrc.h>
# include <libavutil/opt.h>
2015-12-09 14:00:09 -05:00
# include <libavutil/time.h>
2015-10-09 17:58:27 -04:00
# include <libavutil/pixdesc.h>
2017-03-04 03:40:29 -05:00
# include <libavutil/channel_layout.h>
# include <libavutil/mathematics.h>
2009-05-01 09:31:59 -04:00
2009-05-08 11:46:32 -04:00
# include "logger.h"
2009-05-01 09:31:59 -04:00
# include "conffile.h"
2009-06-07 12:58:02 -04:00
# include "db.h"
2015-10-09 17:58:27 -04:00
# include "avio_evbuffer.h"
2019-01-10 04:52:56 -05:00
# include "misc.h"
2009-05-01 09:31:59 -04:00
# include "transcode.h"
2015-10-09 17:58:27 -04:00
// Interval between ICY metadata checks for streams, in seconds
# define METADATA_ICY_INTERVAL 5
// Maximum number of streams in a file that we will accept
# define MAX_STREAMS 64
2015-10-21 17:53:21 -04:00
// Maximum number of times we retry when we encounter bad packets
2015-10-22 15:01:43 -04:00
# define MAX_BAD_PACKETS 5
2015-12-09 14:00:09 -05:00
// How long to wait (in microsec) before interrupting av_read_frame
2019-11-02 11:56:10 -04:00
# define READ_TIMEOUT 30000000
2009-05-01 09:31:59 -04:00
2016-09-25 16:01:07 -04:00
static const char * default_codecs = " mpeg,wav " ;
static const char * roku_codecs = " mpeg,mp4a,wma,wav " ;
static const char * itunes_codecs = " mpeg,mp4a,mp4v,alac,wav " ;
// Used for passing errors to DPRINTF (can't count on av_err2str being present)
static char errbuf [ 64 ] ;
2009-11-18 14:14:03 -05:00
2017-02-26 09:32:37 -05:00
// The settings struct will be filled out based on the profile enum
struct settings_ctx
{
bool encode_video ;
bool encode_audio ;
2017-02-28 17:06:01 -05:00
// Silence some log messages
bool silent ;
2017-02-26 09:32:37 -05:00
// Output format (for the muxer)
const char * format ;
2017-03-01 15:29:08 -05:00
// Input format (for the demuxer)
const char * in_format ;
2017-02-26 09:32:37 -05:00
// Audio settings
enum AVCodecID audio_codec ;
int sample_rate ;
uint64_t channel_layout ;
int channels ;
2019-08-23 11:22:11 -04:00
int bit_rate ;
2017-02-26 09:32:37 -05:00
enum AVSampleFormat sample_format ;
bool wavheader ;
2017-02-28 17:06:01 -05:00
bool icy ;
2017-02-26 09:32:37 -05:00
// Video settings
enum AVCodecID video_codec ;
const char * video_codec_name ;
enum AVPixelFormat pix_fmt ;
int height ;
int width ;
} ;
struct stream_ctx
{
AVStream * stream ;
AVCodecContext * codec ;
2015-10-09 17:58:27 -04:00
AVFilterContext * buffersink_ctx ;
AVFilterContext * buffersrc_ctx ;
AVFilterGraph * filter_graph ;
2017-02-26 11:50:04 -05:00
// Used for seeking
int64_t prev_pts ;
int64_t offset_pts ;
2015-10-09 17:58:27 -04:00
} ;
2009-11-18 14:14:03 -05:00
2017-02-26 09:32:37 -05:00
struct decode_ctx
{
// Settings derived from the profile
struct settings_ctx settings ;
2015-10-09 17:58:27 -04:00
// Input format context
AVFormatContext * ifmt_ctx ;
2010-03-15 13:38:33 -04:00
2017-03-01 15:29:08 -05:00
// IO Context for non-file input
AVIOContext * avio ;
2017-02-26 09:32:37 -05:00
// Stream and decoder data
struct stream_ctx audio_stream ;
struct stream_ctx video_stream ;
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
// Duration (used to make wav header)
2009-05-01 09:31:59 -04:00
uint32_t duration ;
2015-10-09 17:58:27 -04:00
2017-01-22 17:23:18 -05:00
// Data kind (used to determine if ICY metadata is relevant to look for)
enum data_kind data_kind ;
2017-02-26 17:41:30 -05:00
// Set to true if we just seeked
bool resume ;
2017-02-27 14:42:07 -05:00
// Set to true if we have reached eof
bool eof ;
2017-02-28 17:06:01 -05:00
// Set to true if avcodec_receive_frame() gave us a frame
bool got_frame ;
2017-02-26 17:41:30 -05:00
// Contains the most recent packet from av_read_frame()
AVPacket * packet ;
// Contains the most recent frame from avcodec_receive_frame()
AVFrame * decoded_frame ;
2015-12-09 14:00:09 -05:00
// Used to measure if av_read_frame is taking too long
int64_t timestamp ;
2015-10-09 17:58:27 -04:00
} ;
2017-02-26 09:32:37 -05:00
struct encode_ctx
{
// Settings derived from the profile
struct settings_ctx settings ;
2015-10-09 17:58:27 -04:00
// Output format context
AVFormatContext * ofmt_ctx ;
2017-02-26 09:32:37 -05:00
// Stream, filter and decoder data
struct stream_ctx audio_stream ;
struct stream_ctx video_stream ;
2015-10-09 17:58:27 -04:00
// The ffmpeg muxer writes to this buffer using the avio_evbuffer interface
struct evbuffer * obuf ;
2017-02-26 11:50:04 -05:00
// Contains the most recent packet from av_buffersink_get_frame()
AVFrame * filt_frame ;
// Contains the most recent packet from avcodec_receive_packet()
AVPacket * encoded_pkt ;
2015-10-09 17:58:27 -04:00
// How many output bytes we have processed in total
off_t total_bytes ;
// Used to check for ICY metadata changes at certain intervals
uint32_t icy_interval ;
2015-03-14 16:42:53 -04:00
uint32_t icy_hash ;
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
// WAV header
2009-05-01 09:31:59 -04:00
uint8_t header [ 44 ] ;
} ;
2020-11-21 10:29:54 -05:00
enum probe_type
{
PROBE_TYPE_DEFAULT ,
PROBE_TYPE_QUICK ,
} ;
2015-10-09 17:58:27 -04:00
/* -------------------------- PROFILE CONFIGURATION ------------------------ */
static int
2019-04-02 16:47:11 -04:00
init_settings ( struct settings_ctx * settings , enum transcode_profile profile , struct media_quality * quality )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:32:37 -05:00
memset ( settings , 0 , sizeof ( struct settings_ctx ) ) ;
2015-10-09 17:58:27 -04:00
switch ( profile )
{
2019-02-08 12:53:40 -05:00
case XCODE_PCM_NATIVE : // Sample rate and bit depth determined by source
settings - > encode_audio = 1 ;
settings - > icy = 1 ;
break ;
2015-10-09 17:58:27 -04:00
case XCODE_PCM16_HEADER :
2017-02-26 09:32:37 -05:00
settings - > wavheader = 1 ;
2019-04-02 16:47:11 -04:00
case XCODE_PCM16 :
2017-02-26 09:32:37 -05:00
settings - > encode_audio = 1 ;
settings - > format = " s16le " ;
settings - > audio_codec = AV_CODEC_ID_PCM_S16LE ;
settings - > sample_format = AV_SAMPLE_FMT_S16 ;
2019-01-11 13:34:36 -05:00
break ;
2019-04-02 16:47:11 -04:00
case XCODE_PCM24 :
2019-02-08 12:53:40 -05:00
settings - > encode_audio = 1 ;
settings - > format = " s24le " ;
settings - > audio_codec = AV_CODEC_ID_PCM_S24LE ;
2019-02-27 15:58:33 -05:00
settings - > sample_format = AV_SAMPLE_FMT_S32 ;
break ;
2019-04-02 16:47:11 -04:00
case XCODE_PCM32 :
2019-02-27 15:58:33 -05:00
settings - > encode_audio = 1 ;
settings - > format = " s32le " ;
settings - > audio_codec = AV_CODEC_ID_PCM_S32LE ;
settings - > sample_format = AV_SAMPLE_FMT_S32 ;
break ;
2015-10-09 17:58:27 -04:00
case XCODE_MP3 :
2017-02-26 09:32:37 -05:00
settings - > encode_audio = 1 ;
settings - > format = " mp3 " ;
settings - > audio_codec = AV_CODEC_ID_MP3 ;
settings - > sample_format = AV_SAMPLE_FMT_S16P ;
break ;
2019-01-10 04:52:56 -05:00
case XCODE_OPUS :
settings - > encode_audio = 1 ;
settings - > format = " data " ; // Means we get the raw packet from the encoder, no muxing
settings - > audio_codec = AV_CODEC_ID_OPUS ;
settings - > sample_format = AV_SAMPLE_FMT_S16 ; // Only libopus support
break ;
2017-02-26 09:32:37 -05:00
case XCODE_JPEG :
settings - > encode_video = 1 ;
2017-02-28 17:06:01 -05:00
settings - > silent = 1 ;
2020-07-19 17:52:42 -04:00
// With ffmpeg 4.3 (> libavformet 58.29) "image2" only works for actual file
// output. It's possible we should have used "image2pipe" all along, but since
// "image2" has been working we only replace it going forward.
# if (LIBAVFORMAT_VERSION_MAJOR > 58) || ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR > 29))
settings - > format = " image2pipe " ;
# else
2017-02-26 09:32:37 -05:00
settings - > format = " image2 " ;
2020-07-19 17:52:42 -04:00
# endif
2017-03-01 15:29:08 -05:00
settings - > in_format = " mjpeg " ;
2019-04-08 15:30:29 -04:00
settings - > pix_fmt = AV_PIX_FMT_YUVJ420P ;
2017-02-26 09:32:37 -05:00
settings - > video_codec = AV_CODEC_ID_MJPEG ;
break ;
case XCODE_PNG :
settings - > encode_video = 1 ;
2017-02-28 17:06:01 -05:00
settings - > silent = 1 ;
2020-07-19 17:52:42 -04:00
// See explanation above
# if (LIBAVFORMAT_VERSION_MAJOR > 58) || ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR > 29))
settings - > format = " image2pipe " ;
# else
2017-02-26 09:32:37 -05:00
settings - > format = " image2 " ;
2020-07-19 17:52:42 -04:00
# endif
2019-04-08 15:30:29 -04:00
settings - > pix_fmt = AV_PIX_FMT_RGB24 ;
2017-02-26 09:32:37 -05:00
settings - > video_codec = AV_CODEC_ID_PNG ;
break ;
2015-10-09 17:58:27 -04:00
2020-11-05 17:04:56 -05:00
case XCODE_VP8 :
settings - > encode_video = 1 ;
settings - > silent = 1 ;
// See explanation above
# if (LIBAVFORMAT_VERSION_MAJOR > 58) || ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR > 29))
settings - > format = " image2pipe " ;
# else
settings - > format = " image2 " ;
# endif
settings - > pix_fmt = AV_PIX_FMT_YUVJ420P ;
settings - > video_codec = AV_CODEC_ID_VP8 ;
break ;
2015-10-09 17:58:27 -04:00
default :
DPRINTF ( E_LOG , L_XCODE , " Bug! Unknown transcoding profile \n " ) ;
return - 1 ;
}
2017-02-26 09:32:37 -05:00
2019-04-02 16:47:11 -04:00
if ( quality & & quality - > sample_rate )
{
settings - > sample_rate = quality - > sample_rate ;
}
if ( quality & & quality - > channels )
{
settings - > channels = quality - > channels ;
settings - > channel_layout = av_get_default_channel_layout ( quality - > channels ) ;
}
2019-08-23 11:22:11 -04:00
if ( quality & & quality - > bit_rate )
{
settings - > bit_rate = quality - > bit_rate ;
}
2019-04-02 16:47:11 -04:00
if ( quality & & quality - > bits_per_sample & & ( quality - > bits_per_sample ! = 8 * av_get_bytes_per_sample ( settings - > sample_format ) ) )
{
2020-11-03 17:32:15 -05:00
DPRINTF ( E_LOG , L_XCODE , " Bug! Mismatch between profile (%d bps) and media quality (%d bps) \n " , 8 * av_get_bytes_per_sample ( settings - > sample_format ) , quality - > bits_per_sample ) ;
2019-04-02 16:47:11 -04:00
return - 1 ;
}
2017-02-26 09:32:37 -05:00
return 0 ;
}
static void
stream_settings_set ( struct stream_ctx * s , struct settings_ctx * settings , enum AVMediaType type )
{
if ( type = = AVMEDIA_TYPE_AUDIO )
{
s - > codec - > sample_rate = settings - > sample_rate ;
s - > codec - > channel_layout = settings - > channel_layout ;
s - > codec - > channels = settings - > channels ;
s - > codec - > sample_fmt = settings - > sample_format ;
s - > codec - > time_base = ( AVRational ) { 1 , settings - > sample_rate } ;
2019-08-23 11:22:11 -04:00
s - > codec - > bit_rate = settings - > bit_rate ;
2017-02-26 09:32:37 -05:00
}
2017-02-28 17:06:01 -05:00
else if ( type = = AVMEDIA_TYPE_VIDEO )
2017-02-26 09:32:37 -05:00
{
s - > codec - > height = settings - > height ;
s - > codec - > width = settings - > width ;
s - > codec - > pix_fmt = settings - > pix_fmt ;
s - > codec - > time_base = ( AVRational ) { 1 , 25 } ;
}
2015-10-09 17:58:27 -04:00
}
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
/* -------------------------------- HELPERS -------------------------------- */
2009-05-01 09:31:59 -04:00
2019-04-02 16:47:11 -04:00
static enum AVSampleFormat
bitdepth2format ( int bits_per_sample )
{
if ( bits_per_sample = = 16 )
return AV_SAMPLE_FMT_S16 ;
else if ( bits_per_sample = = 24 )
return AV_SAMPLE_FMT_S32 ;
else if ( bits_per_sample = = 32 )
return AV_SAMPLE_FMT_S32 ;
else
return AV_SAMPLE_FMT_NONE ;
}
2016-09-25 16:01:07 -04:00
static inline char *
2016-10-11 15:32:16 -04:00
err2str ( int errnum )
2016-09-25 16:01:07 -04:00
{
av_strerror ( errnum , errbuf , sizeof ( errbuf ) ) ;
return errbuf ;
}
2009-05-01 09:31:59 -04:00
static inline void
add_le16 ( uint8_t * dst , uint16_t val )
{
dst [ 0 ] = val & 0xff ;
dst [ 1 ] = ( val > > 8 ) & 0xff ;
}
static inline void
add_le32 ( uint8_t * dst , uint32_t val )
{
dst [ 0 ] = val & 0xff ;
dst [ 1 ] = ( val > > 8 ) & 0xff ;
dst [ 2 ] = ( val > > 16 ) & 0xff ;
dst [ 3 ] = ( val > > 24 ) & 0xff ;
}
static void
2015-10-09 17:58:27 -04:00
make_wav_header ( struct encode_ctx * ctx , struct decode_ctx * src_ctx , off_t * est_size )
2009-05-01 09:31:59 -04:00
{
uint32_t wav_len ;
int duration ;
2019-01-11 13:34:36 -05:00
int bps ;
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
if ( src_ctx - > duration )
duration = src_ctx - > duration ;
2009-05-01 09:31:59 -04:00
else
duration = 3 * 60 * 1000 ; /* 3 minutes, in ms */
2019-03-02 10:43:32 -05:00
bps = av_get_bytes_per_sample ( ctx - > settings . sample_format ) ;
wav_len = ctx - > settings . channels * bps * ctx - > settings . sample_rate * ( duration / 1000 ) ;
2009-05-01 09:31:59 -04:00
2019-02-17 10:41:37 -05:00
if ( est_size )
* est_size = wav_len + sizeof ( ctx - > header ) ;
2009-05-01 09:31:59 -04:00
memcpy ( ctx - > header , " RIFF " , 4 ) ;
add_le32 ( ctx - > header + 4 , 36 + wav_len ) ;
memcpy ( ctx - > header + 8 , " WAVEfmt " , 8 ) ;
add_le32 ( ctx - > header + 16 , 16 ) ;
add_le16 ( ctx - > header + 20 , 1 ) ;
2017-02-26 09:32:37 -05:00
add_le16 ( ctx - > header + 22 , ctx - > settings . channels ) ; /* channels */
add_le32 ( ctx - > header + 24 , ctx - > settings . sample_rate ) ; /* samplerate */
2019-03-02 10:43:32 -05:00
add_le32 ( ctx - > header + 28 , ctx - > settings . sample_rate * ctx - > settings . channels * bps ) ; /* byte rate */
add_le16 ( ctx - > header + 32 , ctx - > settings . channels * bps ) ; /* block align */
add_le16 ( ctx - > header + 34 , 8 * bps ) ; /* bits per sample */
2009-05-01 09:31:59 -04:00
memcpy ( ctx - > header + 36 , " data " , 4 ) ;
add_le32 ( ctx - > header + 40 , wav_len ) ;
}
2015-10-22 15:01:43 -04:00
/*
2017-02-26 09:32:37 -05:00
* Checks if this stream index is one that we are decoding
2015-10-22 15:01:43 -04:00
*
* @ in ctx Decode context
2017-02-26 09:32:37 -05:00
* @ in stream_index Index of stream to check
* @ return Type of stream , unknown if we are not decoding the stream
*/
static enum AVMediaType
stream_find ( struct decode_ctx * ctx , unsigned int stream_index )
{
if ( ctx - > audio_stream . stream & & ( stream_index = = ctx - > audio_stream . stream - > index ) )
return AVMEDIA_TYPE_AUDIO ;
if ( ctx - > video_stream . stream & & ( stream_index = = ctx - > video_stream . stream - > index ) )
return AVMEDIA_TYPE_VIDEO ;
return AVMEDIA_TYPE_UNKNOWN ;
}
/*
* Adds a stream to an output
*
* @ out ctx A pre - allocated stream ctx where we save stream and codec info
* @ in output Output to add the stream to
* @ in codec_id What kind of codec should we use
* @ return Negative on failure , otherwise zero
2015-10-22 15:01:43 -04:00
*/
2015-10-09 17:58:27 -04:00
static int
2019-01-11 13:34:36 -05:00
stream_add ( struct encode_ctx * ctx , struct stream_ctx * s , enum AVCodecID codec_id )
2015-10-09 17:58:27 -04:00
{
2019-01-11 13:34:36 -05:00
const AVCodecDescriptor * codec_desc ;
2017-02-26 09:32:37 -05:00
AVCodec * encoder ;
2018-04-21 17:19:41 -04:00
AVDictionary * options = NULL ;
2017-02-26 09:32:37 -05:00
int ret ;
2019-01-11 13:34:36 -05:00
codec_desc = avcodec_descriptor_get ( codec_id ) ;
if ( ! codec_desc )
{
DPRINTF ( E_LOG , L_XCODE , " Invalid codec ID (%d) \n " , codec_id ) ;
return - 1 ;
}
2017-02-26 09:32:37 -05:00
encoder = avcodec_find_encoder ( codec_id ) ;
if ( ! encoder )
{
2019-01-11 13:34:36 -05:00
DPRINTF ( E_LOG , L_XCODE , " Necessary encoder (%s) not found \n " , codec_desc - > name ) ;
2017-02-26 09:32:37 -05:00
return - 1 ;
}
CHECK_NULL ( L_XCODE , s - > stream = avformat_new_stream ( ctx - > ofmt_ctx , NULL ) ) ;
CHECK_NULL ( L_XCODE , s - > codec = avcodec_alloc_context3 ( encoder ) ) ;
stream_settings_set ( s , & ctx - > settings , encoder - > type ) ;
2017-02-28 17:06:01 -05:00
if ( ! s - > codec - > pix_fmt )
{
s - > codec - > pix_fmt = avcodec_default_get_format ( s - > codec , encoder - > pix_fmts ) ;
2019-01-11 13:34:36 -05:00
DPRINTF ( E_DBG , L_XCODE , " Pixel format set to %s (encoder is %s) \n " , av_get_pix_fmt_name ( s - > codec - > pix_fmt ) , codec_desc - > name ) ;
2017-02-28 17:06:01 -05:00
}
2017-02-26 09:32:37 -05:00
if ( ctx - > ofmt_ctx - > oformat - > flags & AVFMT_GLOBALHEADER )
2017-09-16 17:01:42 -04:00
s - > codec - > flags | = AV_CODEC_FLAG_GLOBAL_HEADER ;
2017-02-26 09:32:37 -05:00
2018-04-21 17:19:41 -04:00
// With ffmpeg 3.4, jpeg encoding with optimal huffman tables will segfault, see issue #502
if ( codec_id = = AV_CODEC_ID_MJPEG )
av_dict_set ( & options , " huffman " , " default " , 0 ) ;
2020-11-03 17:32:15 -05:00
// 20 ms frames is the current ffmpeg default, but we set it anyway, so that
// we don't risk issues if future versions change the default (it would become
// an issue because outputs/cast.c relies on 20 ms frames)
if ( codec_id = = AV_CODEC_ID_OPUS )
av_dict_set ( & options , " frame_duration " , " 20 " , 0 ) ;
2018-04-21 17:19:41 -04:00
ret = avcodec_open2 ( s - > codec , NULL , & options ) ;
2017-02-26 09:32:37 -05:00
if ( ret < 0 )
{
2019-01-11 13:34:36 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot open encoder (%s): %s \n " , codec_desc - > name , err2str ( ret ) ) ;
2017-02-26 09:32:37 -05:00
avcodec_free_context ( & s - > codec ) ;
return - 1 ;
}
// Copy the codec parameters we just set to the stream, so the muxer knows them
ret = avcodec_parameters_from_context ( s - > stream - > codecpar , s - > codec ) ;
if ( ret < 0 )
{
2019-01-11 13:34:36 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot copy stream parameters (%s): %s \n " , codec_desc - > name , err2str ( ret ) ) ;
2017-02-26 09:32:37 -05:00
avcodec_free_context ( & s - > codec ) ;
return - 1 ;
}
return 0 ;
2015-10-09 17:58:27 -04:00
}
2009-05-01 09:31:59 -04:00
2015-12-09 14:00:09 -05:00
/*
* Called by libavformat while demuxing . Used to interrupt / unblock av_read_frame
* in case a source ( especially a network stream ) becomes unavailable .
*
* @ in arg Will point to the decode context
* @ return Non - zero if av_read_frame should be interrupted
*/
2019-05-21 14:49:20 -04:00
static int
decode_interrupt_cb ( void * arg )
2015-12-09 14:00:09 -05:00
{
struct decode_ctx * ctx ;
ctx = ( struct decode_ctx * ) arg ;
if ( av_gettime ( ) - ctx - > timestamp > READ_TIMEOUT )
{
DPRINTF ( E_LOG , L_XCODE , " Timeout while reading source (connection problem?) \n " ) ;
return 1 ;
}
return 0 ;
}
2017-02-26 17:41:30 -05:00
/* Will read the next packet from the source, unless we are resuming after a
* seek in which case the most recent packet found by transcode_seek ( ) will be
* returned . The packet will be put in ctx - > packet .
2015-10-22 15:01:43 -04:00
*
2017-02-26 09:32:37 -05:00
* @ out type Media type of packet
2015-10-22 15:01:43 -04:00
* @ in ctx Decode context
2015-10-22 16:09:19 -04:00
* @ return 0 if OK , < 0 on error or end of file
2015-10-21 17:53:21 -04:00
*/
static int
2017-02-26 17:41:30 -05:00
read_packet ( enum AVMediaType * type , struct decode_ctx * dec_ctx )
2015-10-21 17:53:21 -04:00
{
int ret ;
2017-02-26 17:41:30 -05:00
// We just seeked, so transcode_seek() will have found a new ctx->packet and
// we should just use start with that (if the stream is one are ok with)
if ( dec_ctx - > resume )
2015-10-21 17:53:21 -04:00
{
2017-02-26 17:41:30 -05:00
dec_ctx - > resume = 0 ;
* type = stream_find ( dec_ctx , dec_ctx - > packet - > stream_index ) ;
if ( * type ! = AVMEDIA_TYPE_UNKNOWN )
return 0 ;
}
2015-10-21 17:53:21 -04:00
2017-02-26 17:41:30 -05:00
do
{
dec_ctx - > timestamp = av_gettime ( ) ;
2015-10-21 17:53:21 -04:00
2017-02-26 17:41:30 -05:00
av_packet_unref ( dec_ctx - > packet ) ;
ret = av_read_frame ( dec_ctx - > ifmt_ctx , dec_ctx - > packet ) ;
if ( ret < 0 )
{
DPRINTF ( E_WARN , L_XCODE , " Could not read frame: %s \n " , err2str ( ret ) ) ;
return ret ;
2015-10-21 17:53:21 -04:00
}
2017-02-26 17:41:30 -05:00
* type = stream_find ( dec_ctx , dec_ctx - > packet - > stream_index ) ;
2015-10-21 17:53:21 -04:00
}
2017-02-26 09:32:37 -05:00
while ( * type = = AVMEDIA_TYPE_UNKNOWN ) ;
2015-10-21 17:53:21 -04:00
return 0 ;
}
2017-02-26 11:50:04 -05:00
// Prepares a packet from the encoder for muxing
static void
packet_prepare ( AVPacket * pkt , struct stream_ctx * s )
2009-05-01 09:31:59 -04:00
{
2017-02-26 11:50:04 -05:00
pkt - > stream_index = s - > stream - > index ;
2013-12-29 18:40:16 -05:00
2017-02-26 11:50:04 -05:00
// This "wonderful" peace of code makes sure that the timestamp always increases,
// even if the user seeked backwards. The muxer will not accept non-increasing
// timestamps.
pkt - > pts + = s - > offset_pts ;
if ( pkt - > pts < s - > prev_pts )
2015-10-09 17:58:27 -04:00
{
2017-02-26 11:50:04 -05:00
s - > offset_pts + = s - > prev_pts - pkt - > pts ;
pkt - > pts = s - > prev_pts ;
2015-10-09 17:58:27 -04:00
}
2017-02-26 11:50:04 -05:00
s - > prev_pts = pkt - > pts ;
pkt - > dts = pkt - > pts ; //FIXME
2010-03-15 13:38:33 -04:00
2017-02-26 11:50:04 -05:00
av_packet_rescale_ts ( pkt , s - > codec - > time_base , s - > stream - > time_base ) ;
2015-10-09 17:58:27 -04:00
}
2009-05-01 09:31:59 -04:00
2017-02-26 17:41:30 -05:00
/*
2017-02-27 14:42:07 -05:00
* Part 4 + 5 of the conversion chain : read - > decode - > filter - > encode - > write
2017-02-26 17:41:30 -05:00
*
*/
2015-10-09 17:58:27 -04:00
static int
2017-02-26 11:50:04 -05:00
encode_write ( struct encode_ctx * ctx , struct stream_ctx * s , AVFrame * filt_frame )
2015-10-09 17:58:27 -04:00
{
int ret ;
2009-05-01 09:31:59 -04:00
2017-02-26 11:50:04 -05:00
// If filt_frame is null then flushing will be initiated by the codec
ret = avcodec_send_frame ( s - > codec , filt_frame ) ;
if ( ret < 0 )
return ret ;
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
while ( 1 )
{
2017-02-26 11:50:04 -05:00
ret = avcodec_receive_packet ( s - > codec , ctx - > encoded_pkt ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
2009-05-01 09:31:59 -04:00
{
2017-02-26 11:50:04 -05:00
if ( ret = = AVERROR ( EAGAIN ) )
2015-10-09 17:58:27 -04:00
ret = 0 ;
2017-02-26 11:50:04 -05:00
2015-10-09 17:58:27 -04:00
break ;
2009-05-01 09:31:59 -04:00
}
2017-02-26 11:50:04 -05:00
packet_prepare ( ctx - > encoded_pkt , s ) ;
ret = av_interleaved_write_frame ( ctx - > ofmt_ctx , ctx - > encoded_pkt ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
2020-07-19 17:52:42 -04:00
{
DPRINTF ( E_WARN , L_XCODE , " av_interleaved_write_frame() failed: %s \n " , err2str ( ret ) ) ;
break ;
}
2009-05-01 09:31:59 -04:00
}
2015-10-09 17:58:27 -04:00
return ret ;
2009-05-01 09:31:59 -04:00
}
2017-02-26 11:50:04 -05:00
2017-02-26 17:41:30 -05:00
/*
* Part 3 of the conversion chain : read - > decode - > filter - > encode - > write
*
* transcode_encode ( ) starts here since the caller already has a frame
*
*/
2015-10-09 17:58:27 -04:00
static int
2017-02-26 11:50:04 -05:00
filter_encode_write ( struct encode_ctx * ctx , struct stream_ctx * s , AVFrame * frame )
2010-04-04 06:34:28 -04:00
{
int ret ;
2015-10-09 17:58:27 -04:00
// Push the decoded frame into the filtergraph
2015-10-25 14:59:06 -04:00
if ( frame )
2010-04-04 06:34:28 -04:00
{
2017-02-26 11:50:04 -05:00
ret = av_buffersrc_add_frame ( s - > buffersrc_ctx , frame ) ;
2015-10-25 14:59:06 -04:00
if ( ret < 0 )
{
2016-09-25 16:01:07 -04:00
DPRINTF ( E_LOG , L_XCODE , " Error while feeding the filtergraph: %s \n " , err2str ( ret ) ) ;
2015-10-25 14:59:06 -04:00
return - 1 ;
}
2010-04-04 06:34:28 -04:00
}
2017-02-26 11:50:04 -05:00
// Pull filtered frames from the filtergraph and pass to encoder
2010-04-04 06:34:28 -04:00
while ( 1 )
{
2017-02-26 11:50:04 -05:00
ret = av_buffersink_get_frame ( s - > buffersink_ctx , ctx - > filt_frame ) ;
2010-04-04 06:34:28 -04:00
if ( ret < 0 )
{
2017-02-26 11:50:04 -05:00
if ( ! frame ) // We are flushing
ret = encode_write ( ctx , s , NULL ) ;
else if ( ret = = AVERROR ( EAGAIN ) )
2015-10-09 17:58:27 -04:00
ret = 0 ;
2017-02-26 11:50:04 -05:00
2010-04-04 06:34:28 -04:00
break ;
}
2017-02-26 11:50:04 -05:00
ret = encode_write ( ctx , s , ctx - > filt_frame ) ;
av_frame_unref ( ctx - > filt_frame ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
break ;
2010-04-04 06:34:28 -04:00
}
2015-10-09 17:58:27 -04:00
return ret ;
}
2011-06-02 11:26:45 -04:00
2017-02-26 17:41:30 -05:00
/*
* Part 2 of the conversion chain : read - > decode - > filter - > encode - > write
*
* If there is no encode_ctx the chain will aborted here
2015-10-22 15:01:43 -04:00
*
2015-10-21 17:53:21 -04:00
*/
static int
2017-02-26 17:41:30 -05:00
decode_filter_encode_write ( struct transcode_ctx * ctx , struct stream_ctx * s , AVPacket * pkt , enum AVMediaType type )
2015-10-21 17:53:21 -04:00
{
2017-02-26 17:41:30 -05:00
struct decode_ctx * dec_ctx = ctx - > decode_ctx ;
struct stream_ctx * out_stream = NULL ;
int ret ;
2015-10-21 17:53:21 -04:00
2017-02-26 17:41:30 -05:00
ret = avcodec_send_packet ( s - > codec , pkt ) ;
2018-01-26 16:37:28 -05:00
if ( ret < 0 & & ( ret ! = AVERROR_INVALIDDATA ) & & ( ret ! = AVERROR ( EAGAIN ) ) ) // We don't bail on invalid data, some streams work anyway
{
DPRINTF ( E_LOG , L_XCODE , " Decoder error, avcodec_send_packet said '%s' (%d) \n " , err2str ( ret ) , ret ) ;
return ret ;
}
2017-02-26 17:41:30 -05:00
if ( ctx - > encode_ctx )
2015-10-21 17:53:21 -04:00
{
2017-02-26 17:41:30 -05:00
if ( type = = AVMEDIA_TYPE_AUDIO )
out_stream = & ctx - > encode_ctx - > audio_stream ;
else if ( type = = AVMEDIA_TYPE_VIDEO )
out_stream = & ctx - > encode_ctx - > video_stream ;
else
return - 1 ;
2017-02-26 09:32:37 -05:00
}
2015-10-21 17:53:21 -04:00
2017-02-26 17:41:30 -05:00
while ( 1 )
2017-02-26 09:32:37 -05:00
{
2017-02-26 17:41:30 -05:00
ret = avcodec_receive_frame ( s - > codec , dec_ctx - > decoded_frame ) ;
if ( ret < 0 )
{
if ( ret = = AVERROR ( EAGAIN ) )
ret = 0 ;
else if ( out_stream )
ret = filter_encode_write ( ctx - > encode_ctx , out_stream , NULL ) ; // Flush
break ;
}
2017-02-28 17:06:01 -05:00
dec_ctx - > got_frame = 1 ;
2017-02-26 17:41:30 -05:00
if ( ! out_stream )
break ;
ret = filter_encode_write ( ctx - > encode_ctx , out_stream , dec_ctx - > decoded_frame ) ;
if ( ret < 0 )
break ;
2015-10-21 17:53:21 -04:00
}
2017-02-26 17:41:30 -05:00
return ret ;
}
/*
* Part 1 of the conversion chain : read - > decode - > filter - > encode - > write
*
* Will read exactly one packet from the input and put it in the chain . You
* cannot count on anything coming out of the other end from just one packet ,
* so you probably should loop when calling this and check the contents of
* enc_ctx - > obuf .
*
*/
static int
read_decode_filter_encode_write ( struct transcode_ctx * ctx )
{
struct decode_ctx * dec_ctx = ctx - > decode_ctx ;
enum AVMediaType type ;
int ret ;
ret = read_packet ( & type , dec_ctx ) ;
if ( ret < 0 )
{
2017-02-27 14:42:07 -05:00
if ( ret = = AVERROR_EOF )
dec_ctx - > eof = 1 ;
2017-02-26 17:41:30 -05:00
if ( dec_ctx - > audio_stream . stream )
decode_filter_encode_write ( ctx , & dec_ctx - > audio_stream , NULL , AVMEDIA_TYPE_AUDIO ) ;
if ( dec_ctx - > video_stream . stream )
decode_filter_encode_write ( ctx , & dec_ctx - > video_stream , NULL , AVMEDIA_TYPE_VIDEO ) ;
2017-02-27 14:42:07 -05:00
// Flush muxer
if ( ctx - > encode_ctx )
{
av_interleaved_write_frame ( ctx - > encode_ctx - > ofmt_ctx , NULL ) ;
av_write_trailer ( ctx - > encode_ctx - > ofmt_ctx ) ;
}
2017-02-26 17:41:30 -05:00
return ret ;
}
if ( type = = AVMEDIA_TYPE_AUDIO )
ret = decode_filter_encode_write ( ctx , & dec_ctx - > audio_stream , dec_ctx - > packet , type ) ;
else if ( type = = AVMEDIA_TYPE_VIDEO )
ret = decode_filter_encode_write ( ctx , & dec_ctx - > video_stream , dec_ctx - > packet , type ) ;
return ret ;
2015-10-21 17:53:21 -04:00
}
2010-04-04 06:34:28 -04:00
2015-10-09 17:58:27 -04:00
/* --------------------------- INPUT/OUTPUT INIT --------------------------- */
2010-04-04 06:34:28 -04:00
2020-11-21 06:01:33 -05:00
static int
open_decoder ( AVCodecContext * * dec_ctx , unsigned int * stream_index , struct decode_ctx * ctx , enum AVMediaType type )
2017-02-28 17:06:01 -05:00
{
AVCodec * decoder ;
int ret ;
2020-08-10 16:56:08 -04:00
ret = av_find_best_stream ( ctx - > ifmt_ctx , type , - 1 , - 1 , & decoder , 0 ) ;
2020-11-21 06:01:33 -05:00
if ( ret < 0 )
2017-02-28 17:06:01 -05:00
{
if ( ! ctx - > settings . silent )
2020-11-21 06:01:33 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error finding best stream: %s \n " , err2str ( ret ) ) ;
return ret ;
2017-02-28 17:06:01 -05:00
}
2020-08-10 16:56:08 -04:00
* stream_index = ( unsigned int ) ret ;
2020-11-21 06:01:33 -05:00
CHECK_NULL ( L_XCODE , * dec_ctx = avcodec_alloc_context3 ( decoder ) ) ;
2017-02-28 17:06:01 -05:00
// In open_filter() we need to tell the sample rate and format that the decoder
// is giving us - however sample rate of dec_ctx will be 0 if we don't prime it
// with the streams codecpar data.
2020-11-21 06:01:33 -05:00
ret = avcodec_parameters_to_context ( * dec_ctx , ctx - > ifmt_ctx - > streams [ * stream_index ] - > codecpar ) ;
2017-02-28 17:06:01 -05:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_XCODE , " Failed to copy codecpar for stream #%d: %s \n " , * stream_index , err2str ( ret ) ) ;
2020-11-21 06:01:33 -05:00
avcodec_free_context ( dec_ctx ) ;
return ret ;
2017-02-28 17:06:01 -05:00
}
if ( type = = AVMEDIA_TYPE_AUDIO )
{
2020-11-21 06:01:33 -05:00
( * dec_ctx ) - > request_sample_fmt = ctx - > settings . sample_format ;
( * dec_ctx ) - > request_channel_layout = ctx - > settings . channel_layout ;
2017-02-28 17:06:01 -05:00
}
2020-11-21 06:01:33 -05:00
ret = avcodec_open2 ( * dec_ctx , NULL , NULL ) ;
2017-02-28 17:06:01 -05:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_XCODE , " Failed to open decoder for stream #%d: %s \n " , * stream_index , err2str ( ret ) ) ;
2020-11-21 06:01:33 -05:00
avcodec_free_context ( dec_ctx ) ;
return ret ;
2017-02-28 17:06:01 -05:00
}
2020-11-21 06:01:33 -05:00
return 0 ;
2017-02-28 17:06:01 -05:00
}
2015-10-09 17:58:27 -04:00
static int
2020-11-21 10:29:54 -05:00
open_input ( struct decode_ctx * ctx , const char * path , struct evbuffer * evbuf , enum probe_type probe_type )
2009-05-01 09:31:59 -04:00
{
2017-02-26 09:32:37 -05:00
AVDictionary * options = NULL ;
AVCodecContext * dec_ctx ;
2017-03-01 15:29:08 -05:00
AVInputFormat * ifmt ;
2017-02-28 17:06:01 -05:00
unsigned int stream_index ;
2018-08-12 13:49:23 -04:00
const char * user_agent ;
2020-11-21 06:01:33 -05:00
int ret = 0 ;
2009-05-01 09:31:59 -04:00
2017-02-26 09:32:37 -05:00
CHECK_NULL ( L_XCODE , ctx - > ifmt_ctx = avformat_alloc_context ( ) ) ;
2015-04-01 18:09:12 -04:00
2020-11-21 10:29:54 -05:00
// Caller can ask for small probe to start quicker + search for embedded
// artwork quicker. Especially useful for http sources. The standard probe
// size takes around 5 sec for an mp3, while the below only takes around a
// second. The improved performance comes at the cost of possible inaccuracy.
if ( probe_type = = PROBE_TYPE_QUICK )
2017-02-26 09:32:37 -05:00
{
2020-04-07 15:43:30 -04:00
ctx - > ifmt_ctx - > probesize = 65536 ;
ctx - > ifmt_ctx - > format_probesize = 65536 ;
2020-11-21 10:29:54 -05:00
}
2020-04-07 15:43:30 -04:00
2020-11-21 10:29:54 -05:00
if ( ctx - > data_kind = = DATA_KIND_HTTP )
{
2017-02-26 09:32:37 -05:00
av_dict_set ( & options , " icy " , " 1 " , 0 ) ;
2018-08-12 13:49:23 -04:00
user_agent = cfg_getstr ( cfg_getsec ( cfg , " general " ) , " user_agent " ) ;
av_dict_set ( & options , " user_agent " , user_agent , 0 ) ;
2018-08-12 13:50:54 -04:00
av_dict_set ( & options , " reconnect " , " 1 " , 0 ) ;
2019-05-21 14:49:20 -04:00
// The below option disabled because it does not work with m3u8 streams,
// see https://lists.ffmpeg.org/pipermail/ffmpeg-user/2018-September/041109.html
// av_dict_set(&options, "reconnect_at_eof", "1", 0);
2018-08-12 13:50:54 -04:00
av_dict_set ( & options , " reconnect_streamed " , " 1 " , 0 ) ;
2017-02-26 09:32:37 -05:00
}
2015-04-01 18:09:12 -04:00
2015-12-09 14:00:09 -05:00
// TODO Newest versions of ffmpeg have timeout and reconnect options we should use
ctx - > ifmt_ctx - > interrupt_callback . callback = decode_interrupt_cb ;
ctx - > ifmt_ctx - > interrupt_callback . opaque = ctx ;
ctx - > timestamp = av_gettime ( ) ;
2017-03-01 15:29:08 -05:00
if ( evbuf )
{
ifmt = av_find_input_format ( ctx - > settings . in_format ) ;
if ( ! ifmt )
{
DPRINTF ( E_LOG , L_XCODE , " Could not find input format: '%s' \n " , ctx - > settings . in_format ) ;
2020-11-21 05:24:24 -05:00
goto out_fail ;
2017-03-01 15:29:08 -05:00
}
CHECK_NULL ( L_XCODE , ctx - > avio = avio_input_evbuffer_open ( evbuf ) ) ;
ctx - > ifmt_ctx - > pb = ctx - > avio ;
ret = avformat_open_input ( & ctx - > ifmt_ctx , NULL , ifmt , & options ) ;
}
else
{
ret = avformat_open_input ( & ctx - > ifmt_ctx , path , NULL , & options ) ;
}
2015-12-09 14:00:09 -05:00
2015-04-01 18:09:12 -04:00
if ( options )
av_dict_free ( & options ) ;
2015-12-09 14:00:09 -05:00
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
2009-05-01 09:31:59 -04:00
{
2016-10-26 13:52:30 -04:00
DPRINTF ( E_LOG , L_XCODE , " Cannot open '%s': %s \n " , path , err2str ( ret ) ) ;
2020-11-21 05:24:24 -05:00
goto out_fail ;
2009-05-01 09:31:59 -04:00
}
2015-10-09 17:58:27 -04:00
ret = avformat_find_stream_info ( ctx - > ifmt_ctx , NULL ) ;
2009-05-01 09:31:59 -04:00
if ( ret < 0 )
{
2016-09-25 16:01:07 -04:00
DPRINTF ( E_LOG , L_XCODE , " Cannot find stream information: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
2009-05-01 09:31:59 -04:00
}
2015-10-09 17:58:27 -04:00
if ( ctx - > ifmt_ctx - > nb_streams > MAX_STREAMS )
2014-01-02 16:49:18 -05:00
{
2016-10-26 13:52:30 -04:00
DPRINTF ( E_LOG , L_XCODE , " File '%s' has too many streams (%u) \n " , path , ctx - > ifmt_ctx - > nb_streams ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
2014-01-02 16:49:18 -05:00
}
2009-05-01 09:31:59 -04:00
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_audio )
2015-10-09 17:58:27 -04:00
{
2020-11-21 06:01:33 -05:00
ret = open_decoder ( & dec_ctx , & stream_index , ctx , AVMEDIA_TYPE_AUDIO ) ;
if ( ret < 0 )
2017-02-28 17:06:01 -05:00
goto out_fail ;
2013-12-29 18:40:16 -05:00
2017-02-26 09:32:37 -05:00
ctx - > audio_stream . codec = dec_ctx ;
ctx - > audio_stream . stream = ctx - > ifmt_ctx - > streams [ stream_index ] ;
2009-05-01 09:31:59 -04:00
}
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_video )
2009-05-01 09:31:59 -04:00
{
2020-11-21 06:01:33 -05:00
ret = open_decoder ( & dec_ctx , & stream_index , ctx , AVMEDIA_TYPE_VIDEO ) ;
if ( ret < 0 )
2017-02-28 17:06:01 -05:00
goto out_fail ;
2017-02-26 09:32:37 -05:00
ctx - > video_stream . codec = dec_ctx ;
ctx - > video_stream . stream = ctx - > ifmt_ctx - > streams [ stream_index ] ;
2015-10-09 17:58:27 -04:00
}
2013-12-30 17:47:41 -05:00
2015-10-09 17:58:27 -04:00
return 0 ;
2013-12-30 17:16:30 -05:00
2015-10-09 17:58:27 -04:00
out_fail :
2017-03-01 15:29:08 -05:00
avio_evbuffer_close ( ctx - > avio ) ;
2017-02-26 09:32:37 -05:00
avcodec_free_context ( & ctx - > audio_stream . codec ) ;
avcodec_free_context ( & ctx - > video_stream . codec ) ;
2015-10-09 17:58:27 -04:00
avformat_close_input ( & ctx - > ifmt_ctx ) ;
2010-03-15 13:38:33 -04:00
2020-11-21 06:01:33 -05:00
return ( ret < 0 ? ret : - 1 ) ; // If we got an error code from ffmpeg then return that
2015-10-09 17:58:27 -04:00
}
2013-12-29 18:40:16 -05:00
2015-10-09 17:58:27 -04:00
static void
close_input ( struct decode_ctx * ctx )
{
2017-03-01 15:29:08 -05:00
avio_evbuffer_close ( ctx - > avio ) ;
2017-02-26 09:32:37 -05:00
avcodec_free_context ( & ctx - > audio_stream . codec ) ;
avcodec_free_context ( & ctx - > video_stream . codec ) ;
2015-10-09 17:58:27 -04:00
avformat_close_input ( & ctx - > ifmt_ctx ) ;
}
2014-10-02 16:48:50 -04:00
2015-10-09 17:58:27 -04:00
static int
open_output ( struct encode_ctx * ctx , struct decode_ctx * src_ctx )
{
2017-03-04 03:40:29 -05:00
AVOutputFormat * oformat ;
2015-10-09 17:58:27 -04:00
int ret ;
2014-10-02 16:48:50 -04:00
2017-03-04 03:40:29 -05:00
oformat = av_guess_format ( ctx - > settings . format , NULL , NULL ) ;
if ( ! oformat )
2015-10-09 17:58:27 -04:00
{
2017-03-04 03:40:29 -05:00
DPRINTF ( E_LOG , L_XCODE , " ffmpeg/libav could not find the '%s' output format \n " , ctx - > settings . format ) ;
2015-10-09 17:58:27 -04:00
return - 1 ;
}
2014-10-02 16:48:50 -04:00
2017-02-28 17:06:01 -05:00
// Clear AVFMT_NOFILE bit, it is not allowed as we will set our own AVIOContext
2019-03-13 16:44:16 -04:00
oformat - > flags & = ~ AVFMT_NOFILE ;
2017-03-04 03:40:29 -05:00
CHECK_NULL ( L_XCODE , ctx - > ofmt_ctx = avformat_alloc_context ( ) ) ;
ctx - > ofmt_ctx - > oformat = oformat ;
2017-02-28 17:06:01 -05:00
2015-10-09 17:58:27 -04:00
ctx - > obuf = evbuffer_new ( ) ;
if ( ! ctx - > obuf )
{
DPRINTF ( E_LOG , L_XCODE , " Could not create output evbuffer \n " ) ;
2017-02-26 09:32:37 -05:00
goto out_free_output ;
2015-10-09 17:58:27 -04:00
}
2014-10-02 16:48:50 -04:00
2016-01-05 15:44:44 -05:00
ctx - > ofmt_ctx - > pb = avio_output_evbuffer_open ( ctx - > obuf ) ;
2015-10-09 17:58:27 -04:00
if ( ! ctx - > ofmt_ctx - > pb )
{
DPRINTF ( E_LOG , L_XCODE , " Could not create output avio pb \n " ) ;
2017-02-26 09:32:37 -05:00
goto out_free_evbuf ;
2015-10-09 17:58:27 -04:00
}
2013-12-29 18:40:16 -05:00
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_audio )
{
2019-01-11 13:34:36 -05:00
ret = stream_add ( ctx , & ctx - > audio_stream , ctx - > settings . audio_codec ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
2017-02-26 09:32:37 -05:00
goto out_free_streams ;
}
2014-01-02 16:49:18 -05:00
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_video )
{
2019-01-11 13:34:36 -05:00
ret = stream_add ( ctx , & ctx - > video_stream , ctx - > settings . video_codec ) ;
2017-02-26 09:32:37 -05:00
if ( ret < 0 )
goto out_free_streams ;
2010-03-15 13:38:33 -04:00
}
2015-10-09 17:58:27 -04:00
// Notice, this will not write WAV header (so we do that manually)
ret = avformat_write_header ( ctx - > ofmt_ctx , NULL ) ;
if ( ret < 0 )
{
2016-09-25 16:01:07 -04:00
DPRINTF ( E_LOG , L_XCODE , " Error writing header to output buffer: %s \n " , err2str ( ret ) ) ;
2017-02-26 09:32:37 -05:00
goto out_free_streams ;
2015-10-09 17:58:27 -04:00
}
2014-03-11 18:20:29 -04:00
2017-02-26 17:41:30 -05:00
if ( ctx - > settings . wavheader )
{
evbuffer_add ( ctx - > obuf , ctx - > header , sizeof ( ctx - > header ) ) ;
}
2014-03-11 18:20:29 -04:00
return 0 ;
2009-05-01 09:31:59 -04:00
2017-02-26 09:32:37 -05:00
out_free_streams :
avcodec_free_context ( & ctx - > audio_stream . codec ) ;
avcodec_free_context ( & ctx - > video_stream . codec ) ;
2015-10-09 17:58:27 -04:00
avio_evbuffer_close ( ctx - > ofmt_ctx - > pb ) ;
2017-02-26 09:32:37 -05:00
out_free_evbuf :
2015-10-09 17:58:27 -04:00
evbuffer_free ( ctx - > obuf ) ;
2017-02-26 09:32:37 -05:00
out_free_output :
2015-10-09 17:58:27 -04:00
avformat_free_context ( ctx - > ofmt_ctx ) ;
2010-03-15 13:35:29 -04:00
2014-03-11 18:20:29 -04:00
return - 1 ;
2009-05-01 09:31:59 -04:00
}
2015-10-09 17:58:27 -04:00
static void
close_output ( struct encode_ctx * ctx )
2009-05-01 09:31:59 -04:00
{
2017-02-26 09:32:37 -05:00
avcodec_free_context ( & ctx - > audio_stream . codec ) ;
avcodec_free_context ( & ctx - > video_stream . codec ) ;
2015-10-09 17:58:27 -04:00
avio_evbuffer_close ( ctx - > ofmt_ctx - > pb ) ;
evbuffer_free ( ctx - > obuf ) ;
2017-02-27 14:42:07 -05:00
2015-10-09 17:58:27 -04:00
avformat_free_context ( ctx - > ofmt_ctx ) ;
}
static int
2017-02-26 09:40:37 -05:00
open_filter ( struct stream_ctx * out_stream , struct stream_ctx * in_stream )
2015-10-09 17:58:27 -04:00
{
2018-06-05 18:00:22 -04:00
const AVFilter * buffersrc ;
const AVFilter * format ;
const AVFilter * scale ;
const AVFilter * buffersink ;
2017-02-26 09:32:37 -05:00
AVFilterContext * buffersrc_ctx ;
AVFilterContext * format_ctx ;
AVFilterContext * scale_ctx ;
AVFilterContext * buffersink_ctx ;
AVFilterGraph * filter_graph ;
2015-10-09 17:58:27 -04:00
char args [ 512 ] ;
int ret ;
2017-02-26 09:32:37 -05:00
CHECK_NULL ( L_XCODE , filter_graph = avfilter_graph_alloc ( ) ) ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
if ( in_stream - > codec - > codec_type = = AVMEDIA_TYPE_AUDIO )
2015-10-09 17:58:27 -04:00
{
buffersrc = avfilter_get_by_name ( " abuffer " ) ;
2017-02-26 09:32:37 -05:00
format = avfilter_get_by_name ( " aformat " ) ;
2015-10-09 17:58:27 -04:00
buffersink = avfilter_get_by_name ( " abuffersink " ) ;
2017-02-26 09:32:37 -05:00
if ( ! buffersrc | | ! format | | ! buffersink )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Filtering source, format or sink element not found \n " ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2017-02-26 09:32:37 -05:00
if ( ! in_stream - > codec - > channel_layout )
in_stream - > codec - > channel_layout = av_get_default_channel_layout ( in_stream - > codec - > channels ) ;
2015-10-09 17:58:27 -04:00
snprintf ( args , sizeof ( args ) ,
" time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x% " PRIx64 ,
2017-02-26 09:32:37 -05:00
in_stream - > stream - > time_base . num , in_stream - > stream - > time_base . den ,
in_stream - > codec - > sample_rate , av_get_sample_fmt_name ( in_stream - > codec - > sample_fmt ) ,
in_stream - > codec - > channel_layout ) ;
2015-10-09 17:58:27 -04:00
ret = avfilter_graph_create_filter ( & buffersrc_ctx , buffersrc , " in " , args , NULL , filter_graph ) ;
if ( ret < 0 )
{
2017-02-28 17:06:01 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create audio buffer source (%s): %s \n " , args , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2019-02-08 12:53:40 -05:00
DPRINTF ( E_DBG , L_XCODE , " Created 'in' filter: %s \n " , args ) ;
2020-04-07 15:24:31 -04:00
// For some AIFF files, ffmpeg (3.4.6) will not give us a channel_layout (bug in ffmpeg?)
if ( ! out_stream - > codec - > channel_layout )
out_stream - > codec - > channel_layout = av_get_default_channel_layout ( out_stream - > codec - > channels ) ;
2017-02-26 09:32:37 -05:00
snprintf ( args , sizeof ( args ) ,
" sample_fmts=%s:sample_rates=%d:channel_layouts=0x% " PRIx64 ,
av_get_sample_fmt_name ( out_stream - > codec - > sample_fmt ) , out_stream - > codec - > sample_rate ,
out_stream - > codec - > channel_layout ) ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
ret = avfilter_graph_create_filter ( & format_ctx , format , " format " , args , NULL , filter_graph ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2017-02-28 17:06:01 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create audio format filter (%s): %s \n " , args , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2019-02-08 12:53:40 -05:00
DPRINTF ( E_DBG , L_XCODE , " Created 'format' filter: %s \n " , args ) ;
2017-02-26 09:32:37 -05:00
ret = avfilter_graph_create_filter ( & buffersink_ctx , buffersink , " out " , NULL , NULL , filter_graph ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create audio buffer sink: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2017-02-26 09:32:37 -05:00
if ( ( ret = avfilter_link ( buffersrc_ctx , 0 , format_ctx , 0 ) ) < 0 | |
( ret = avfilter_link ( format_ctx , 0 , buffersink_ctx , 0 ) ) < 0 )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error connecting audio filters: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
}
2017-02-26 09:32:37 -05:00
else if ( in_stream - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO )
2015-10-09 17:58:27 -04:00
{
buffersrc = avfilter_get_by_name ( " buffer " ) ;
format = avfilter_get_by_name ( " format " ) ;
2017-02-26 09:32:37 -05:00
scale = avfilter_get_by_name ( " scale " ) ;
2015-10-09 17:58:27 -04:00
buffersink = avfilter_get_by_name ( " buffersink " ) ;
if ( ! buffersrc | | ! format | | ! buffersink )
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Filtering source, format, scale or sink element not found \n " ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
snprintf ( args , sizeof ( args ) ,
2017-03-01 16:32:41 -05:00
" width=%d:height=%d:pix_fmt=%s:time_base=%d/%d:sar=%d/%d " ,
in_stream - > codec - > width , in_stream - > codec - > height , av_get_pix_fmt_name ( in_stream - > codec - > pix_fmt ) ,
2017-02-26 09:32:37 -05:00
in_stream - > stream - > time_base . num , in_stream - > stream - > time_base . den ,
in_stream - > codec - > sample_aspect_ratio . num , in_stream - > codec - > sample_aspect_ratio . den ) ;
2015-10-09 17:58:27 -04:00
ret = avfilter_graph_create_filter ( & buffersrc_ctx , buffersrc , " in " , args , NULL , filter_graph ) ;
if ( ret < 0 )
{
2017-02-28 17:06:01 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create buffer source (%s): %s \n " , args , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
snprintf ( args , sizeof ( args ) ,
2017-02-28 17:06:01 -05:00
" pix_fmts=%s " , av_get_pix_fmt_name ( out_stream - > codec - > pix_fmt ) ) ;
2015-10-09 17:58:27 -04:00
ret = avfilter_graph_create_filter ( & format_ctx , format , " format " , args , NULL , filter_graph ) ;
if ( ret < 0 )
{
2017-02-28 17:06:01 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create format filter (%s): %s \n " , args , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
snprintf ( args , sizeof ( args ) ,
2017-03-01 16:32:41 -05:00
" w=%d:h=%d " , out_stream - > codec - > width , out_stream - > codec - > height ) ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
ret = avfilter_graph_create_filter ( & scale_ctx , scale , " scale " , args , NULL , filter_graph ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2017-02-28 17:06:01 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create scale filter (%s): %s \n " , args , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2017-02-26 09:32:37 -05:00
ret = avfilter_graph_create_filter ( & buffersink_ctx , buffersink , " out " , NULL , NULL , filter_graph ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot create buffer sink: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
2017-02-26 09:32:37 -05:00
if ( ( ret = avfilter_link ( buffersrc_ctx , 0 , format_ctx , 0 ) ) < 0 | |
( ret = avfilter_link ( format_ctx , 0 , scale_ctx , 0 ) ) < 0 | |
( ret = avfilter_link ( scale_ctx , 0 , buffersink_ctx , 0 ) ) < 0 )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:32:37 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error connecting video filters: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
goto out_fail ;
}
}
else
{
DPRINTF ( E_LOG , L_XCODE , " Bug! Unknown type passed to filter graph init \n " ) ;
goto out_fail ;
}
ret = avfilter_graph_config ( filter_graph , NULL ) ;
if ( ret < 0 )
goto out_fail ;
/* Fill filtering context */
2017-02-26 09:32:37 -05:00
out_stream - > buffersrc_ctx = buffersrc_ctx ;
out_stream - > buffersink_ctx = buffersink_ctx ;
out_stream - > filter_graph = filter_graph ;
2015-10-09 17:58:27 -04:00
return 0 ;
out_fail :
avfilter_graph_free ( & filter_graph ) ;
return - 1 ;
}
static int
open_filters ( struct encode_ctx * ctx , struct decode_ctx * src_ctx )
{
int ret ;
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_audio )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:40:37 -05:00
ret = open_filter ( & ctx - > audio_stream , & src_ctx - > audio_stream ) ;
2017-02-26 09:32:37 -05:00
if ( ret < 0 )
goto out_fail ;
2010-03-15 13:38:33 -04:00
}
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_video )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:40:37 -05:00
ret = open_filter ( & ctx - > video_stream , & src_ctx - > video_stream ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
goto out_fail ;
}
return 0 ;
out_fail :
2017-02-26 09:32:37 -05:00
avfilter_graph_free ( & ctx - > audio_stream . filter_graph ) ;
avfilter_graph_free ( & ctx - > video_stream . filter_graph ) ;
2015-10-09 17:58:27 -04:00
return - 1 ;
}
static void
close_filters ( struct encode_ctx * ctx )
{
2017-02-26 09:32:37 -05:00
avfilter_graph_free ( & ctx - > audio_stream . filter_graph ) ;
avfilter_graph_free ( & ctx - > video_stream . filter_graph ) ;
2015-10-09 17:58:27 -04:00
}
/* ----------------------------- TRANSCODE API ----------------------------- */
/* Setup */
struct decode_ctx *
2019-04-02 16:47:11 -04:00
transcode_decode_setup ( enum transcode_profile profile , struct media_quality * quality , enum data_kind data_kind , const char * path , struct evbuffer * evbuf , uint32_t song_length )
2015-10-09 17:58:27 -04:00
{
struct decode_ctx * ctx ;
2020-11-21 06:01:33 -05:00
int ret ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
CHECK_NULL ( L_XCODE , ctx = calloc ( 1 , sizeof ( struct decode_ctx ) ) ) ;
2017-02-26 17:41:30 -05:00
CHECK_NULL ( L_XCODE , ctx - > decoded_frame = av_frame_alloc ( ) ) ;
CHECK_NULL ( L_XCODE , ctx - > packet = av_packet_alloc ( ) ) ;
2017-02-26 11:50:04 -05:00
2017-01-22 17:23:18 -05:00
ctx - > duration = song_length ;
ctx - > data_kind = data_kind ;
2020-11-21 06:01:33 -05:00
ret = init_settings ( & ctx - > settings , profile , quality ) ;
if ( ret < 0 )
goto fail_free ;
2020-11-21 10:29:54 -05:00
if ( data_kind = = DATA_KIND_HTTP )
{
ret = open_input ( ctx , path , evbuf , PROBE_TYPE_QUICK ) ;
// Retry with a default, slower probe size
if ( ret = = AVERROR_STREAM_NOT_FOUND )
ret = open_input ( ctx , path , evbuf , PROBE_TYPE_DEFAULT ) ;
}
else
ret = open_input ( ctx , path , evbuf , PROBE_TYPE_DEFAULT ) ;
2020-11-21 06:01:33 -05:00
if ( ret < 0 )
2017-02-26 17:41:30 -05:00
goto fail_free ;
2015-10-09 17:58:27 -04:00
return ctx ;
2017-02-26 17:41:30 -05:00
fail_free :
av_packet_free ( & ctx - > packet ) ;
av_frame_free ( & ctx - > decoded_frame ) ;
free ( ctx ) ;
return NULL ;
2015-10-09 17:58:27 -04:00
}
struct encode_ctx *
2019-04-02 16:47:11 -04:00
transcode_encode_setup ( enum transcode_profile profile , struct media_quality * quality , struct decode_ctx * src_ctx , off_t * est_size , int width , int height )
2015-10-09 17:58:27 -04:00
{
struct encode_ctx * ctx ;
2019-01-11 13:34:36 -05:00
int bps ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
CHECK_NULL ( L_XCODE , ctx = calloc ( 1 , sizeof ( struct encode_ctx ) ) ) ;
2017-02-26 11:50:04 -05:00
CHECK_NULL ( L_XCODE , ctx - > filt_frame = av_frame_alloc ( ) ) ;
CHECK_NULL ( L_XCODE , ctx - > encoded_pkt = av_packet_alloc ( ) ) ;
2015-10-09 17:58:27 -04:00
2019-04-02 16:47:11 -04:00
if ( init_settings ( & ctx - > settings , profile , quality ) < 0 )
2017-02-26 17:41:30 -05:00
goto fail_free ;
2017-02-28 17:06:01 -05:00
ctx - > settings . width = width ;
ctx - > settings . height = height ;
2019-04-02 16:47:11 -04:00
// Caller did not specify a sample rate -> use same as source
2019-01-11 13:34:36 -05:00
if ( ! ctx - > settings . sample_rate & & ctx - > settings . encode_audio )
2019-04-02 16:47:11 -04:00
{
ctx - > settings . sample_rate = src_ctx - > audio_stream . codec - > sample_rate ;
}
2019-01-11 13:34:36 -05:00
2019-04-02 16:47:11 -04:00
// Caller did not specify a sample format -> use same as source
2019-01-11 13:34:36 -05:00
if ( ! ctx - > settings . sample_format & & ctx - > settings . encode_audio )
{
2019-02-27 15:58:33 -05:00
bps = av_get_bytes_per_sample ( src_ctx - > audio_stream . codec - > sample_fmt ) ;
if ( bps = = 4 )
2019-01-11 13:34:36 -05:00
{
ctx - > settings . sample_format = AV_SAMPLE_FMT_S32 ;
2019-02-27 15:58:33 -05:00
ctx - > settings . audio_codec = AV_CODEC_ID_PCM_S32LE ;
ctx - > settings . format = " s32le " ;
2019-01-11 13:34:36 -05:00
}
else
{
ctx - > settings . sample_format = AV_SAMPLE_FMT_S16 ;
ctx - > settings . audio_codec = AV_CODEC_ID_PCM_S16LE ;
ctx - > settings . format = " s16le " ;
}
}
2019-04-02 16:47:11 -04:00
// Caller did not specify channels -> use same as source
if ( ! ctx - > settings . channels & & ctx - > settings . encode_audio )
{
ctx - > settings . channels = src_ctx - > audio_stream . codec - > channels ;
2020-04-07 15:24:31 -04:00
ctx - > settings . channel_layout = src_ctx - > audio_stream . codec - > channel_layout ;
2019-04-02 16:47:11 -04:00
}
2017-02-26 17:41:30 -05:00
if ( ctx - > settings . wavheader )
make_wav_header ( ctx , src_ctx , est_size ) ;
if ( open_output ( ctx , src_ctx ) < 0 )
goto fail_free ;
2015-10-09 17:58:27 -04:00
if ( open_filters ( ctx , src_ctx ) < 0 )
2017-02-26 17:41:30 -05:00
goto fail_close ;
2015-10-09 17:58:27 -04:00
2017-02-28 17:06:01 -05:00
if ( ctx - > settings . icy & & src_ctx - > data_kind = = DATA_KIND_HTTP )
2019-01-11 13:34:36 -05:00
{
2019-03-02 10:43:32 -05:00
bps = av_get_bytes_per_sample ( ctx - > settings . sample_format ) ;
ctx - > icy_interval = METADATA_ICY_INTERVAL * ctx - > settings . channels * bps * ctx - > settings . sample_rate ;
2019-01-11 13:34:36 -05:00
}
2015-10-09 17:58:27 -04:00
return ctx ;
2017-02-26 17:41:30 -05:00
fail_close :
close_output ( ctx ) ;
fail_free :
av_packet_free ( & ctx - > encoded_pkt ) ;
av_frame_free ( & ctx - > filt_frame ) ;
free ( ctx ) ;
return NULL ;
2015-10-09 17:58:27 -04:00
}
struct transcode_ctx *
2019-04-02 16:47:11 -04:00
transcode_setup ( enum transcode_profile profile , struct media_quality * quality , enum data_kind data_kind , const char * path , uint32_t song_length , off_t * est_size )
2015-10-09 17:58:27 -04:00
{
struct transcode_ctx * ctx ;
2017-02-26 17:41:30 -05:00
CHECK_NULL ( L_XCODE , ctx = calloc ( 1 , sizeof ( struct transcode_ctx ) ) ) ;
2015-10-09 17:58:27 -04:00
2019-04-02 16:47:11 -04:00
ctx - > decode_ctx = transcode_decode_setup ( profile , quality , data_kind , path , NULL , song_length ) ;
2015-10-09 17:58:27 -04:00
if ( ! ctx - > decode_ctx )
{
free ( ctx ) ;
return NULL ;
}
2019-04-02 16:47:11 -04:00
ctx - > encode_ctx = transcode_encode_setup ( profile , quality , ctx - > decode_ctx , est_size , 0 , 0 ) ;
2015-10-09 17:58:27 -04:00
if ( ! ctx - > encode_ctx )
{
2017-02-26 17:41:30 -05:00
transcode_decode_cleanup ( & ctx - > decode_ctx ) ;
2015-10-09 17:58:27 -04:00
free ( ctx ) ;
return NULL ;
}
return ctx ;
}
struct decode_ctx *
2019-04-02 16:47:11 -04:00
transcode_decode_setup_raw ( enum transcode_profile profile , struct media_quality * quality )
2015-10-09 17:58:27 -04:00
{
2019-01-11 13:34:36 -05:00
const AVCodecDescriptor * codec_desc ;
2015-10-09 17:58:27 -04:00
struct decode_ctx * ctx ;
2017-02-26 09:32:37 -05:00
AVCodec * decoder ;
int ret ;
CHECK_NULL ( L_XCODE , ctx = calloc ( 1 , sizeof ( struct decode_ctx ) ) ) ;
2015-10-09 17:58:27 -04:00
2019-04-02 16:47:11 -04:00
if ( init_settings ( & ctx - > settings , profile , quality ) < 0 )
2015-10-09 17:58:27 -04:00
{
2017-02-26 09:32:37 -05:00
goto out_free_ctx ;
2015-10-09 17:58:27 -04:00
}
2019-01-11 13:34:36 -05:00
codec_desc = avcodec_descriptor_get ( ctx - > settings . audio_codec ) ;
if ( ! codec_desc )
{
DPRINTF ( E_LOG , L_XCODE , " Invalid codec ID (%d) \n " , ctx - > settings . audio_codec ) ;
goto out_free_ctx ;
}
2017-02-26 09:32:37 -05:00
// In raw mode we won't actually need to read or decode, but we still setup
// the decode_ctx because transcode_encode_setup() gets info about the input
// through this structure (TODO dont' do that)
decoder = avcodec_find_decoder ( ctx - > settings . audio_codec ) ;
if ( ! decoder )
2015-10-09 17:58:27 -04:00
{
2019-01-11 13:34:36 -05:00
DPRINTF ( E_LOG , L_XCODE , " Could not find decoder for: %s \n " , codec_desc - > name ) ;
2017-02-26 09:32:37 -05:00
goto out_free_ctx ;
2015-10-09 17:58:27 -04:00
}
2017-02-26 09:32:37 -05:00
CHECK_NULL ( L_XCODE , ctx - > ifmt_ctx = avformat_alloc_context ( ) ) ;
CHECK_NULL ( L_XCODE , ctx - > audio_stream . codec = avcodec_alloc_context3 ( decoder ) ) ;
CHECK_NULL ( L_XCODE , ctx - > audio_stream . stream = avformat_new_stream ( ctx - > ifmt_ctx , NULL ) ) ;
2015-10-09 17:58:27 -04:00
2017-02-26 09:32:37 -05:00
stream_settings_set ( & ctx - > audio_stream , & ctx - > settings , decoder - > type ) ;
// Copy the data we just set to the structs we will be querying later, e.g. in open_filter
ctx - > audio_stream . stream - > time_base = ctx - > audio_stream . codec - > time_base ;
ret = avcodec_parameters_from_context ( ctx - > audio_stream . stream - > codecpar , ctx - > audio_stream . codec ) ;
if ( ret < 0 )
2015-10-09 17:58:27 -04:00
{
2019-01-11 13:34:36 -05:00
DPRINTF ( E_LOG , L_XCODE , " Cannot copy stream parameters (%s): %s \n " , codec_desc - > name , err2str ( ret ) ) ;
2017-02-26 09:32:37 -05:00
goto out_free_codec ;
2015-10-09 17:58:27 -04:00
}
return ctx ;
2017-02-26 09:32:37 -05:00
out_free_codec :
avcodec_free_context ( & ctx - > audio_stream . codec ) ;
avformat_free_context ( ctx - > ifmt_ctx ) ;
out_free_ctx :
free ( ctx ) ;
return NULL ;
2009-05-01 09:31:59 -04:00
}
int
2014-08-22 18:02:01 -04:00
transcode_needed ( const char * user_agent , const char * client_codecs , char * file_codectype )
2009-05-01 09:31:59 -04:00
{
char * codectype ;
cfg_t * lib ;
int size ;
int i ;
2014-03-11 18:20:29 -04:00
if ( ! file_codectype )
{
2015-10-09 17:58:27 -04:00
DPRINTF ( E_LOG , L_XCODE , " Can't determine decode status, codec type is unknown \n " ) ;
2014-03-11 18:20:29 -04:00
return - 1 ;
}
2010-03-19 14:06:47 -04:00
lib = cfg_getsec ( cfg , " library " ) ;
2009-05-01 09:31:59 -04:00
2015-10-09 17:58:27 -04:00
size = cfg_size ( lib , " no_decode " ) ;
2009-05-01 09:31:59 -04:00
if ( size > 0 )
{
for ( i = 0 ; i < size ; i + + )
{
2015-10-09 17:58:27 -04:00
codectype = cfg_getnstr ( lib , " no_decode " , i ) ;
2009-05-01 09:31:59 -04:00
if ( strcmp ( file_codectype , codectype ) = = 0 )
2015-10-09 17:58:27 -04:00
return 0 ; // Codectype is in no_decode
2009-05-01 09:31:59 -04:00
}
}
2015-10-09 17:58:27 -04:00
size = cfg_size ( lib , " force_decode " ) ;
2009-05-01 09:31:59 -04:00
if ( size > 0 )
{
for ( i = 0 ; i < size ; i + + )
{
2015-10-09 17:58:27 -04:00
codectype = cfg_getnstr ( lib , " force_decode " , i ) ;
2009-05-01 09:31:59 -04:00
if ( strcmp ( file_codectype , codectype ) = = 0 )
2015-10-09 17:58:27 -04:00
return 1 ; // Codectype is in force_decode
2009-05-01 09:31:59 -04:00
}
}
if ( ! client_codecs )
{
if ( user_agent )
{
if ( strncmp ( user_agent , " iTunes " , strlen ( " iTunes " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
client_codecs = itunes_codecs ;
2009-12-08 14:45:57 -05:00
else if ( strncmp ( user_agent , " QuickTime " , strlen ( " QuickTime " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
client_codecs = itunes_codecs ; // Use iTunes codecs
2009-12-08 14:45:57 -05:00
else if ( strncmp ( user_agent , " Front%20Row " , strlen ( " Front%20Row " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
client_codecs = itunes_codecs ; // Use iTunes codecs
2014-02-05 20:34:27 -05:00
else if ( strncmp ( user_agent , " AppleCoreMedia " , strlen ( " AppleCoreMedia " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
client_codecs = itunes_codecs ; // Use iTunes codecs
2009-05-01 09:31:59 -04:00
else if ( strncmp ( user_agent , " Roku " , strlen ( " Roku " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
client_codecs = roku_codecs ;
2009-05-01 09:31:59 -04:00
else if ( strncmp ( user_agent , " Hifidelio " , strlen ( " Hifidelio " ) ) = = 0 )
2015-10-09 17:58:27 -04:00
/* Allegedly can't transcode for Hifidelio because their
* HTTP implementation doesn ' t honour Connection : close .
* At least , that ' s why mt - daapd didn ' t do it .
*/
return 0 ;
2009-05-01 09:31:59 -04:00
}
}
else
2018-01-20 18:21:05 -05:00
DPRINTF ( E_SPAM , L_XCODE , " Client advertises codecs: %s \n " , client_codecs ) ;
2009-05-01 09:31:59 -04:00
if ( ! client_codecs )
{
2018-01-20 18:21:05 -05:00
DPRINTF ( E_SPAM , L_XCODE , " Could not identify client, using default codectype set \n " ) ;
2009-05-01 09:31:59 -04:00
client_codecs = default_codecs ;
}
if ( strstr ( client_codecs , file_codectype ) )
{
2018-01-20 18:21:05 -05:00
DPRINTF ( E_SPAM , L_XCODE , " Codectype supported by client, no decoding needed \n " ) ;
2009-05-01 09:31:59 -04:00
return 0 ;
}
2018-01-20 18:21:05 -05:00
DPRINTF ( E_SPAM , L_XCODE , " Will decode \n " ) ;
2009-05-01 09:31:59 -04:00
return 1 ;
}
2015-03-14 16:42:53 -04:00
2015-10-09 17:58:27 -04:00
/* Cleanup */
2015-03-14 16:42:53 -04:00
void
2017-02-26 17:41:30 -05:00
transcode_decode_cleanup ( struct decode_ctx * * ctx )
2015-03-14 16:42:53 -04:00
{
2017-02-26 17:41:30 -05:00
if ( ! ( * ctx ) )
return ;
close_input ( * ctx ) ;
av_packet_free ( & ( * ctx ) - > packet ) ;
av_frame_free ( & ( * ctx ) - > decoded_frame ) ;
free ( * ctx ) ;
* ctx = NULL ;
2015-10-09 17:58:27 -04:00
}
2015-03-14 16:42:53 -04:00
2015-10-09 17:58:27 -04:00
void
2017-02-26 17:41:30 -05:00
transcode_encode_cleanup ( struct encode_ctx * * ctx )
2015-10-09 17:58:27 -04:00
{
2017-02-26 17:41:30 -05:00
if ( ! * ctx )
return ;
close_filters ( * ctx ) ;
close_output ( * ctx ) ;
2017-02-26 11:50:04 -05:00
2017-02-26 17:41:30 -05:00
av_packet_free ( & ( * ctx ) - > encoded_pkt ) ;
av_frame_free ( & ( * ctx ) - > filt_frame ) ;
free ( * ctx ) ;
* ctx = NULL ;
2015-10-09 17:58:27 -04:00
}
void
2017-02-26 17:41:30 -05:00
transcode_cleanup ( struct transcode_ctx * * ctx )
2015-10-09 17:58:27 -04:00
{
2019-02-08 12:53:40 -05:00
if ( ! * ctx )
return ;
2017-02-26 17:41:30 -05:00
transcode_encode_cleanup ( & ( * ctx ) - > encode_ctx ) ;
transcode_decode_cleanup ( & ( * ctx ) - > decode_ctx ) ;
free ( * ctx ) ;
* ctx = NULL ;
2015-10-09 17:58:27 -04:00
}
/* Encoding, decoding and transcoding */
2015-10-22 16:09:19 -04:00
int
2019-01-10 04:52:56 -05:00
transcode_decode ( transcode_frame * * frame , struct decode_ctx * dec_ctx )
2015-10-09 17:58:27 -04:00
{
2017-02-28 17:06:01 -05:00
struct transcode_ctx ctx ;
int ret ;
if ( dec_ctx - > got_frame )
DPRINTF ( E_LOG , L_XCODE , " Bug! Currently no support for multiple calls to transcode_decode() \n " ) ;
ctx . decode_ctx = dec_ctx ;
ctx . encode_ctx = NULL ;
do
{
// This function stops after decoding because ctx->encode_ctx is NULL
ret = read_decode_filter_encode_write ( & ctx ) ;
}
while ( ( ret = = 0 ) & & ( ! dec_ctx - > got_frame ) ) ;
if ( ret < 0 )
return - 1 ;
* frame = dec_ctx - > decoded_frame ;
if ( dec_ctx - > eof )
return 0 ;
return 1 ;
2015-10-09 17:58:27 -04:00
}
// Filters and encodes
int
2019-01-10 04:52:56 -05:00
transcode_encode ( struct evbuffer * evbuf , struct encode_ctx * ctx , transcode_frame * frame , int eof )
2015-10-09 17:58:27 -04:00
{
2017-02-28 17:06:01 -05:00
AVFrame * f = frame ;
2017-02-26 09:32:37 -05:00
struct stream_ctx * s ;
2017-02-26 17:41:30 -05:00
size_t start_length ;
2015-10-09 17:58:27 -04:00
int ret ;
2017-02-26 17:41:30 -05:00
start_length = evbuffer_get_length ( ctx - > obuf ) ;
2015-10-09 17:58:27 -04:00
2017-02-28 17:06:01 -05:00
// Really crappy way of detecting if frame is audio, video or something else
if ( f - > channel_layout & & f - > sample_rate )
2017-02-26 09:32:37 -05:00
s = & ctx - > audio_stream ;
2017-02-28 17:06:01 -05:00
else if ( f - > width & & f - > height )
2017-02-26 09:32:37 -05:00
s = & ctx - > video_stream ;
else
2017-02-28 17:06:01 -05:00
{
DPRINTF ( E_LOG , L_XCODE , " Bug! Encoder could not detect frame type \n " ) ;
return - 1 ;
}
2015-10-09 17:58:27 -04:00
2017-02-28 17:06:01 -05:00
ret = filter_encode_write ( ctx , s , f ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2017-02-26 17:41:30 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error occurred while encoding: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
return ret ;
}
2017-02-28 17:06:01 -05:00
// Flush
if ( eof )
{
filter_encode_write ( ctx , s , NULL ) ;
av_write_trailer ( ctx - > ofmt_ctx ) ;
}
2017-02-26 17:41:30 -05:00
ret = evbuffer_get_length ( ctx - > obuf ) - start_length ;
2015-10-09 17:58:27 -04:00
evbuffer_add_buffer ( evbuf , ctx - > obuf ) ;
2017-02-26 17:41:30 -05:00
return ret ;
2015-10-09 17:58:27 -04:00
}
int
2017-02-28 17:06:01 -05:00
transcode ( struct evbuffer * evbuf , int * icy_timer , struct transcode_ctx * ctx , int want_bytes )
2015-10-09 17:58:27 -04:00
{
2017-02-26 17:41:30 -05:00
size_t start_length ;
int processed = 0 ;
2015-10-09 17:58:27 -04:00
int ret ;
2017-02-28 17:06:01 -05:00
if ( icy_timer )
* icy_timer = 0 ;
2017-01-22 17:23:18 -05:00
2017-02-27 14:42:07 -05:00
if ( ctx - > decode_ctx - > eof )
2017-02-26 17:41:30 -05:00
return 0 ;
2015-10-09 17:58:27 -04:00
2017-02-26 17:41:30 -05:00
start_length = evbuffer_get_length ( ctx - > encode_ctx - > obuf ) ;
2015-10-09 17:58:27 -04:00
2017-02-26 17:41:30 -05:00
do
{
ret = read_decode_filter_encode_write ( ctx ) ;
processed = evbuffer_get_length ( ctx - > encode_ctx - > obuf ) - start_length ;
2015-10-09 17:58:27 -04:00
}
2017-02-26 17:41:30 -05:00
while ( ( ret = = 0 ) & & ( ! want_bytes | | ( processed < want_bytes ) ) ) ;
evbuffer_add_buffer ( evbuf , ctx - > encode_ctx - > obuf ) ;
2015-10-09 17:58:27 -04:00
ctx - > encode_ctx - > total_bytes + = processed ;
2017-02-28 17:06:01 -05:00
if ( icy_timer & & ctx - > encode_ctx - > icy_interval )
2017-01-22 17:23:18 -05:00
* icy_timer = ( ctx - > encode_ctx - > total_bytes % ctx - > encode_ctx - > icy_interval < processed ) ;
2015-10-09 17:58:27 -04:00
2017-03-04 12:30:19 -05:00
if ( ( ret < 0 ) & & ( ret ! = AVERROR_EOF ) )
2017-02-26 17:41:30 -05:00
return ret ;
2015-10-09 17:58:27 -04:00
return processed ;
}
2019-01-10 04:52:56 -05:00
transcode_frame *
2019-04-02 16:47:11 -04:00
transcode_frame_new ( void * data , size_t size , int nsamples , struct media_quality * quality )
2015-10-09 17:58:27 -04:00
{
2017-02-28 17:06:01 -05:00
AVFrame * f ;
2016-09-25 16:01:07 -04:00
int ret ;
2015-10-09 17:58:27 -04:00
2017-02-28 17:06:01 -05:00
f = av_frame_alloc ( ) ;
if ( ! f )
2015-10-09 17:58:27 -04:00
{
2016-11-19 17:08:50 -05:00
DPRINTF ( E_LOG , L_XCODE , " Out of memory for frame \n " ) ;
2015-10-09 17:58:27 -04:00
return NULL ;
}
2019-04-02 16:47:11 -04:00
f - > format = bitdepth2format ( quality - > bits_per_sample ) ;
if ( f - > format = = AV_SAMPLE_FMT_NONE )
2019-02-08 12:53:40 -05:00
{
2019-04-02 16:47:11 -04:00
DPRINTF ( E_LOG , L_XCODE , " transcode_frame_new() called with unsupported bps (%d) \n " , quality - > bits_per_sample ) ;
2019-02-08 12:53:40 -05:00
av_frame_free ( & f ) ;
return NULL ;
}
2019-04-02 16:47:11 -04:00
f - > sample_rate = quality - > sample_rate ;
2019-02-08 12:53:40 -05:00
f - > nb_samples = nsamples ;
2019-04-02 16:47:11 -04:00
f - > channel_layout = av_get_default_channel_layout ( quality - > channels ) ;
2016-03-20 14:22:16 -04:00
# ifdef HAVE_FFMPEG
2019-04-02 16:47:11 -04:00
f - > channels = quality - > channels ;
2016-03-20 14:22:16 -04:00
# endif
2017-02-28 17:06:01 -05:00
f - > pts = AV_NOPTS_VALUE ;
2015-10-09 17:58:27 -04:00
2019-01-10 04:52:56 -05:00
// We don't align because the frame won't be given directly to the encoder
// anyway, it will first go through the filter (which might align it...?)
2019-05-19 16:45:39 -04:00
ret = avcodec_fill_audio_frame ( f , quality - > channels , f - > format , data , size , 1 ) ;
2016-09-25 16:01:07 -04:00
if ( ret < 0 )
2015-10-09 17:58:27 -04:00
{
2019-05-19 16:45:39 -04:00
DPRINTF ( E_LOG , L_XCODE , " Error filling frame with rawbuf, size %zu, samples %d (%d/%d/%d): %s \n " ,
size , nsamples , quality - > sample_rate , quality - > bits_per_sample , quality - > channels , err2str ( ret ) ) ;
2017-02-28 17:06:01 -05:00
av_frame_free ( & f ) ;
2015-10-09 17:58:27 -04:00
return NULL ;
}
2017-02-28 17:06:01 -05:00
return f ;
}
void
2019-01-10 04:52:56 -05:00
transcode_frame_free ( transcode_frame * frame )
2017-02-28 17:06:01 -05:00
{
AVFrame * f = frame ;
av_frame_free ( & f ) ;
2015-10-09 17:58:27 -04:00
}
/* Seeking */
int
transcode_seek ( struct transcode_ctx * ctx , int ms )
{
2017-02-26 09:32:37 -05:00
struct decode_ctx * dec_ctx = ctx - > decode_ctx ;
struct stream_ctx * s ;
2015-10-09 17:58:27 -04:00
int64_t start_time ;
int64_t target_pts ;
int64_t got_pts ;
int got_ms ;
int ret ;
2017-02-26 09:32:37 -05:00
s = & dec_ctx - > audio_stream ;
if ( ! s - > stream )
{
DPRINTF ( E_LOG , L_XCODE , " Could not seek in non-audio input \n " ) ;
return - 1 ;
}
start_time = s - > stream - > start_time ;
2015-10-09 17:58:27 -04:00
target_pts = ms ;
target_pts = target_pts * AV_TIME_BASE / 1000 ;
2017-02-26 09:32:37 -05:00
target_pts = av_rescale_q ( target_pts , AV_TIME_BASE_Q , s - > stream - > time_base ) ;
2015-10-09 17:58:27 -04:00
if ( ( start_time ! = AV_NOPTS_VALUE ) & & ( start_time > 0 ) )
target_pts + = start_time ;
2017-02-26 09:32:37 -05:00
ret = av_seek_frame ( dec_ctx - > ifmt_ctx , s - > stream - > index , target_pts , AVSEEK_FLAG_BACKWARD ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2016-09-25 16:01:07 -04:00
DPRINTF ( E_WARN , L_XCODE , " Could not seek into stream: %s \n " , err2str ( ret ) ) ;
2015-10-09 17:58:27 -04:00
return - 1 ;
}
2017-02-26 09:32:37 -05:00
avcodec_flush_buffers ( s - > codec ) ;
2015-10-09 17:58:27 -04:00
// Fast forward until first packet with a timestamp is found
2017-02-26 09:32:37 -05:00
s - > codec - > skip_frame = AVDISCARD_NONREF ;
2015-10-09 17:58:27 -04:00
while ( 1 )
{
2017-02-26 09:32:37 -05:00
dec_ctx - > timestamp = av_gettime ( ) ;
2015-12-09 14:00:09 -05:00
2017-02-26 17:41:30 -05:00
av_packet_unref ( dec_ctx - > packet ) ;
ret = av_read_frame ( dec_ctx - > ifmt_ctx , dec_ctx - > packet ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2016-09-25 16:01:07 -04:00
DPRINTF ( E_WARN , L_XCODE , " Could not read more data while seeking: %s \n " , err2str ( ret ) ) ;
2017-02-26 09:32:37 -05:00
s - > codec - > skip_frame = AVDISCARD_DEFAULT ;
2015-10-09 17:58:27 -04:00
return - 1 ;
}
2017-02-26 17:41:30 -05:00
if ( stream_find ( dec_ctx , dec_ctx - > packet - > stream_index ) = = AVMEDIA_TYPE_UNKNOWN )
2015-10-09 17:58:27 -04:00
continue ;
// Need a pts to return the real position
2017-02-26 17:41:30 -05:00
if ( dec_ctx - > packet - > pts = = AV_NOPTS_VALUE )
2015-10-09 17:58:27 -04:00
continue ;
break ;
}
2017-02-26 09:32:37 -05:00
s - > codec - > skip_frame = AVDISCARD_DEFAULT ;
2015-10-09 17:58:27 -04:00
2017-02-26 17:41:30 -05:00
// Tell read_packet() to resume with dec_ctx->packet
2017-02-26 09:32:37 -05:00
dec_ctx - > resume = 1 ;
2015-10-09 17:58:27 -04:00
// Compute position in ms from pts
2017-02-26 17:41:30 -05:00
got_pts = dec_ctx - > packet - > pts ;
2015-10-09 17:58:27 -04:00
if ( ( start_time ! = AV_NOPTS_VALUE ) & & ( start_time > 0 ) )
got_pts - = start_time ;
2017-02-26 09:32:37 -05:00
got_pts = av_rescale_q ( got_pts , s - > stream - > time_base , AV_TIME_BASE_Q ) ;
2015-10-09 17:58:27 -04:00
got_ms = got_pts / ( AV_TIME_BASE / 1000 ) ;
2016-12-26 18:02:02 -05:00
// Since negative return would mean error, we disallow it here
if ( got_ms < 0 )
got_ms = 0 ;
2015-10-09 17:58:27 -04:00
DPRINTF ( E_DBG , L_XCODE , " Seek wanted %d ms, got %d ms \n " , ms , got_ms ) ;
return got_ms ;
}
2017-02-28 17:06:01 -05:00
/* Querying */
int
transcode_decode_query ( struct decode_ctx * ctx , const char * query )
{
if ( strcmp ( query , " width " ) = = 0 )
{
if ( ctx - > video_stream . stream )
return ctx - > video_stream . stream - > codecpar - > width ;
}
else if ( strcmp ( query , " height " ) = = 0 )
{
if ( ctx - > video_stream . stream )
return ctx - > video_stream . stream - > codecpar - > height ;
}
else if ( strcmp ( query , " is_png " ) = = 0 )
{
if ( ctx - > video_stream . stream )
return ( ctx - > video_stream . stream - > codecpar - > codec_id = = AV_CODEC_ID_PNG ) ;
}
else if ( strcmp ( query , " is_jpeg " ) = = 0 )
{
if ( ctx - > video_stream . stream )
return ( ctx - > video_stream . stream - > codecpar - > codec_id = = AV_CODEC_ID_MJPEG ) ;
}
return - 1 ;
}
2015-10-09 17:58:27 -04:00
2019-01-11 13:34:36 -05:00
int
transcode_encode_query ( struct encode_ctx * ctx , const char * query )
{
if ( strcmp ( query , " sample_rate " ) = = 0 )
{
if ( ctx - > audio_stream . stream )
return ctx - > audio_stream . stream - > codecpar - > sample_rate ;
}
else if ( strcmp ( query , " bits_per_sample " ) = = 0 )
{
if ( ctx - > audio_stream . stream )
return av_get_bits_per_sample ( ctx - > audio_stream . stream - > codecpar - > codec_id ) ;
}
2019-02-08 12:53:40 -05:00
else if ( strcmp ( query , " channels " ) = = 0 )
{
if ( ctx - > audio_stream . stream )
return ctx - > audio_stream . stream - > codecpar - > channels ;
}
2019-01-11 13:34:36 -05:00
return - 1 ;
}
2015-10-09 17:58:27 -04:00
/* Metadata */
struct http_icy_metadata *
transcode_metadata ( struct transcode_ctx * ctx , int * changed )
{
struct http_icy_metadata * m ;
if ( ! ctx - > decode_ctx - > ifmt_ctx )
return NULL ;
m = http_icy_metadata_get ( ctx - > decode_ctx - > ifmt_ctx , 1 ) ;
2015-03-29 14:16:56 -04:00
if ( ! m )
2015-10-09 17:58:27 -04:00
return NULL ;
2015-03-14 16:42:53 -04:00
2015-10-09 17:58:27 -04:00
* changed = ( m - > hash ! = ctx - > encode_ctx - > icy_hash ) ;
2015-03-14 16:42:53 -04:00
2015-10-09 17:58:27 -04:00
ctx - > encode_ctx - > icy_hash = m - > hash ;
2015-03-14 16:42:53 -04:00
2015-10-09 17:58:27 -04:00
return m ;
2015-03-14 16:42:53 -04:00
}