/* * Copyright (C) 2009-2011 Julien BLACHE * * Adapted from mt-daapd: * Copyright (C) 2006-2007 Ron Pedde * * 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 #endif #include #include #include #include #include #include #include #if defined(__linux__) || defined(__GLIBC__) # include # include #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) # include #endif #include #include #include #if defined(HAVE_LIBSWRESAMPLE) # include #elif defined(HAVE_LIBAVRESAMPLE) # include # include #endif #include "logger.h" #include "conffile.h" #include "db.h" #include "transcode.h" #ifdef HAVE_SPOTIFY_H # include "spotify.h" #endif #if LIBAVCODEC_VERSION_MAJOR >= 56 || (LIBAVCODEC_VERSION_MAJOR == 55 && LIBAVCODEC_VERSION_MINOR >= 18) # define XCODE_BUFFER_SIZE ((192000 * 3) / 2) #else # define XCODE_BUFFER_SIZE ((AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2) #endif struct transcode_ctx { AVFormatContext *fmtctx; /* Audio stream */ int astream; AVCodecContext *acodec; /* pCodecCtx */ AVCodec *adecoder; /* pCodec */ AVPacket apacket; AVPacket apacket2; int16_t *abuffer; /* Resampling */ #if defined(HAVE_LIBSWRESAMPLE) SwrContext *resample_ctx; #elif defined(HAVE_LIBAVRESAMPLE) AVAudioResampleContext *resample_ctx; #else ReSampleContext *resample_ctx; int input_size; #endif int need_resample; int16_t *re_abuffer; off_t offset; uint32_t duration; uint64_t samples; uint32_t icy_hash; /* WAV header */ int wavhdr; uint8_t header[44]; }; static char *default_codecs = "mpeg,wav"; static char *roku_codecs = "mpeg,mp4a,wma,wav"; static char *itunes_codecs = "mpeg,mp4a,mp4v,alac,wav"; 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 make_wav_header(struct transcode_ctx *ctx, off_t *est_size) { uint32_t wav_len; int duration; if (ctx->duration) duration = ctx->duration; else duration = 3 * 60 * 1000; /* 3 minutes, in ms */ if (ctx->samples && !ctx->need_resample) wav_len = 2 * 2 * ctx->samples; else wav_len = 2 * 2 * 44100 * (duration / 1000); *est_size = wav_len + sizeof(ctx->header); 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); add_le16(ctx->header + 22, 2); /* channels */ add_le32(ctx->header + 24, 44100); /* samplerate */ add_le32(ctx->header + 28, 44100 * 2 * 2); /* byte rate */ add_le16(ctx->header + 32, 2 * 2); /* block align */ add_le16(ctx->header + 34, 16); /* bits per sample */ memcpy(ctx->header + 36, "data", 4); add_le32(ctx->header + 40, wav_len); } int transcode(struct transcode_ctx *ctx, struct evbuffer *evbuf, int wanted) { int16_t *buf; int buflen; int processed; int used; int stop; int ret; #if BYTE_ORDER == BIG_ENDIAN int i; #endif #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) AVFrame *frame = NULL; int got_frame; int out_samples; #elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) AVFrame *frame = NULL; int got_frame; #endif #if defined(HAVE_LIBAVRESAMPLE) int out_linesize; #endif processed = 0; if (ctx->wavhdr && (ctx->offset == 0)) { evbuffer_add(evbuf, ctx->header, sizeof(ctx->header)); processed += sizeof(ctx->header); ctx->offset += sizeof(ctx->header); } stop = 0; while ((processed < wanted) && !stop) { /* Decode data */ while (ctx->apacket2.size > 0) { #if LIBAVCODEC_VERSION_MAJOR >= 56 || (LIBAVCODEC_VERSION_MAJOR == 55 && LIBAVCODEC_VERSION_MINOR >= 29) got_frame = 0; if (!frame) { frame = av_frame_alloc(); if (!frame) { DPRINTF(E_LOG, L_XCODE, "Out of memory for decoded frame\n"); return -1; } } else av_frame_unref(frame); used = avcodec_decode_audio4(ctx->acodec, frame, &got_frame, &ctx->apacket2); #elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) got_frame = 0; if (!frame) { frame = avcodec_alloc_frame(); if (!frame) { DPRINTF(E_LOG, L_XCODE, "Out of memory for decoded frame\n"); return -1; } } else avcodec_get_frame_defaults(frame); used = avcodec_decode_audio4(ctx->acodec, frame, &got_frame, &ctx->apacket2); #else buflen = XCODE_BUFFER_SIZE; used = avcodec_decode_audio3(ctx->acodec, ctx->abuffer, &buflen, &ctx->apacket2); #endif if (used < 0) { /* Something happened, skip this packet */ ctx->apacket2.size = 0; break; } ctx->apacket2.data += used; ctx->apacket2.size -= used; /* No frame decoded this time around */ #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (!got_frame) continue; #elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) if (!got_frame) continue; else { /* This part is from the libav wrapper for avcodec_decode_audio3 */ int ch, plane_size; int planar = av_sample_fmt_is_planar(ctx->acodec->sample_fmt); int data_size = av_samples_get_buffer_size(&plane_size, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); if (XCODE_BUFFER_SIZE < data_size) { DPRINTF(E_WARN, L_XCODE, "Output buffer too small for frame (%d < %d)\n", XCODE_BUFFER_SIZE, data_size); continue; } memcpy(ctx->abuffer, frame->extended_data[0], plane_size); if (planar && ctx->acodec->channels > 1) { uint8_t *out = ((uint8_t *)ctx->abuffer) + plane_size; for (ch = 1; ch < ctx->acodec->channels; ch++) { memcpy(out, frame->extended_data[ch], plane_size); out += plane_size; } } buflen = data_size; } #else if (buflen == 0) continue; #endif // TODO Use the AVFrame resampling API's - probably much safer and easier than the following mess if (ctx->need_resample) #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) { out_samples = 0; # if defined(HAVE_LIBSWRESAMPLE) out_samples = av_rescale_rnd( swr_get_delay(ctx->resample_ctx, ctx->acodec->sample_rate) + frame->nb_samples, 44100, ctx->acodec->sample_rate, AV_ROUND_UP ); out_samples = swr_convert(ctx->resample_ctx, (uint8_t **)&ctx->re_abuffer, out_samples, (const uint8_t **)frame->data, frame->nb_samples); # elif defined(HAVE_LIBAVRESAMPLE) av_samples_get_buffer_size(&out_linesize, 2, frame->nb_samples, AV_SAMPLE_FMT_S16, 0); out_samples = avresample_convert(ctx->resample_ctx, (uint8_t **)&ctx->re_abuffer, out_linesize, XCODE_BUFFER_SIZE, (uint8_t **)frame->data, frame->linesize[0], frame->nb_samples); # endif if (out_samples < 0) { DPRINTF(E_LOG, L_XCODE, "Resample returned no samples!\n"); return -1; } buflen = out_samples * 2 * 2; /* 16bit samples, 2 channels */ buf = ctx->re_abuffer; } else { buf = (int16_t *)frame->data[0]; buflen = av_samples_get_buffer_size(NULL, ctx->acodec->channels, frame->nb_samples, ctx->acodec->sample_fmt, 1); } #else { buflen = audio_resample(ctx->resample_ctx, ctx->re_abuffer, ctx->abuffer, buflen / ctx->input_size); if (buflen == 0) { DPRINTF(E_WARN, L_XCODE, "Resample returned no samples!\n"); continue; } buflen = buflen * 2 * 2; /* 16bit samples, 2 channels */ buf = ctx->re_abuffer; } else buf = ctx->abuffer; #endif #if BYTE_ORDER == BIG_ENDIAN /* swap buffer, LE16 */ for (i = 0; i < (buflen / 2); i++) { buf[i] = htole16(buf[i]); } #endif ret = evbuffer_add(evbuf, buf, buflen); if (ret != 0) { DPRINTF(E_WARN, L_XCODE, "Could not copy WAV data to buffer\n"); return -1; } processed += buflen; } /* Read more data */ do { if (ctx->apacket.data) av_free_packet(&ctx->apacket); ret = av_read_frame(ctx->fmtctx, &ctx->apacket); if (ret < 0) { DPRINTF(E_WARN, L_XCODE, "Could not read more data\n"); stop = 1; break; } } while (ctx->apacket.stream_index != ctx->astream); /* Copy apacket and do not mess with it */ ctx->apacket2 = ctx->apacket; } ctx->offset += processed; #if LIBAVCODEC_VERSION_MAJOR >= 56 || (LIBAVCODEC_VERSION_MAJOR == 55 && LIBAVCODEC_VERSION_MINOR >= 29) if (frame) av_frame_free(&frame); #elif LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) if (frame) avcodec_free_frame(&frame); #elif LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 35) if (frame) av_free(frame); #endif return processed; } int transcode_seek(struct transcode_ctx *ctx, int ms) { int64_t start_time; int64_t target_pts; int64_t got_pts; int got_ms; int flags; int ret; start_time = ctx->fmtctx->streams[ctx->astream]->start_time; target_pts = ms; target_pts = target_pts * AV_TIME_BASE / 1000; target_pts = av_rescale_q(target_pts, AV_TIME_BASE_Q, ctx->fmtctx->streams[ctx->astream]->time_base); if ((start_time != AV_NOPTS_VALUE) && (start_time > 0)) target_pts += start_time; ret = av_seek_frame(ctx->fmtctx, ctx->astream, target_pts, AVSEEK_FLAG_BACKWARD); if (ret < 0) { DPRINTF(E_WARN, L_XCODE, "Could not seek into stream: %s\n", strerror(AVUNERROR(ret))); return -1; } avcodec_flush_buffers(ctx->acodec); #if LIBAVCODEC_VERSION_MAJOR >= 53 ctx->acodec->skip_frame = AVDISCARD_NONREF; #else ctx->acodec->hurry_up = 1; #endif flags = 0; while (1) { if (ctx->apacket.data) av_free_packet(&ctx->apacket); ret = av_read_frame(ctx->fmtctx, &ctx->apacket); if (ret < 0) { DPRINTF(E_WARN, L_XCODE, "Could not read more data while seeking\n"); flags = 1; break; } if (ctx->apacket.stream_index != ctx->astream) continue; /* Need a pts to return the real position */ if (ctx->apacket.pts == AV_NOPTS_VALUE) continue; break; } #if LIBAVCODEC_VERSION_MAJOR >= 53 ctx->acodec->skip_frame = AVDISCARD_DEFAULT; #else ctx->acodec->hurry_up = 0; #endif /* Error while reading frame above */ if (flags) return -1; /* Copy apacket and do not mess with it */ ctx->apacket2 = ctx->apacket; /* Compute position in ms from pts */ got_pts = ctx->apacket.pts; if ((start_time != AV_NOPTS_VALUE) && (start_time > 0)) got_pts -= start_time; got_pts = av_rescale_q(got_pts, ctx->fmtctx->streams[ctx->astream]->time_base, AV_TIME_BASE_Q); got_ms = got_pts / (AV_TIME_BASE / 1000); DPRINTF(E_DBG, L_XCODE, "Seek wanted %d ms, got %d ms\n", ms, got_ms); return got_ms; } int transcode_setup(struct transcode_ctx **nctx, struct media_file_info *mfi, off_t *est_size, int wavhdr) { AVDictionary *options; struct transcode_ctx *ctx; int ret; ctx = (struct transcode_ctx *)malloc(sizeof(struct transcode_ctx)); if (!ctx) { DPRINTF(E_WARN, L_XCODE, "Could not allocate transcode context\n"); return -1; } memset(ctx, 0, sizeof(struct transcode_ctx)); options = NULL; #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) # ifndef HAVE_FFMPEG // Without this, libav is slow to probe some internet streams, which leads to RAOP timeouts if (mfi->data_kind == 1) { ctx->fmtctx = avformat_alloc_context(); ctx->fmtctx->probesize = 64000; } # endif if (mfi->data_kind == 1) av_dict_set(&options, "icy", "1", 0); ret = avformat_open_input(&ctx->fmtctx, mfi->path, NULL, &options); if (options) av_dict_free(&options); #else ret = av_open_input_file(&ctx->fmtctx, mfi->path, NULL, 0, NULL); #endif if (ret != 0) { DPRINTF(E_WARN, L_XCODE, "Could not open file %s: %s\n", mfi->fname, strerror(AVUNERROR(ret))); free(ctx); return -1; } #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) ret = avformat_find_stream_info(ctx->fmtctx, NULL); #else ret = av_find_stream_info(ctx->fmtctx); #endif if (ret < 0) { DPRINTF(E_WARN, L_XCODE, "Could not find stream info: %s\n", strerror(AVUNERROR(ret))); goto setup_fail; } #if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 64) ctx->astream = av_find_best_stream(ctx->fmtctx, AVMEDIA_TYPE_AUDIO, -1, -1, &ctx->adecoder, 0); if (ctx->astream < 0) { DPRINTF(E_WARN, L_XCODE, "Did not find audio stream or suitable decoder for %s\n", mfi->fname); goto setup_fail; } #else int i; ctx->astream = -1; for (i = 0; i < ctx->fmtctx->nb_streams; i++) { if (ctx->fmtctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) { ctx->astream = i; break; } } if (ctx->astream < 0) { DPRINTF(E_WARN, L_XCODE, "No audio stream found in file %s\n", mfi->fname); goto setup_fail; } ctx->adecoder = avcodec_find_decoder(ctx->fmtctx->streams[ctx->astream]->codec->codec_id); if (!ctx->adecoder) { DPRINTF(E_WARN, L_XCODE, "No suitable decoder found for codec\n"); goto setup_fail; } #endif ctx->acodec = ctx->fmtctx->streams[ctx->astream]->codec; if (ctx->adecoder->capabilities & CODEC_CAP_TRUNCATED) ctx->acodec->flags |= CODEC_FLAG_TRUNCATED; #if LIBAVCODEC_VERSION_MAJOR >= 54 || (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR >= 6) ctx->acodec->request_sample_fmt = AV_SAMPLE_FMT_S16; ctx->acodec->request_channel_layout = AV_CH_LAYOUT_STEREO; ret = avcodec_open2(ctx->acodec, ctx->adecoder, NULL); #else ret = avcodec_open(ctx->acodec, ctx->adecoder); #endif if (ret != 0) { DPRINTF(E_WARN, L_XCODE, "Could not open codec: %s\n", strerror(AVUNERROR(ret))); goto setup_fail; } ctx->abuffer = (int16_t *)av_malloc(XCODE_BUFFER_SIZE); if (!ctx->abuffer) { DPRINTF(E_WARN, L_XCODE, "Could not allocate transcode buffer\n"); goto setup_fail_codec; } ctx->need_resample = (ctx->acodec->sample_fmt != AV_SAMPLE_FMT_S16) || (ctx->acodec->channels != 2) || (ctx->acodec->sample_rate != 44100); if (ctx->need_resample) { #if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE) if (!ctx->acodec->channel_layout) { DPRINTF(E_DBG, L_XCODE, "Resample requires channel_layout, but none from ffmpeg. Setting to default.\n"); ctx->acodec->channel_layout = av_get_default_channel_layout(ctx->acodec->channels); } DPRINTF(E_DBG, L_XCODE, "Will resample, decoded stream is: %s, %d channels (layout %" PRIu64 "), %d Hz\n", av_get_sample_fmt_name(ctx->acodec->sample_fmt), ctx->acodec->channels, ctx->acodec->channel_layout, ctx->acodec->sample_rate); #endif #if defined(HAVE_LIBSWRESAMPLE) ctx->resample_ctx = swr_alloc_set_opts( NULL, // we're allocating a new context AV_CH_LAYOUT_STEREO, // out_ch_layout AV_SAMPLE_FMT_S16, // out_sample_fmt 44100, // out_sample_rate ctx->acodec->channel_layout, // in_ch_layout ctx->acodec->sample_fmt, // in_sample_fmt ctx->acodec->sample_rate, // in_sample_rate 0, // log_offset NULL); if (!ctx->resample_ctx) { DPRINTF(E_LOG, L_XCODE, "Out of memory for resample context\n"); goto setup_fail_codec; } ret = swr_init(ctx->resample_ctx); if (ret < 0) { DPRINTF(E_LOG, L_XCODE, "Could not open resample context\n"); swr_free(&ctx->resample_ctx); goto setup_fail_codec; } ctx->re_abuffer = av_realloc(ctx->re_abuffer, XCODE_BUFFER_SIZE * 2); if (!ctx->re_abuffer) { DPRINTF(E_LOG, L_XCODE, "Could not allocate resample buffer\n"); swr_free(&ctx->resample_ctx); goto setup_fail_codec; } #elif defined(HAVE_LIBAVRESAMPLE) ctx->resample_ctx = avresample_alloc_context(); if (!ctx->resample_ctx) { DPRINTF(E_LOG, L_XCODE, "Out of memory for resample context\n"); goto setup_fail_codec; } av_opt_set_int(ctx->resample_ctx, "in_sample_fmt", ctx->acodec->sample_fmt, 0); av_opt_set_int(ctx->resample_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(ctx->resample_ctx, "in_channel_layout", ctx->acodec->channel_layout, 0); av_opt_set_int(ctx->resample_ctx, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); av_opt_set_int(ctx->resample_ctx, "in_sample_rate", ctx->acodec->sample_rate, 0); av_opt_set_int(ctx->resample_ctx, "out_sample_rate", 44100, 0); ret = avresample_open(ctx->resample_ctx); if (ret < 0) { DPRINTF(E_LOG, L_XCODE, "Could not open resample context\n"); avresample_free(&ctx->resample_ctx); goto setup_fail_codec; } ctx->re_abuffer = av_realloc(ctx->re_abuffer, XCODE_BUFFER_SIZE * 2); if (!ctx->re_abuffer) { DPRINTF(E_LOG, L_XCODE, "Could not allocate resample buffer\n"); avresample_free(&ctx->resample_ctx); goto setup_fail_codec; } #else DPRINTF(E_DBG, L_XCODE, "Setting up resampling (%d@%d)\n", ctx->acodec->channels, ctx->acodec->sample_rate); ctx->resample_ctx = av_audio_resample_init(2, ctx->acodec->channels, 44100, ctx->acodec->sample_rate, AV_SAMPLE_FMT_S16, ctx->acodec->sample_fmt, 16, 10, 0, 0.8); if (!ctx->resample_ctx) { DPRINTF(E_WARN, L_XCODE, "Could not init resample from %d@%d to 2@44100\n", ctx->acodec->channels, ctx->acodec->sample_rate); goto setup_fail_codec; } ctx->re_abuffer = (int16_t *)av_malloc(XCODE_BUFFER_SIZE * 2); if (!ctx->re_abuffer) { DPRINTF(E_WARN, L_XCODE, "Could not allocate resample buffer\n"); audio_resample_close(ctx->resample_ctx); goto setup_fail_codec; } #endif #if LIBAVCODEC_VERSION_MAJOR >= 55 || (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 35) #elif LIBAVUTIL_VERSION_MAJOR >= 52 || (LIBAVUTIL_VERSION_MAJOR == 51 && LIBAVUTIL_VERSION_MINOR >= 4) ctx->input_size = ctx->acodec->channels * av_get_bytes_per_sample(ctx->acodec->sample_fmt); #elif LIBAVCODEC_VERSION_MAJOR >= 53 ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_fmt(ctx->acodec->sample_fmt) / 8; #else ctx->input_size = ctx->acodec->channels * av_get_bits_per_sample_format(ctx->acodec->sample_fmt) / 8; #endif } ctx->duration = mfi->song_length; ctx->samples = mfi->sample_count; ctx->wavhdr = wavhdr; if (wavhdr) make_wav_header(ctx, est_size); *nctx = ctx; return 0; setup_fail_codec: avcodec_close(ctx->acodec); setup_fail: #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) avformat_close_input(&ctx->fmtctx); #else av_close_input_file(ctx->fmtctx); #endif free(ctx); return -1; } void transcode_cleanup(struct transcode_ctx *ctx) { if (ctx->apacket.data) av_free_packet(&ctx->apacket); if (ctx->acodec) avcodec_close(ctx->acodec); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) if (ctx->fmtctx) avformat_close_input(&ctx->fmtctx); #else if (ctx->fmtctx) av_close_input_file(ctx->fmtctx); #endif av_free(ctx->abuffer); if (ctx->need_resample) { #if defined(HAVE_LIBSWRESAMPLE) swr_free(&ctx->resample_ctx); #elif defined(HAVE_LIBAVRESAMPLE) avresample_free(&ctx->resample_ctx); #else audio_resample_close(ctx->resample_ctx); #endif av_free(ctx->re_abuffer); } free(ctx); } int transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype) { char *codectype; cfg_t *lib; int size; int i; if (!file_codectype) { DPRINTF(E_LOG, L_XCODE, "Can't determine transcode status, codec type is unknown\n"); return -1; } DPRINTF(E_DBG, L_XCODE, "Determining transcoding status for codectype %s\n", file_codectype); lib = cfg_getsec(cfg, "library"); size = cfg_size(lib, "no_transcode"); if (size > 0) { for (i = 0; i < size; i++) { codectype = cfg_getnstr(lib, "no_transcode", i); if (strcmp(file_codectype, codectype) == 0) { DPRINTF(E_DBG, L_XCODE, "Codectype is in no_transcode\n"); return 0; } } } size = cfg_size(lib, "force_transcode"); if (size > 0) { for (i = 0; i < size; i++) { codectype = cfg_getnstr(lib, "force_transcode", i); if (strcmp(file_codectype, codectype) == 0) { DPRINTF(E_DBG, L_XCODE, "Codectype is in force_transcode\n"); return 1; } } } if (!client_codecs) { if (user_agent) { DPRINTF(E_DBG, L_XCODE, "User-Agent: %s\n", user_agent); if (strncmp(user_agent, "iTunes", strlen("iTunes")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is iTunes\n"); client_codecs = itunes_codecs; } else if (strncmp(user_agent, "QuickTime", strlen("QuickTime")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is QuickTime, using iTunes codecs\n"); client_codecs = itunes_codecs; } else if (strncmp(user_agent, "Front%20Row", strlen("Front%20Row")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is Front Row, using iTunes codecs\n"); client_codecs = itunes_codecs; } else if (strncmp(user_agent, "AppleCoreMedia", strlen("AppleCoreMedia")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is AppleCoreMedia, using iTunes codecs\n"); client_codecs = itunes_codecs; } else if (strncmp(user_agent, "Roku", strlen("Roku")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is a Roku device\n"); client_codecs = roku_codecs; } else if (strncmp(user_agent, "Hifidelio", strlen("Hifidelio")) == 0) { DPRINTF(E_DBG, L_XCODE, "Client is a Hifidelio device, allegedly cannot transcode\n"); /* 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; } } } else DPRINTF(E_DBG, L_XCODE, "Client advertises codecs: %s\n", client_codecs); if (!client_codecs) { DPRINTF(E_DBG, L_XCODE, "Could not identify client, using default codectype set\n"); client_codecs = default_codecs; } if (strstr(client_codecs, file_codectype)) { DPRINTF(E_DBG, L_XCODE, "Codectype supported by client, no transcoding needed\n"); return 0; } DPRINTF(E_DBG, L_XCODE, "Will transcode\n"); return 1; } void transcode_metadata(struct transcode_ctx *ctx, struct http_icy_metadata **metadata, int *changed) { struct http_icy_metadata *m; *metadata = NULL; if (!ctx->fmtctx) return; m = http_icy_metadata_get(ctx->fmtctx, 1); if (!m) return; *changed = (m->hash != ctx->icy_hash); ctx->icy_hash = m->hash; *metadata = m; } void transcode_metadata_artwork_url(struct transcode_ctx *ctx, char **artwork_url) { struct http_icy_metadata *m; *artwork_url = NULL; if (!ctx->fmtctx || !ctx->fmtctx->filename) return; m = http_icy_metadata_get(ctx->fmtctx, 1); if (!m) return; if (m->artwork_url) *artwork_url = strdup(m->artwork_url); http_icy_metadata_free(m, 0); }