/* * Interface to libshine for mp3 encoding * Copyright (c) 2012 Paul B Mahol * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <shine/layer3.h> #include "libavutil/intreadwrite.h" #include "audio_frame_queue.h" #include "avcodec.h" #include "internal.h" #include "mpegaudio.h" #include "mpegaudiodecheader.h" #define BUFFER_SIZE (4096 * 20) typedef struct SHINEContext { shine_config_t config; shine_t shine; uint8_t buffer[BUFFER_SIZE]; int buffer_index; AudioFrameQueue afq; } SHINEContext; static av_cold int libshine_encode_init(AVCodecContext *avctx) { SHINEContext *s = avctx->priv_data; if (avctx->channels <= 0 || avctx->channels > 2){ av_log(avctx, AV_LOG_ERROR, "only mono or stereo is supported\n"); return AVERROR(EINVAL); } shine_set_config_mpeg_defaults(&s->config.mpeg); if (avctx->bit_rate) s->config.mpeg.bitr = avctx->bit_rate / 1000; s->config.mpeg.mode = avctx->channels == 2 ? STEREO : MONO; s->config.wave.samplerate = avctx->sample_rate; s->config.wave.channels = avctx->channels == 2 ? PCM_STEREO : PCM_MONO; if (shine_check_config(s->config.wave.samplerate, s->config.mpeg.bitr) < 0) { av_log(avctx, AV_LOG_ERROR, "invalid configuration\n"); return AVERROR(EINVAL); } s->shine = shine_initialise(&s->config); if (!s->shine) return AVERROR(ENOMEM); avctx->frame_size = shine_samples_per_pass(s->shine); ff_af_queue_init(avctx, &s->afq); return 0; } static int libshine_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr) { SHINEContext *s = avctx->priv_data; MPADecodeHeader hdr; unsigned char *data; int written; int ret, len; if (frame) data = shine_encode_buffer(s->shine, (int16_t **)frame->data, &written); else data = shine_flush(s->shine, &written); if (written < 0) return -1; if (written > 0) { if (s->buffer_index + written > BUFFER_SIZE) { av_log(avctx, AV_LOG_ERROR, "internal buffer too small\n"); return AVERROR_BUG; } memcpy(s->buffer + s->buffer_index, data, written); s->buffer_index += written; } if (frame) { if ((ret = ff_af_queue_add(&s->afq, frame)) < 0) return ret; } if (s->buffer_index < 4 || !s->afq.frame_count) return 0; if (avpriv_mpegaudio_decode_header(&hdr, AV_RB32(s->buffer))) { av_log(avctx, AV_LOG_ERROR, "free format output not supported\n"); return -1; } len = hdr.frame_size; if (len <= s->buffer_index) { if ((ret = ff_alloc_packet2(avctx, avpkt, len, 0))) return ret; memcpy(avpkt->data, s->buffer, len); s->buffer_index -= len; memmove(s->buffer, s->buffer + len, s->buffer_index); ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts, &avpkt->duration); avpkt->size = len; *got_packet_ptr = 1; } return 0; } static av_cold int libshine_encode_close(AVCodecContext *avctx) { SHINEContext *s = avctx->priv_data; ff_af_queue_close(&s->afq); shine_close(s->shine); return 0; } static const int libshine_sample_rates[] = { 44100, 48000, 32000, 0 }; AVCodec ff_libshine_encoder = { .name = "libshine", .long_name = NULL_IF_CONFIG_SMALL("libshine MP3 (MPEG audio layer 3)"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_MP3, .priv_data_size = sizeof(SHINEContext), .init = libshine_encode_init, .encode2 = libshine_encode_frame, .close = libshine_encode_close, .capabilities = AV_CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, .supported_samplerates = libshine_sample_rates, .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, 0 }, .wrapper_name = "libshine", };