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"
2019-01-10 04:52:56 -05:00
# include "misc.h"
2009-05-01 09:31:59 -04:00
# include "transcode.h"
2022-10-22 10:35:01 -04:00
// Switches for compability with ffmpeg's ever changing API
# define USE_IMAGE2PIPE (LIBAVFORMAT_VERSION_MAJOR > 58) || ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR > 29))
# define USE_CONST_AVFORMAT (LIBAVFORMAT_VERSION_MAJOR > 59) || ((LIBAVFORMAT_VERSION_MAJOR == 59) && (LIBAVFORMAT_VERSION_MINOR > 15))
# define USE_CONST_AVCODEC (LIBAVFORMAT_VERSION_MAJOR > 59) || ((LIBAVFORMAT_VERSION_MAJOR == 59) && (LIBAVFORMAT_VERSION_MINOR > 15))
# define USE_NO_CLEAR_AVFMT_NOFILE (LIBAVFORMAT_VERSION_MAJOR > 59) || ((LIBAVFORMAT_VERSION_MAJOR == 59) && (LIBAVFORMAT_VERSION_MINOR > 15))
# define USE_CH_LAYOUT (LIBAVCODEC_VERSION_MAJOR > 59) || ((LIBAVCODEC_VERSION_MAJOR == 59) && (LIBAVCODEC_VERSION_MINOR > 24))
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
2021-04-06 05:44:55 -04:00
// Buffer size for reading/writing input and output evbuffers
# define AVIO_BUFFER_SIZE 4096
2022-10-09 17:46:39 -04:00
// Size of the wav header that iTunes needs
# define WAV_HEADER_LEN 44
2023-02-21 16:53:38 -05:00
// Max filters in a filtergraph
# define MAX_FILTERS 9
2009-05-01 09:31:59 -04:00
2016-09-25 16:01:07 -04:00
static const char * default_codecs = " mpeg,wav " ;
2021-01-29 14:31:53 -05:00
static const char * roku_codecs = " mpeg,mp4a,wma,alac,wav " ;
2016-09-25 16:01:07 -04:00
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 ;
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
AVChannelLayout channel_layout ;
# else
2017-02-26 09:32:37 -05:00
uint64_t channel_layout ;
int channels ;
2022-10-09 17:46:39 -04:00
# endif
2019-08-23 11:22:11 -04:00
int bit_rate ;
2023-11-26 03:47:48 -05:00
int frame_size ;
2017-02-26 09:32:37 -05:00
enum AVSampleFormat sample_format ;
2023-02-21 16:53:38 -05:00
bool with_wav_header ;
bool with_icy ;
bool with_user_filters ;
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
2023-10-20 18:17:20 -04:00
// Source duration in ms as provided by caller
uint32_t len_ms ;
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
2023-10-20 18:17:20 -04:00
off_t bytes_processed ;
// Estimated total size of output
off_t bytes_total ;
2015-10-09 17:58:27 -04:00
// 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
2022-10-09 17:46:39 -04:00
uint8_t wav_header [ WAV_HEADER_LEN ] ;
2009-05-01 09:31:59 -04:00
} ;
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
2021-04-06 05:44:55 -04:00
struct avio_evbuffer {
struct evbuffer * evbuf ;
uint8_t * buffer ;
2021-04-06 10:16:32 -04:00
transcode_seekfn seekfn ;
void * seekfn_arg ;
2021-04-06 05:44:55 -04:00
} ;
2023-02-21 16:53:38 -05:00
struct filter_def
{
char name [ 64 ] ;
char args [ 512 ] ;
} ;
struct filters
{
AVFilterContext * av_ctx ;
// Function that will create the filter arguments for ffmpeg
int ( * deffn ) ( struct filter_def * , struct stream_ctx * , struct stream_ctx * , const char * ) ;
const char * deffn_arg ;
} ;
2021-04-06 05:44:55 -04:00
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
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
settings - > with_icy = true ;
settings - > with_user_filters = true ;
2019-02-08 12:53:40 -05:00
break ;
2023-10-20 18:17:20 -04:00
case XCODE_WAV :
2023-02-21 16:53:38 -05:00
settings - > with_wav_header = true ;
settings - > with_user_filters = true ;
2019-04-02 16:47:11 -04:00
case XCODE_PCM16 :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2017-02-26 09:32:37 -05:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2019-02-08 12:53:40 -05:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2019-02-27 15:58:33 -05:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2017-02-26 09:32:37 -05:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2019-01-10 04:52:56 -05:00
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 ;
2021-01-17 16:45:24 -05:00
case XCODE_ALAC :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2021-01-17 16:45:24 -05:00
settings - > format = " data " ; // Means we get the raw packet from the encoder, no muxing
settings - > audio_codec = AV_CODEC_ID_ALAC ;
settings - > sample_format = AV_SAMPLE_FMT_S16P ;
2023-11-26 03:47:48 -05:00
settings - > frame_size = 352 ;
2021-01-17 16:45:24 -05:00
break ;
2021-03-28 14:00:15 -04:00
case XCODE_OGG :
2023-02-21 16:53:38 -05:00
settings - > encode_audio = true ;
2021-03-28 14:00:15 -04:00
settings - > in_format = " ogg " ;
break ;
2017-02-26 09:32:37 -05:00
case XCODE_JPEG :
2023-02-21 16:53:38 -05:00
settings - > encode_video = true ;
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.
2022-10-22 10:35:01 -04:00
# if USE_IMAGE2PIPE
2020-07-19 17:52:42 -04:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_video = true ;
settings - > silent = true ;
2020-07-19 17:52:42 -04:00
// See explanation above
2022-10-22 10:35:01 -04:00
# if USE_IMAGE2PIPE
2020-07-19 17:52:42 -04:00
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 :
2023-02-21 16:53:38 -05:00
settings - > encode_video = true ;
settings - > silent = true ;
2020-11-05 17:04:56 -05:00
// See explanation above
2022-10-22 10:35:01 -04:00
# if USE_IMAGE2PIPE
2020-11-05 17:04:56 -05:00
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 )
{
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
av_channel_layout_default ( & settings - > channel_layout , quality - > channels ) ;
# else
2019-04-02 16:47:11 -04:00
settings - > channel_layout = av_get_default_channel_layout ( quality - > channels ) ;
2022-10-09 17:46:39 -04:00
settings - > channels = quality - > channels ;
2022-10-06 11:09:01 -04:00
# endif
2019-04-02 16:47:11 -04:00
}
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 ;
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
av_channel_layout_copy ( & s - > codec - > ch_layout , & ( settings - > channel_layout ) ) ;
# else
2017-02-26 09:32:37 -05:00
s - > codec - > channel_layout = settings - > channel_layout ;
s - > codec - > channels = settings - > channels ;
2022-10-06 11:09:01 -04:00
# endif
2017-02-26 09:32:37 -05:00
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 ;
}
2022-10-09 17:46:39 -04:00
/*
* header must have size WAV_HEADER_LEN ( 44 bytes )
*/
2009-05-01 09:31:59 -04:00
static void
2023-10-20 18:17:20 -04:00
make_wav_header ( uint8_t * header , int sample_rate , int bytes_per_sample , int channels , off_t bytes_total )
2009-05-01 09:31:59 -04:00
{
2023-10-20 18:17:20 -04:00
uint32_t wav_size = bytes_total - WAV_HEADER_LEN ;
2022-10-09 17:46:39 -04:00
memcpy ( header , " RIFF " , 4 ) ;
2023-10-20 18:17:20 -04:00
add_le32 ( header + 4 , 36 + wav_size ) ;
2022-10-09 17:46:39 -04:00
memcpy ( header + 8 , " WAVEfmt " , 8 ) ;
add_le32 ( header + 16 , 16 ) ;
add_le16 ( header + 20 , 1 ) ;
add_le16 ( header + 22 , channels ) ; /* channels */
add_le32 ( header + 24 , sample_rate ) ; /* samplerate */
2023-10-20 18:17:20 -04:00
add_le32 ( header + 28 , sample_rate * channels * bytes_per_sample ) ; /* byte rate */
add_le16 ( header + 32 , channels * bytes_per_sample ) ; /* block align */
add_le16 ( header + 34 , 8 * bytes_per_sample ) ; /* bits per sample */
2022-10-09 17:46:39 -04:00
memcpy ( header + 36 , " data " , 4 ) ;
2023-10-20 18:17:20 -04:00
add_le32 ( header + 40 , wav_size ) ;
2009-05-01 09:31:59 -04:00
}
2023-10-20 18:17:20 -04:00
static off_t
size_estimate ( enum transcode_profile profile , int bit_rate , int sample_rate , int bytes_per_sample , int channels , int len_ms )
{
off_t bytes ;
// If the source has a number of samples that doesn't match an even len_ms
// then the length may have been rounded up. We prefer an estimate that is on
// the low side, otherwise ffprobe won't trust the length from our wav header.
if ( len_ms > 0 )
len_ms - = 1 ;
else
len_ms = 3 * 60 * 1000 ;
if ( profile = = XCODE_WAV )
bytes = ( int64_t ) len_ms * channels * bytes_per_sample * sample_rate / 1000 + WAV_HEADER_LEN ;
else if ( profile = = XCODE_MP3 )
bytes = ( int64_t ) len_ms * bit_rate / 8000 ;
else
bytes = - 1 ;
return bytes ;
}
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 ;
2022-10-22 10:35:01 -04:00
# if USE_CONST_AVCODEC
2022-01-20 14:03:22 -05:00
const AVCodec * encoder ;
# else
// Not const before ffmpeg 5.0
2017-02-26 09:32:37 -05:00
AVCodec * encoder ;
2022-01-20 14:03:22 -05:00
# endif
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 ;
}
2023-10-20 18:17:20 -04:00
DPRINTF ( E_DBG , L_XCODE , " Selected encoder '%s' \n " , encoder - > long_name ) ;
2017-02-26 09:32:37 -05:00
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 ) ) ;
2023-02-05 11:11:01 -05:00
goto error ;
2017-02-26 09:32:37 -05:00
}
2023-08-17 17:09:41 -04:00
// airplay.c "misuses" the ffmpeg alac encoder in that it pushes frames with
// 352 samples even though the encoder wants 4096 (and doesn't have variable
// frame capability). This worked with no issues until ffmpeg 6, where it
// seems a frame size check was added. The below circumvents the check, but is
// dirty because we shouldn't be writing to this data element.
2023-11-26 03:47:48 -05:00
if ( ctx - > settings . frame_size )
s - > codec - > frame_size = ctx - > settings . frame_size ;
2023-08-17 17:09:41 -04:00
2017-02-26 09:32:37 -05:00
// 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 ) ) ;
2023-02-05 11:11:01 -05:00
goto error ;
}
if ( options )
{
DPRINTF ( E_WARN , L_XCODE , " Encoder %s didn't recognize all options given to avcodec_open2 \n " , codec_desc - > name ) ;
av_dict_free ( & options ) ;
2017-02-26 09:32:37 -05:00
}
return 0 ;
2023-02-05 11:11:01 -05:00
error :
if ( s - > codec )
avcodec_free_context ( & s - > codec ) ;
if ( options )
av_dict_free ( & options ) ;
return - 1 ;
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
}
2021-04-06 05:44:55 -04:00
/* ------------------------------- CUSTOM I/O ------------------------------ */
/* For using ffmpeg with evbuffer input/output instead of files */
static int
avio_evbuffer_read ( void * opaque , uint8_t * buf , int size )
{
struct avio_evbuffer * ae = ( struct avio_evbuffer * ) opaque ;
int ret ;
ret = evbuffer_remove ( ae - > evbuf , buf , size ) ;
// Must return AVERROR, see avio.h: avio_alloc_context()
return ( ret > 0 ) ? ret : AVERROR_EOF ;
}
static int
avio_evbuffer_write ( void * opaque , uint8_t * buf , int size )
{
struct avio_evbuffer * ae = ( struct avio_evbuffer * ) opaque ;
int ret ;
ret = evbuffer_add ( ae - > evbuf , buf , size ) ;
return ( ret = = 0 ) ? size : - 1 ;
}
2021-04-06 10:16:32 -04:00
static int64_t
avio_evbuffer_seek ( void * opaque , int64_t offset , int whence )
{
struct avio_evbuffer * ae = ( struct avio_evbuffer * ) opaque ;
enum transcode_seek_type seek_type ;
// Caller shouldn't need to know about ffmpeg defines
if ( whence & AVSEEK_SIZE )
seek_type = XCODE_SEEK_SIZE ;
else if ( whence = = SEEK_SET )
seek_type = XCODE_SEEK_SET ;
else if ( whence = = SEEK_CUR )
seek_type = XCODE_SEEK_CUR ;
else
return - 1 ;
return ae - > seekfn ( ae - > seekfn_arg , offset , seek_type ) ;
}
2021-04-06 05:44:55 -04:00
static AVIOContext *
2021-04-06 10:16:32 -04:00
avio_evbuffer_open ( struct transcode_evbuf_io * evbuf_io , int is_output )
2021-04-06 05:44:55 -04:00
{
struct avio_evbuffer * ae ;
AVIOContext * s ;
ae = calloc ( 1 , sizeof ( struct avio_evbuffer ) ) ;
if ( ! ae )
{
DPRINTF ( E_LOG , L_FFMPEG , " Out of memory for avio_evbuffer \n " ) ;
return NULL ;
}
ae - > buffer = av_mallocz ( AVIO_BUFFER_SIZE ) ;
if ( ! ae - > buffer )
{
DPRINTF ( E_LOG , L_FFMPEG , " Out of memory for avio buffer \n " ) ;
free ( ae ) ;
return NULL ;
}
2021-04-06 10:16:32 -04:00
ae - > evbuf = evbuf_io - > evbuf ;
ae - > seekfn = evbuf_io - > seekfn ;
ae - > seekfn_arg = evbuf_io - > seekfn_arg ;
2021-04-06 05:44:55 -04:00
if ( is_output )
s = avio_alloc_context ( ae - > buffer , AVIO_BUFFER_SIZE , 1 , ae , NULL , avio_evbuffer_write , NULL ) ;
else
2021-04-06 10:16:32 -04:00
s = avio_alloc_context ( ae - > buffer , AVIO_BUFFER_SIZE , 0 , ae , avio_evbuffer_read , NULL , ( evbuf_io - > seekfn ? avio_evbuffer_seek : NULL ) ) ;
2021-04-06 05:44:55 -04:00
if ( ! s )
{
DPRINTF ( E_LOG , L_FFMPEG , " Could not allocate AVIOContext \n " ) ;
av_free ( ae - > buffer ) ;
free ( ae ) ;
return NULL ;
}
2021-04-08 16:53:19 -04:00
s - > seekable = ( evbuf_io - > seekfn ? AVIO_SEEKABLE_NORMAL : 0 ) ;
2021-04-06 05:44:55 -04:00
return s ;
}
static AVIOContext *
2021-04-06 10:16:32 -04:00
avio_input_evbuffer_open ( struct transcode_evbuf_io * evbuf_io )
2021-04-06 05:44:55 -04:00
{
2021-04-06 10:16:32 -04:00
return avio_evbuffer_open ( evbuf_io , 0 ) ;
2021-04-06 05:44:55 -04:00
}
static AVIOContext *
avio_output_evbuffer_open ( struct evbuffer * evbuf )
{
2021-04-06 10:16:32 -04:00
struct transcode_evbuf_io evbuf_io = { 0 } ;
evbuf_io . evbuf = evbuf ;
return avio_evbuffer_open ( & evbuf_io , 1 ) ;
2021-04-06 05:44:55 -04:00
}
static void
avio_evbuffer_close ( AVIOContext * s )
{
struct avio_evbuffer * ae ;
if ( ! s )
return ;
ae = ( struct avio_evbuffer * ) s - > opaque ;
avio_flush ( s ) ;
av_free ( s - > buffer ) ;
free ( ae ) ;
av_free ( s ) ;
}
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
{
2022-10-22 10:35:01 -04:00
# if USE_CONST_AVCODEC
2022-01-20 14:03:22 -05:00
const AVCodec * decoder ;
# else
// Not const before ffmpeg 5.0
2017-02-28 17:06:01 -05:00
AVCodec * decoder ;
2022-01-20 14:03:22 -05:00
# endif
2017-02-28 17:06:01 -05:00
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
2023-02-21 16:53:38 -05:00
// Filter creation will need 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
2017-02-28 17:06:01 -05:00
// 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
}
2022-10-22 11:14:26 -04: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
2021-04-06 10:16:32 -04:00
open_input ( struct decode_ctx * ctx , const char * path , struct transcode_evbuf_io * evbuf_io , 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 ;
2022-10-22 10:35:01 -04:00
# if USE_CONST_AVFORMAT
2022-01-20 14:03:22 -05:00
const AVInputFormat * ifmt ;
# else
// Not const before ffmpeg 5.0
2017-03-01 15:29:08 -05:00
AVInputFormat * ifmt ;
2022-01-20 14:03:22 -05:00
# endif
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 ( ) ;
2021-04-06 10:16:32 -04:00
if ( evbuf_io )
2017-03-01 15:29:08 -05:00
{
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
}
2021-04-06 10:16:32 -04:00
CHECK_NULL ( L_XCODE , ctx - > avio = avio_input_evbuffer_open ( evbuf_io ) ) ;
2017-03-01 15:29:08 -05:00
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
}
2023-02-21 16:53:38 -05:00
// If the source has REPLAYGAIN_TRACK_GAIN metadata, this will inject the
// values into the the next packet's side data (as AV_FRAME_DATA_REPLAYGAIN),
// which has the effect that a volume replaygain filter works. Note that
// ffmpeg itself uses another method in process_input() in ffmpeg.c.
av_format_inject_global_side_data ( ctx - > ifmt_ctx ) ;
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 )
{
2022-10-22 10:35:01 -04:00
# if USE_CONST_AVFORMAT
2022-01-20 14:03:22 -05:00
const AVOutputFormat * oformat ;
# else
// Not const before ffmpeg 5.0
2017-03-04 03:40:29 -05:00
AVOutputFormat * oformat ;
2022-01-20 14:03:22 -05:00
# endif
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
2022-10-22 10:35:01 -04:00
# if USE_NO_CLEAR_AVFMT_NOFILE
2022-01-20 14:03:22 -05:00
CHECK_ERRNO ( L_XCODE , avformat_alloc_output_context2 ( & ctx - > ofmt_ctx , oformat , NULL , NULL ) ) ;
# else
// Clear AVFMT_NOFILE bit, it is not allowed as we will set our own AVIOContext.
// If this is not done with e.g. ffmpeg 3.4 then artwork rescaling will fail.
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 ;
2022-01-20 14:03:22 -05:00
# endif
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
2023-02-21 16:53:38 -05:00
if ( ctx - > settings . with_wav_header )
2017-02-26 17:41:30 -05:00
{
2022-10-09 17:46:39 -04:00
evbuffer_add ( ctx - > obuf , ctx - > wav_header , sizeof ( ctx - > wav_header ) ) ;
2017-02-26 17:41:30 -05:00
}
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
2023-02-21 16:53:38 -05:00
filter_def_abuffer ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
2015-10-09 17:58:27 -04:00
{
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
char buf [ 64 ] ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
// Some AIFF files only have a channel number, not a layout
if ( in_stream - > codec - > ch_layout . order = = AV_CHANNEL_ORDER_UNSPEC )
av_channel_layout_default ( & in_stream - > codec - > ch_layout , in_stream - > codec - > ch_layout . nb_channels ) ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
av_channel_layout_describe ( & in_stream - > codec - > ch_layout , buf , sizeof ( buf ) ) ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
snprintf ( def - > args , sizeof ( def - > args ) ,
" time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s " ,
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 ) ,
buf ) ;
# else
if ( ! in_stream - > codec - > channel_layout )
in_stream - > codec - > channel_layout = av_get_default_channel_layout ( in_stream - > codec - > channels ) ;
snprintf ( def - > args , sizeof ( def - > args ) ,
" time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x% " PRIx64 ,
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 ) ;
# endif
snprintf ( def - > name , sizeof ( def - > name ) , " abuffer " ) ;
return 0 ;
}
static int
filter_def_aformat ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2023-02-21 16:53:38 -05:00
char buf [ 64 ] ;
if ( out_stream - > codec - > ch_layout . order = = AV_CHANNEL_ORDER_UNSPEC )
av_channel_layout_default ( & out_stream - > codec - > ch_layout , out_stream - > codec - > ch_layout . nb_channels ) ;
2022-10-09 17:46:39 -04:00
2023-02-21 16:53:38 -05:00
av_channel_layout_describe ( & out_stream - > codec - > ch_layout , buf , sizeof ( buf ) ) ;
2022-10-06 11:09:01 -04:00
2023-02-21 16:53:38 -05:00
snprintf ( def - > args , sizeof ( def - > args ) ,
" sample_fmts=%s:sample_rates=%d:channel_layouts=%s " ,
av_get_sample_fmt_name ( out_stream - > codec - > sample_fmt ) , out_stream - > codec - > sample_rate ,
buf ) ;
2022-10-06 11:09:01 -04:00
# else
2023-02-21 16:53:38 -05: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 ) ;
snprintf ( def - > args , sizeof ( def - > 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 ) ;
2022-10-06 11:09:01 -04:00
# endif
2023-02-21 16:53:38 -05:00
snprintf ( def - > name , sizeof ( def - > name ) , " aformat " ) ;
return 0 ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
static int
filter_def_abuffersink ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
snprintf ( def - > name , sizeof ( def - > name ) , " abuffersink " ) ;
* def - > args = ' \0 ' ;
return 0 ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
static int
filter_def_buffer ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
snprintf ( def - > name , sizeof ( def - > name ) , " buffer " ) ;
snprintf ( def - > args , sizeof ( def - > args ) ,
" 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 ) ,
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 ) ;
return 0 ;
}
2019-02-08 12:53:40 -05:00
2023-02-21 16:53:38 -05:00
static int
filter_def_format ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
snprintf ( def - > name , sizeof ( def - > name ) , " format " ) ;
snprintf ( def - > args , sizeof ( def - > args ) ,
" pix_fmts=%s " , av_get_pix_fmt_name ( out_stream - > codec - > pix_fmt ) ) ;
return 0 ;
}
2022-10-09 17:46:39 -04:00
2023-02-21 16:53:38 -05:00
static int
filter_def_scale ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
snprintf ( def - > name , sizeof ( def - > name ) , " scale " ) ;
snprintf ( def - > args , sizeof ( def - > args ) ,
" w=%d:h=%d " , out_stream - > codec - > width , out_stream - > codec - > height ) ;
return 0 ;
}
2022-10-06 11:09:01 -04:00
2023-02-21 16:53:38 -05:00
static int
filter_def_buffersink ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
snprintf ( def - > name , sizeof ( def - > name ) , " buffersink " ) ;
* def - > args = ' \0 ' ;
return 0 ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
static int
filter_def_user ( struct filter_def * def , struct stream_ctx * out_stream , struct stream_ctx * in_stream , const char * deffn_arg )
{
char * ptr ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
snprintf ( def - > name , sizeof ( def - > name ) , " %s " , deffn_arg ) ;
2019-02-08 12:53:40 -05:00
2023-02-21 16:53:38 -05:00
ptr = strchr ( def - > name , ' = ' ) ;
if ( ptr )
{
* ptr = ' \0 ' ;
snprintf ( def - > args , sizeof ( def - > args ) , " %s " , ptr + 1 ) ;
}
else
* def - > args = ' \0 ' ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
return 0 ;
}
static int
define_audio_filters ( struct filters * filters , size_t filters_len , bool with_user_filters )
{
int num_user_filters ;
int i ;
num_user_filters = cfg_size ( cfg_getsec ( cfg , " library " ) , " decode_audio_filters " ) ;
if ( filters_len < num_user_filters + 3 )
{
DPRINTF ( E_LOG , L_XCODE , " Too many audio filters configured (%d, max is %zu) \n " , num_user_filters , filters_len - 3 ) ;
return - 1 ;
2015-10-09 17:58:27 -04:00
}
2023-02-21 16:53:38 -05:00
filters [ 0 ] . deffn = filter_def_abuffer ;
for ( i = 0 ; with_user_filters & & i < num_user_filters ; i + + )
2015-10-09 17:58:27 -04:00
{
2023-02-21 16:53:38 -05:00
filters [ 1 + i ] . deffn = filter_def_user ;
filters [ 1 + i ] . deffn_arg = cfg_getnstr ( cfg_getsec ( cfg , " library " ) , " decode_audio_filters " , i ) ;
}
filters [ 1 + i ] . deffn = filter_def_aformat ;
filters [ 2 + i ] . deffn = filter_def_abuffersink ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
return 0 ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
static int
define_video_filters ( struct filters * filters , size_t filters_len , bool with_user_filters )
{
int num_user_filters ;
int i ;
num_user_filters = cfg_size ( cfg_getsec ( cfg , " library " ) , " decode_video_filters " ) ;
if ( filters_len < num_user_filters + 3 )
{
DPRINTF ( E_LOG , L_XCODE , " Too many video filters configured (%d, max is %zu) \n " , num_user_filters , filters_len - 3 ) ;
return - 1 ;
}
filters [ 0 ] . deffn = filter_def_buffer ;
for ( i = 0 ; with_user_filters & & i < num_user_filters ; i + + )
{
filters [ 1 + i ] . deffn = filter_def_user ;
filters [ 1 + i ] . deffn_arg = cfg_getnstr ( cfg_getsec ( cfg , " library " ) , " decode_video_filters " , i ) ;
}
filters [ 1 + i ] . deffn = filter_def_format ;
filters [ 2 + i ] . deffn = filter_def_scale ;
filters [ 3 + i ] . deffn = filter_def_buffersink ;
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
return 0 ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
static int
add_filters ( int * num_added , AVFilterGraph * filter_graph , struct filters * filters , size_t filters_len ,
struct stream_ctx * out_stream , struct stream_ctx * in_stream )
{
const AVFilter * av_filter ;
struct filter_def def ;
int i ;
int ret ;
for ( i = 0 ; i < filters_len & & filters [ i ] . deffn ; i + + )
{
ret = filters [ i ] . deffn ( & def , out_stream , in_stream , filters [ i ] . deffn_arg ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2023-02-21 16:53:38 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error creating filter definition \n " ) ;
return - 1 ;
2015-10-09 17:58:27 -04:00
}
2023-02-21 16:53:38 -05:00
av_filter = avfilter_get_by_name ( def . name ) ;
if ( ! av_filter )
2015-10-09 17:58:27 -04:00
{
2023-02-21 16:53:38 -05:00
DPRINTF ( E_LOG , L_XCODE , " Could not find filter '%s' \n " , def . name ) ;
return - 1 ;
2015-10-09 17:58:27 -04:00
}
2023-02-21 16:53:38 -05:00
ret = avfilter_graph_create_filter ( & filters [ i ] . av_ctx , av_filter , def . name , def . args , NULL , filter_graph ) ;
2015-10-09 17:58:27 -04:00
if ( ret < 0 )
{
2023-02-21 16:53:38 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error creating filter '%s': %s \n " , def . name , err2str ( ret ) ) ;
return - 1 ;
2015-10-09 17:58:27 -04:00
}
2023-02-21 16:53:38 -05:00
DPRINTF ( E_DBG , L_XCODE , " Created '%s' filter: '%s' \n " , def . name , def . args ) ;
if ( i = = 0 )
continue ;
ret = avfilter_link ( filters [ i - 1 ] . av_ctx , 0 , filters [ i ] . av_ctx , 0 ) ;
if ( ret < 0 )
2015-10-09 17:58:27 -04:00
{
2023-02-21 16:53:38 -05:00
DPRINTF ( E_LOG , L_XCODE , " Error connecting filters: %s \n " , err2str ( ret ) ) ;
return - 1 ;
2015-10-09 17:58:27 -04:00
}
}
2023-02-21 16:53:38 -05:00
* num_added = i ;
return 0 ;
}
static int
create_filtergraph ( struct stream_ctx * out_stream , struct filters * filters , size_t filters_len , struct stream_ctx * in_stream )
{
AVFilterGraph * filter_graph ;
int ret ;
int added ;
CHECK_NULL ( L_XCODE , filter_graph = avfilter_graph_alloc ( ) ) ;
ret = add_filters ( & added , filter_graph , filters , filters_len , out_stream , in_stream ) ;
if ( ret < 0 )
2015-10-09 17:58:27 -04:00
{
goto out_fail ;
}
ret = avfilter_graph_config ( filter_graph , NULL ) ;
if ( ret < 0 )
2023-02-21 16:53:38 -05:00
{
DPRINTF ( E_LOG , L_XCODE , " Filter graph config failed: %s \n " , err2str ( ret ) ) ;
goto out_fail ;
}
2015-10-09 17:58:27 -04:00
2023-02-21 16:53:38 -05:00
out_stream - > buffersrc_ctx = filters [ 0 ] . av_ctx ;
out_stream - > buffersink_ctx = filters [ added - 1 ] . av_ctx ;
2017-02-26 09:32:37 -05:00
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 )
{
2023-02-21 16:53:38 -05:00
struct filters filters [ MAX_FILTERS ] = { 0 } ;
2015-10-09 17:58:27 -04:00
int ret ;
2017-02-26 09:32:37 -05:00
if ( ctx - > settings . encode_audio )
2015-10-09 17:58:27 -04:00
{
2023-02-21 16:53:38 -05:00
ret = define_audio_filters ( filters , ARRAY_SIZE ( filters ) , ctx - > settings . with_user_filters ) ;
if ( ret < 0 )
goto out_fail ;
ret = create_filtergraph ( & ctx - > audio_stream , filters , ARRAY_SIZE ( filters ) , & src_ctx - > audio_stream ) ;
2017-02-26 09:32:37 -05:00
if ( ret < 0 )
goto out_fail ;
2023-10-20 18:17:20 -04:00
// Many audio encoders require a fixed frame size. This will ensure that
// the filt_frame from av_buffersink_get_frame has that size (except EOF).
if ( ! ( ctx - > audio_stream . codec - > codec - > capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE ) )
av_buffersink_set_frame_size ( ctx - > audio_stream . buffersink_ctx , ctx - > audio_stream . codec - > frame_size ) ;
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
{
2023-02-21 16:53:38 -05:00
ret = define_video_filters ( filters , ARRAY_SIZE ( filters ) , ctx - > settings . with_user_filters ) ;
if ( ret < 0 )
goto out_fail ;
ret = create_filtergraph ( & ctx - > video_stream , filters , ARRAY_SIZE ( filters ) , & 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 *
2023-10-20 18:17:20 -04:00
transcode_decode_setup ( enum transcode_profile profile , struct media_quality * quality , enum data_kind data_kind , const char * path , struct transcode_evbuf_io * evbuf_io , uint32_t len_ms )
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
2023-10-20 18:17:20 -04:00
ctx - > len_ms = len_ms ;
2017-01-22 17:23:18 -05:00
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 )
{
2021-04-06 10:16:32 -04:00
ret = open_input ( ctx , path , evbuf_io , PROBE_TYPE_QUICK ) ;
2020-11-21 10:29:54 -05:00
// Retry with a default, slower probe size
if ( ret = = AVERROR_STREAM_NOT_FOUND )
2021-04-06 10:16:32 -04:00
ret = open_input ( ctx , path , evbuf_io , PROBE_TYPE_DEFAULT ) ;
2020-11-21 10:29:54 -05:00
}
else
2021-04-06 10:16:32 -04:00
ret = open_input ( ctx , path , evbuf_io , PROBE_TYPE_DEFAULT ) ;
2020-11-21 10:29:54 -05:00
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 *
2023-10-20 18:17:20 -04:00
transcode_encode_setup ( enum transcode_profile profile , struct media_quality * quality , struct decode_ctx * src_ctx , int width , int height )
2015-10-09 17:58:27 -04:00
{
struct encode_ctx * ctx ;
2023-10-20 18:17:20 -04:00
int src_bytes_per_sample ;
int dst_bytes_per_sample ;
2022-10-09 17:46:39 -04:00
int channels ;
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
2022-10-09 17:46:39 -04:00
// Caller did not specify a sample format -> determine from source
2019-01-11 13:34:36 -05:00
if ( ! ctx - > settings . sample_format & & ctx - > settings . encode_audio )
{
2023-10-20 18:17:20 -04:00
src_bytes_per_sample = av_get_bytes_per_sample ( src_ctx - > audio_stream . codec - > sample_fmt ) ;
if ( src_bytes_per_sample = = 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 " ;
}
}
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2019-04-02 16:47:11 -04:00
// Caller did not specify channels -> use same as source
2022-10-09 17:46:39 -04:00
if ( ! av_channel_layout_check ( & ctx - > settings . channel_layout ) & & ctx - > settings . encode_audio )
2019-04-02 16:47:11 -04:00
{
2022-10-06 11:09:01 -04:00
av_channel_layout_copy ( & ctx - > settings . channel_layout , & src_ctx - > audio_stream . codec - > ch_layout ) ;
2022-10-09 17:46:39 -04:00
}
channels = ctx - > settings . channel_layout . nb_channels ;
2022-10-06 11:09:01 -04:00
# else
2022-10-09 17:46:39 -04:00
// Caller did not specify channels -> use same as source
if ( ctx - > settings . channels = = 0 & & ctx - > settings . encode_audio )
{
2019-04-02 16:47:11 -04:00
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 ;
2022-10-09 17:46:39 -04:00
}
channels = ctx - > settings . channels ;
2022-10-06 11:09:01 -04:00
# endif
2022-10-09 17:46:39 -04:00
2023-10-20 18:17:20 -04:00
dst_bytes_per_sample = av_get_bytes_per_sample ( ctx - > settings . sample_format ) ;
ctx - > bytes_total = size_estimate ( profile , ctx - > settings . bit_rate , ctx - > settings . sample_rate , dst_bytes_per_sample , channels , src_ctx - > len_ms ) ;
2019-04-02 16:47:11 -04:00
2023-10-20 18:17:20 -04:00
if ( ctx - > settings . with_wav_header )
make_wav_header ( ctx - > wav_header , ctx - > settings . sample_rate , dst_bytes_per_sample , channels , ctx - > bytes_total ) ;
2023-02-21 16:53:38 -05:00
if ( ctx - > settings . with_icy & & src_ctx - > data_kind = = DATA_KIND_HTTP )
2023-10-20 18:17:20 -04:00
ctx - > icy_interval = METADATA_ICY_INTERVAL * channels * dst_bytes_per_sample * ctx - > settings . sample_rate ;
2017-02-26 17:41:30 -05:00
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
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 *
2023-10-20 18:17:20 -04:00
transcode_setup ( enum transcode_profile profile , struct media_quality * quality , enum data_kind data_kind , const char * path , uint32_t len_ms )
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
2023-10-20 18:17:20 -04:00
ctx - > decode_ctx = transcode_decode_setup ( profile , quality , data_kind , path , NULL , len_ms ) ;
2015-10-09 17:58:27 -04:00
if ( ! ctx - > decode_ctx )
{
free ( ctx ) ;
return NULL ;
}
2023-10-20 18:17:20 -04:00
ctx - > encode_ctx = transcode_encode_setup ( profile , quality , ctx - > decode_ctx , 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 ;
2022-10-22 10:35:01 -04:00
# if USE_CONST_AVCODEC
2022-01-20 14:03:22 -05:00
const AVCodec * decoder ;
# else
// Not const before ffmpeg 5.0
2017-02-26 09:32:37 -05:00
AVCodec * decoder ;
2022-01-20 14:03:22 -05:00
# endif
2017-02-26 09:32:37 -05:00
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
}
2023-10-20 18:17:20 -04:00
enum transcode_profile
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
{
2024-01-01 17:13:15 -05:00
const char * codectype ;
const char * prefer_format ;
2009-05-01 09:31:59 -04:00
cfg_t * lib ;
2023-10-20 18:17:20 -04:00
bool force_xcode ;
2024-01-01 17:13:15 -05:00
bool supports_mpeg ;
bool supports_wav ;
2023-10-20 18:17:20 -04:00
int count ;
2009-05-01 09:31:59 -04:00
int i ;
2014-03-11 18:20:29 -04:00
if ( ! file_codectype )
{
2023-10-20 18:17:20 -04:00
return XCODE_UNKNOWN ;
2014-03-11 18:20:29 -04:00
}
2010-03-19 14:06:47 -04:00
lib = cfg_getsec ( cfg , " library " ) ;
2009-05-01 09:31:59 -04:00
2023-10-20 18:17:20 -04:00
count = cfg_size ( lib , " no_decode " ) ;
for ( i = 0 ; i < count ; i + + )
2009-05-01 09:31:59 -04:00
{
2023-10-20 18:17:20 -04:00
codectype = cfg_getnstr ( lib , " no_decode " , i ) ;
if ( strcmp ( file_codectype , codectype ) = = 0 )
return XCODE_NONE ; // Codectype is in no_decode
2009-05-01 09:31:59 -04:00
}
2023-10-20 18:17:20 -04:00
count = cfg_size ( lib , " force_decode " ) ;
for ( i = 0 , force_xcode = false ; i < count & & ! force_xcode ; i + + )
2009-05-01 09:31:59 -04:00
{
2023-10-20 18:17:20 -04:00
codectype = cfg_getnstr ( lib , " force_decode " , i ) ;
if ( strcmp ( file_codectype , codectype ) = = 0 )
force_xcode = true ; // Codectype is in force_decode
2009-05-01 09:31:59 -04:00
}
2023-10-20 18:17:20 -04:00
if ( ! client_codecs & & user_agent )
2009-05-01 09:31:59 -04:00
{
2023-10-20 18:17:20 -04:00
if ( strncmp ( user_agent , " iTunes " , strlen ( " iTunes " ) ) = = 0 )
client_codecs = itunes_codecs ;
else if ( strncmp ( user_agent , " Music/ " , strlen ( " Music/ " ) ) = = 0 ) // Apple Music, include slash because the name is generic
client_codecs = itunes_codecs ;
else if ( strncmp ( user_agent , " QuickTime " , strlen ( " QuickTime " ) ) = = 0 )
client_codecs = itunes_codecs ; // Use iTunes codecs
else if ( strncmp ( user_agent , " Front%20Row " , strlen ( " Front%20Row " ) ) = = 0 )
client_codecs = itunes_codecs ; // Use iTunes codecs
else if ( strncmp ( user_agent , " AppleCoreMedia " , strlen ( " AppleCoreMedia " ) ) = = 0 )
client_codecs = itunes_codecs ; // Use iTunes codecs
else if ( strncmp ( user_agent , " Roku " , strlen ( " Roku " ) ) = = 0 )
client_codecs = roku_codecs ;
else if ( strncmp ( user_agent , " Hifidelio " , strlen ( " Hifidelio " ) ) = = 0 )
/* 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 XCODE_NONE ;
2009-05-01 09:31:59 -04:00
}
if ( ! client_codecs )
2023-10-20 18:17:20 -04:00
client_codecs = default_codecs ;
else
DPRINTF ( E_SPAM , L_XCODE , " Client advertises codecs: %s \n " , client_codecs ) ;
2009-05-01 09:31:59 -04:00
2023-10-20 18:17:20 -04:00
if ( ! force_xcode & & strstr ( client_codecs , file_codectype ) )
return XCODE_NONE ;
2024-01-01 17:13:15 -05:00
supports_mpeg = strstr ( client_codecs , " mpeg " ) & & avcodec_find_encoder ( AV_CODEC_ID_MP3 ) ;
supports_wav = strstr ( client_codecs , " wav " ) ;
prefer_format = cfg_getstr ( lib , " prefer_format " ) ;
if ( prefer_format )
{
if ( strcmp ( prefer_format , " wav " ) = = 0 & & supports_wav )
return XCODE_WAV ;
else if ( strcmp ( prefer_format , " mpeg " ) = = 0 & & supports_mpeg )
return XCODE_MP3 ;
}
// This order determines the default if user didn't configure a preference.
// The lossless formats are given highest preference.
if ( supports_wav )
2023-10-20 18:17:20 -04:00
return XCODE_WAV ;
2024-01-01 17:13:15 -05:00
else if ( supports_mpeg )
return XCODE_MP3 ;
2023-10-20 18:17:20 -04:00
else
return XCODE_UNKNOWN ;
2009-05-01 09:31:59 -04:00
}
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
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
if ( f - > ch_layout . nb_channels & & f - > sample_rate )
# else
2017-02-28 17:06:01 -05:00
if ( f - > channel_layout & & f - > sample_rate )
2022-10-06 11:09:01 -04:00
# endif
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
2023-10-20 18:17:20 -04:00
ctx - > encode_ctx - > bytes_processed + = processed ;
2017-02-28 17:06:01 -05:00
if ( icy_timer & & ctx - > encode_ctx - > icy_interval )
2023-10-20 18:17:20 -04:00
* icy_timer = ( ctx - > encode_ctx - > bytes_processed % 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 ;
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
av_channel_layout_default ( & f - > ch_layout , quality - > channels ) ;
# else
2019-04-02 16:47:11 -04:00
f - > channel_layout = av_get_default_channel_layout ( quality - > channels ) ;
2022-10-09 17:46:39 -04:00
# ifdef HAVE_FFMPEG
2019-04-02 16:47:11 -04:00
f - > channels = quality - > channels ;
2022-10-09 17:46:39 -04:00
# endif
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 )
2022-10-22 10:35:01 -04:00
# if USE_CH_LAYOUT
2022-10-06 11:09:01 -04:00
return ctx - > audio_stream . stream - > codecpar - > ch_layout . nb_channels ;
# else
2019-02-08 12:53:40 -05:00
return ctx - > audio_stream . stream - > codecpar - > channels ;
2022-10-06 11:09:01 -04:00
# endif
2019-02-08 12:53:40 -05:00
}
2023-05-01 17:32:06 -04:00
else if ( strcmp ( query , " samples_per_frame " ) = = 0 )
{
if ( ctx - > audio_stream . stream )
return ctx - > audio_stream . stream - > codecpar - > frame_size ;
}
2023-10-20 18:17:20 -04:00
else if ( strcmp ( query , " estimated_size " ) = = 0 )
{
if ( ctx - > audio_stream . stream )
return ctx - > bytes_total ;
}
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
}
2023-10-20 18:17:20 -04:00
void
transcode_metadata_strings_set ( struct transcode_metadata_string * s , enum transcode_profile profile , struct media_quality * q , uint32_t len_ms )
{
off_t bytes ;
memset ( s , 0 , sizeof ( struct transcode_metadata_string ) ) ;
switch ( profile )
{
case XCODE_WAV :
s - > type = " wav " ;
s - > codectype = " wav " ;
s - > description = " WAV audio file " ;
snprintf ( s - > bitrate , sizeof ( s - > bitrate ) , " %d " , 8 * STOB ( q - > sample_rate , q - > bits_per_sample , q - > channels ) / 1000 ) ; // 44100/16/2 -> 1411
bytes = size_estimate ( profile , q - > bit_rate , q - > sample_rate , q - > bits_per_sample / 8 , q - > channels , len_ms ) ;
snprintf ( s - > file_size , sizeof ( s - > file_size ) , " %d " , ( int ) bytes ) ;
break ;
case XCODE_MP3 :
s - > type = " mp3 " ;
s - > codectype = " mpeg " ;
s - > description = " MPEG audio file " ;
snprintf ( s - > bitrate , sizeof ( s - > bitrate ) , " %d " , q - > bit_rate / 1000 ) ;
bytes = size_estimate ( profile , q - > bit_rate , q - > sample_rate , q - > bits_per_sample / 8 , q - > channels , len_ms ) ;
snprintf ( s - > file_size , sizeof ( s - > file_size ) , " %d " , ( int ) bytes ) ;
break ;
default :
DPRINTF ( E_WARN , L_XCODE , " transcode_metadata_strings_set() called with unknown profile %d \n " , profile ) ;
}
}