From 11dfe7ee92148147d3496a47b9f43ce093d37ed9 Mon Sep 17 00:00:00 2001 From: Sebastian Morgenstern Date: Sat, 25 Apr 2015 19:12:14 +0200 Subject: [PATCH 01/13] inital commit of changes to fork --- CMakeLists.txt | 34 +++++ Makefile | 9 -- NOTES | 50 ------- codechandler.c | 102 +++++++++++++ codechandler.h | 37 +++++ helper.c | 80 ++++++++++ helper.h | 16 ++ myspdif.c | 42 ++++++ myspdif.h | 71 +++++++++ myspdifdec.c | 177 ++++++++++++++++++++++ resample.c | 36 +++++ resample.h | 21 +++ spdif-loop.c | 388 +++++++++++++++++++++++++++++++++---------------- 13 files changed, 881 insertions(+), 182 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile delete mode 100644 NOTES create mode 100644 codechandler.c create mode 100644 codechandler.h create mode 100644 helper.c create mode 100644 helper.h create mode 100644 myspdif.c create mode 100644 myspdif.h create mode 100644 myspdifdec.c create mode 100644 resample.c create mode 100644 resample.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b274555 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required (VERSION 2.8.11) +project (spdif-decoder) + +add_executable (spdif-decoder + codechandler.c + helper.c + myspdif.c + myspdifdec.c + resample.c + spdif-loop.c +) + +SET(FFMPEG /home/sebastian/software/ffmpeg-2.6.1) + +target_include_directories (spdif-decoder + PUBLIC ${FFMPEG} +) + +FIND_LIBRARY(libavcodec avcodec ${FFMPEG}/libavcodec) +FIND_LIBRARY(libavformat avformat ${FFMPEG}/libavformat) +FIND_LIBRARY(libavdevice avdevice ${FFMPEG}/libavdevice) +FIND_LIBRARY(libavutil avutil ${FFMPEG}/libavutil) +FIND_LIBRARY(libswresample swresample ${FFMPEG}/libswresample) +FIND_LIBRARY(libavfilter avfilter ${FFMPEG}/libavfilter) +TARGET_LINK_LIBRARIES(spdif-decoder + ${libavcodec} + ${libavformat} + ${libavdevice} + ${libavutil} + ${libswresample} + ${libavfilter} + ao + m +) diff --git a/Makefile b/Makefile deleted file mode 100644 index af8e2ef..0000000 --- a/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -PROG= spdif-loop - -CFLAGS+= -Wall -std=c99 -g -LDFLAGS+= -lavcodec -lavformat -lavdevice -lavutil -lao -lm - -all: ${PROG} - -clean: - -rm -f ${PROG} diff --git a/NOTES b/NOTES deleted file mode 100644 index 3b919d0..0000000 --- a/NOTES +++ /dev/null @@ -1,50 +0,0 @@ -libavformat sequence - -init - -av_register_all(); -avcodec_register_all(); - -sndinfmt = av_find_input_format("alsa"); -avformat_open_input(&sndinctx, "hw:1", sndinfmt, NULL); - -mock up another input for spdif demux: - -spdifctx = avformat_alloc_context(); -spdifctx->pb = avio_alloc_context(buf, bufsize, 0, mydata, read_cb, NULL, NULL); -spdiffmt = av_find_input_format("spdif"); -avformat_open_input(&spdifctx, NULL, spidffmt, NULL); - -get decoder - -codec = avcodec_find_decoder(spdifcodec_id); -codecctx = avcodec_alloc_context3(codec); -avcodec_open2(&codecctx, codec, NULL); - -outframe = avcodec_alloc_frame(); -processed_len = avcodec_decode_audio4(codecctx, outframe, &got_frame, inpkt); - -/* advance inpkt by processed_len */ - -codecctx->sample_rate; -codecctx->channels; -codecctx->sample_fmt; -codecctx->channel_layout; - -outframe->data -outframe->nb_samples -outlen = av_samples_get_buffer_size(NULL, codecctx->channels, outframe->nb_samples, codecctx->sample_fmt, 1); - -play with libao - -ao_initialize(); -ao_sample_format outfmt; -memset(&outfmt, 0, sizeof(outfmt)); -outfmt.bits = ...; -outfmt.channels = ...; -outfmt.rate = ...; -outfmt.byte_format = ...; - -outdev = ao_open_live(ao_default_driver_id(), &outfmt, NULL); - -ao_play(outdev, buf, bufsize); diff --git a/codechandler.c b/codechandler.c new file mode 100644 index 0000000..e4570ab --- /dev/null +++ b/codechandler.c @@ -0,0 +1,102 @@ +/* + * codechandler.c + * + * Created on: 25.04.2015 + * Author: sebastian + */ + +#include +#include +#include +#include "resample.h" +#include "codechandler.h" + +void CodecHandler_init(CodecHandler* h){ + h->codec = NULL; + h->codecContext = NULL; + h->currentChannelCount = 0; + h->currentCodecID = AV_CODEC_ID_NONE; + h->currentSampleRate = 0; + h->swr = resample_init(); + h->frame = av_frame_alloc(); +} +void CodecHandler_deinit(CodecHandler* h){ + resample_deinit(h->swr); + av_frame_free(&h->frame); +} + +int CodecHandler_loadCodec(CodecHandler * handler, AVFormatContext * formatcontext){ + if (formatcontext->nb_streams == 0){ + printf("could not find a stream\n"); + handler->currentCodecID = AV_CODEC_ID_NONE; + return -1; + } + + if(handler->currentCodecID == formatcontext->streams[0]->codec->codec_id){ + //Codec already loaded + return 0; + } + + if(handler->codecContext != NULL){ + CodecHandler_closeCodec(handler); + } + handler->currentCodecID = AV_CODEC_ID_NONE; + + handler->codec = avcodec_find_decoder(formatcontext->streams[0]->codec->codec_id); + if (!handler->codec) { + printf("could not find codec\n"); + return -1; + }else{ + printf("found codec\n"); + } + handler->codecContext = avcodec_alloc_context3(handler->codec); + if (!handler->codecContext) + errx(1, "cannot allocate codec"); + if (avcodec_open2(handler->codecContext, handler->codec, NULL) != 0) + errx(1, "cannot open codec"); + handler->currentCodecID = formatcontext->streams[0]->codec->codec_id; + return 0; +} + +int CodecHandler_decodeCodec(CodecHandler * h, AVPacket * pkt, + uint8_t *outbuffer, uint32_t* bufferfilled){ + int got_frame; + int processed_len = avcodec_decode_audio4(h->codecContext, h->frame, &got_frame, pkt); + if (processed_len < 0) + errx(1, "cannot decode input"); + + int ret = 0; + + pkt->data += processed_len; + pkt->size -= processed_len; + if(h->currentChannelCount != h->codecContext->channels + || h->currentSampleRate != h->codecContext->sample_rate + || h->currentChannelLayout != h->codecContext->channel_layout){ + resample_loadFromCodec(h->swr, h->codecContext); + printf("c: %d, s: %d\n",h->codecContext->channels, h->codecContext->sample_rate); + ret = 1; + } + + swr_convert(h->swr, &outbuffer, h->frame->nb_samples, (const uint8_t **)h->frame->data, h->frame->nb_samples); + *bufferfilled = av_samples_get_buffer_size(NULL, + h->codecContext->channels, + h->frame->nb_samples, + AV_SAMPLE_FMT_S16, + 1); + + h->currentChannelCount = h->codecContext->channels; + h->currentSampleRate = h->codecContext->sample_rate; + h->currentChannelLayout = h->codecContext->channel_layout; + return ret; +} + + +int CodecHandler_closeCodec(CodecHandler * handler){ + if(handler->codecContext != NULL){ + avcodec_close(handler->codecContext); + avcodec_free_context(&handler->codecContext); + } + handler->codec = NULL; + handler->codecContext = NULL; + return 0; +} diff --git a/codechandler.h b/codechandler.h new file mode 100644 index 0000000..6a858c2 --- /dev/null +++ b/codechandler.h @@ -0,0 +1,37 @@ +/* + * codechandler.h + * + * Created on: 25.04.2015 + * Author: sebastian + */ + +#ifndef CODECHANDLER_H_ +#define CODECHANDLER_H_ +#include +#include +#include +#include + +typedef struct s_codechandler{ + AVCodecContext *codecContext; + AVCodec * codec; + enum AVCodecID currentCodecID; + int currentChannelCount; + uint64_t currentChannelLayout; + int currentSampleRate; + SwrContext * swr; + AVFrame * frame; +} CodecHandler; + +void CodecHandler_init(CodecHandler* handler); +void CodecHandler_deinit(CodecHandler* handler); + +int CodecHandler_loadCodec(CodecHandler * handler, AVFormatContext * formatcontext); +int CodecHandler_hasCodecChangend(CodecHandler * handler, AVFormatContext * formatcontext); + +int CodecHandler_decodeCodec(CodecHandler * h, AVPacket * pkt, + uint8_t *outbuffer, uint32_t* bufferfilled); +int CodecHandler_closeCodec(CodecHandler * handler); + + +#endif /* CODECHANDLER_H_ */ diff --git a/helper.c b/helper.c new file mode 100644 index 0000000..7928a4a --- /dev/null +++ b/helper.c @@ -0,0 +1,80 @@ +/* + * helper.c + * + * Created on: 21.04.2015 + * Author: sebastian + */ + +#include +#include +#include +#include +#include + +#include + +ao_device * open_output(int driver_id, ao_option *dev_opts, int bits, int channels, int sample_rate) +{ + printf("%d bit, %d channels, %dHz\n", + bits, + channels, + sample_rate); + + ao_sample_format out_fmt = { + .bits = bits, + .channels = channels, + .rate = sample_rate, + .byte_format = AO_FMT_NATIVE, + //.matrix = "L,R,BL,BR,C,LFE", + .matrix = "L,R,C,LFE,BL,BR", + }; + + return (ao_open_live(driver_id, &out_fmt, dev_opts)); +} + +int test_audio_out(int driver_id, ao_option *dev_opts) +{ + struct chan_map { + const char *name; + int freq; + int idx; + } map[] = { + /* This needs to match the order in open_output(). */ + { "left", 500, 0 }, + { "center", 500, 2 }, + { "right", 500, 1 }, + { "rear right", 500, 5 }, + { "rear left", 500, 4 }, + { "sub", 50, 3 } + }; + + ao_device *odev = open_output(driver_id, dev_opts, 16, 6, 48000); + if (!odev) + errx(1, "cannot open audio output"); + int ch; + for (ch = 0; ch < 6; ++ch) { + const size_t buflen = 4800; /* 1/10 of a second */ + int16_t buf[buflen * 6]; + + printf("channel %d: %s\n", map[ch].idx, map[ch].name); + + /* prepare sine samples */ + memset(buf, 0, sizeof(buf)); + int i; + for (i = 0; i < buflen; ++i) { + buf[i * 6 + map[ch].idx] = INT16_MAX / 10 * cos(2 * (3.14159265358979323846) * map[ch].freq * i / 48000.0); + } + + /* play for 2 sec, 1 sec pause */ + for (i = 0; i < 30; ++i) { + if (i == 20) { + /* now pause */ + memset(buf, 0, sizeof(buf)); + } + if (!ao_play(odev, (char *)buf, sizeof(buf))) + errx(1, "cannot play test audio"); + } + } + + return (0); +} diff --git a/helper.h b/helper.h new file mode 100644 index 0000000..3324542 --- /dev/null +++ b/helper.h @@ -0,0 +1,16 @@ +/* + * helper.h + * + * Created on: 21.04.2015 + * Author: sebastian + */ + +#ifndef HELPER_H_ +#define HELPER_H_ + +#include +#include + +extern ao_device * open_output(int driver_id, ao_option *dev_opts, int bits, int channels, int sample_rate); +extern int test_audio_out(int driver_id, ao_option *dev_opts); +#endif /* HELPER_H_ */ diff --git a/myspdif.c b/myspdif.c new file mode 100644 index 0000000..c4548c1 --- /dev/null +++ b/myspdif.c @@ -0,0 +1,42 @@ +/* + * IEC 61937 common code + * Copyright (c) 2009 Bartlomiej Wolowiec + * + * 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 "myspdif.h" +#include + +//TODO move to DSP +void my_spdif_bswap_buf16(uint16_t *dst, const uint16_t *src, int w) +{ + int i; + + for (i = 0; i + 8 <= w; i += 8) { + dst[i + 0] = av_bswap16(src[i + 0]); + dst[i + 1] = av_bswap16(src[i + 1]); + dst[i + 2] = av_bswap16(src[i + 2]); + dst[i + 3] = av_bswap16(src[i + 3]); + dst[i + 4] = av_bswap16(src[i + 4]); + dst[i + 5] = av_bswap16(src[i + 5]); + dst[i + 6] = av_bswap16(src[i + 6]); + dst[i + 7] = av_bswap16(src[i + 7]); + } + for (; i < w; i++) + dst[i + 0] = av_bswap16(src[i + 0]); +} diff --git a/myspdif.h b/myspdif.h new file mode 100644 index 0000000..2b5582a --- /dev/null +++ b/myspdif.h @@ -0,0 +1,71 @@ +/* + * IEC 61937 common header + * Copyright (c) 2009 Bartlomiej Wolowiec + * + * 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 + */ + +#ifndef MYSPDIF_H_ +#define MYSPDIF_H_ + +#include +#include + +#define SYNCWORD1 0xF872 +#define SYNCWORD2 0x4E1F +#define BURST_HEADER_SIZE 0x8 +#define SPDIF_MAX_OFFSET 16384 + +/* +enum myIEC61937DataType { + IEC61937_AC3 = 0x01, ///< AC-3 data + IEC61937_MPEG1_LAYER1 = 0x04, ///< MPEG-1 layer 1 + IEC61937_MPEG1_LAYER23 = 0x05, ///< MPEG-1 layer 2 or 3 data or MPEG-2 without extension + IEC61937_MPEG2_EXT = 0x06, ///< MPEG-2 data with extension + IEC61937_MPEG2_AAC = 0x07, ///< MPEG-2 AAC ADTS + IEC61937_MPEG2_LAYER1_LSF = 0x08, ///< MPEG-2, layer-1 low sampling frequency + IEC61937_MPEG2_LAYER2_LSF = 0x09, ///< MPEG-2, layer-2 low sampling frequency + IEC61937_MPEG2_LAYER3_LSF = 0x0A, ///< MPEG-2, layer-3 low sampling frequency + IEC61937_DTS1 = 0x0B, ///< DTS type I (512 samples) + IEC61937_DTS2 = 0x0C, ///< DTS type II (1024 samples) + IEC61937_DTS3 = 0x0D, ///< DTS type III (2048 samples) + IEC61937_ATRAC = 0x0E, ///< ATRAC data + IEC61937_ATRAC3 = 0x0F, ///< ATRAC3 data + IEC61937_ATRACX = 0x10, ///< ATRAC3+ data + IEC61937_DTSHD = 0x11, ///< DTS HD data + IEC61937_WMAPRO = 0x12, ///< WMA 9 Professional data + IEC61937_MPEG2_AAC_LSF_2048 = 0x13, ///< MPEG-2 AAC ADTS half-rate low sampling frequency + IEC61937_MPEG2_AAC_LSF_4096 = 0x13 | 0x20, ///< MPEG-2 AAC ADTS quarter-rate low sampling frequency + IEC61937_EAC3 = 0x15, ///< E-AC-3 data + IEC61937_TRUEHD = 0x16, ///< TrueHD data +}; +*/ +/* +static const uint16_t spdif_mpeg_pkt_offset[2][3] = { + //LAYER1 LAYER2 LAYER3 + { 3072, 9216, 4608 }, // MPEG2 LSF + { 1536, 4608, 4608 }, // MPEG1 +}; +*/ + +void my_spdif_bswap_buf16(uint16_t *dst, const uint16_t *src, int w); +int my_spdif_read_packet(AVFormatContext *s, AVPacket *pkt, + uint8_t * garbagebuffer, int garbagebuffersize, int * garbagebufferfilled); +int my_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec); + + +#endif /* MYSPDIF_H_ */ diff --git a/myspdifdec.c b/myspdifdec.c new file mode 100644 index 0000000..50afdd1 --- /dev/null +++ b/myspdifdec.c @@ -0,0 +1,177 @@ +/* + * IEC 61937 demuxer + * Copyright (c) 2010 Anssi Hannula + * + * 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 + */ + +/** + * @file + * IEC 61937 demuxer, used for compressed data in S/PDIF + * @author Anssi Hannula + */ + +#include +#include +#include "myspdif.h" +#include +#include + +static int spdif_get_offset_and_codec(AVFormatContext *s, + enum IEC61937DataType data_type, + const uint8_t *buf, int *offset, + enum AVCodecID *codec) +{ + AACADTSHeaderInfo aac_hdr; + GetBitContext gbc; + + switch (data_type & 0xff) { + case IEC61937_AC3: + *offset = AC3_FRAME_SIZE << 2; + *codec = AV_CODEC_ID_AC3; + break; + case IEC61937_MPEG1_LAYER1: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = AV_CODEC_ID_MP1; + break; + case IEC61937_MPEG1_LAYER23: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_MPEG2_EXT: + *offset = 4608; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_MPEG2_AAC: + init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8); + if (avpriv_aac_parse_header(&gbc, &aac_hdr) < 0) { + if (s) /* be silent during a probe */ + av_log(s, AV_LOG_ERROR, "Invalid AAC packet in IEC 61937\n"); + return AVERROR_INVALIDDATA; + } + *offset = aac_hdr.samples << 2; + *codec = AV_CODEC_ID_AAC; + break; + case IEC61937_MPEG2_LAYER1_LSF: + *offset = spdif_mpeg_pkt_offset[0][0]; + *codec = AV_CODEC_ID_MP1; + break; + case IEC61937_MPEG2_LAYER2_LSF: + *offset = spdif_mpeg_pkt_offset[0][1]; + *codec = AV_CODEC_ID_MP2; + break; + case IEC61937_MPEG2_LAYER3_LSF: + *offset = spdif_mpeg_pkt_offset[0][2]; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_DTS1: + *offset = 2048; + *codec = AV_CODEC_ID_DTS; + break; + case IEC61937_DTS2: + *offset = 4096; + *codec = AV_CODEC_ID_DTS; + break; + case IEC61937_DTS3: + *offset = 8192; + *codec = AV_CODEC_ID_DTS; + break; + default: + if (s) { /* be silent during a probe */ + + //avpriv_request_sample(s, "Data type 0x%04x in IEC 61937", + // data_type); + } + return AVERROR_PATCHWELCOME; + } + return 0; +} + +int my_spdif_read_packet(AVFormatContext *s, AVPacket *pkt, + uint8_t * garbagebuffer, int garbagebuffersize, int * garbagebufferfilled) +{ + AVIOContext *pb = s->pb; + enum IEC61937DataType data_type; + enum AVCodecID codec_id; + uint32_t state = 0; + int pkt_size_bits, offset, ret; + *garbagebufferfilled = 0; + while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) { + if(*garbagebufferfilled < garbagebuffersize){ + *garbagebuffer = avio_r8(pb); + (*garbagebufferfilled)++; + + state = (state << 8) | *garbagebuffer; + garbagebuffer++; + + if (avio_feof(pb)){ + return AVERROR_EOF; + } + }else{ + return AVERROR_STREAM_NOT_FOUND; + } + } + *garbagebufferfilled -= 4; + data_type = avio_rl16(pb); + pkt_size_bits = avio_rl16(pb); + + if (pkt_size_bits % 16) + avpriv_request_sample(s, "Packet not ending at a 16-bit boundary"); + + ret = av_new_packet(pkt, FFALIGN(pkt_size_bits, 16) >> 3); + if (ret) + return ret; + + pkt->pos = avio_tell(pb) - BURST_HEADER_SIZE; + + if (avio_read(pb, pkt->data, pkt->size) < pkt->size) { + av_free_packet(pkt); + return AVERROR_EOF; + } + my_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1); + + ret = spdif_get_offset_and_codec(s, data_type, pkt->data, + &offset, &codec_id); + if (ret) { + av_free_packet(pkt); + return ret; + } + + /* skip over the padding to the beginning of the next frame */ + avio_skip(pb, offset - pkt->size - BURST_HEADER_SIZE); + + if (!s->nb_streams) { + /* first packet, create a stream */ + AVStream *st = avformat_new_stream(s, NULL); + if (!st) { + av_free_packet(pkt); + return AVERROR(ENOMEM); + } + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = codec_id; + } else if (codec_id != s->streams[0]->codec->codec_id) { + avpriv_report_missing_feature(s, "Codec change in IEC 61937"); + return AVERROR_PATCHWELCOME; + } + + if (!s->bit_rate && s->streams[0]->codec->sample_rate) + /* stream bitrate matches 16-bit stereo PCM bitrate for currently + supported codecs */ + s->bit_rate = 2 * 16 * s->streams[0]->codec->sample_rate; + + return 0; +} diff --git a/resample.c b/resample.c new file mode 100644 index 0000000..bd2c773 --- /dev/null +++ b/resample.c @@ -0,0 +1,36 @@ +/* + * resample.c + * + * Created on: 20.04.2015 + * Author: sebastian + */ +#include "resample.h" + +#include + +SwrContext* resample_init(){ + return swr_alloc(); +} + +void resample_deinit(SwrContext* swr){ + swr_free(&swr); +} + +void resample_loadFromCodec(SwrContext *swr, AVCodecContext* audioCodec){ + // Set up SWR context once you've got codec information + av_opt_set_int(swr, "in_channel_layout", audioCodec->channel_layout, 0); + av_opt_set_int(swr, "out_channel_layout", audioCodec->channel_layout, 0); + av_opt_set_int(swr, "in_sample_rate", audioCodec->sample_rate, 0); + av_opt_set_int(swr, "out_sample_rate", audioCodec->sample_rate, 0); + av_opt_set_sample_fmt(swr, "in_sample_fmt", audioCodec->sample_fmt, 0); + av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + swr_init(swr); +} + +void resample_do(SwrContext* swr, AVFrame *audioFrame, uint8_t* outputBuffer){ + swr_convert(swr, + &outputBuffer, + audioFrame->nb_samples, + (const uint8_t**)audioFrame->data, + audioFrame->nb_samples); +} diff --git a/resample.h b/resample.h new file mode 100644 index 0000000..504cd86 --- /dev/null +++ b/resample.h @@ -0,0 +1,21 @@ +/* + * resample.h + * + * Created on: 20.04.2015 + * Author: sebastian + */ + +#ifndef RESAMPLE_H_ +#define RESAMPLE_H_ + +#include +#include +#include +#include + +SwrContext* resample_init(); +void resample_deinit(SwrContext* swr); +void resample_loadFromCodec(SwrContext *swr, AVCodecContext* audioCodec); +void resample_do(SwrContext* swr, AVFrame *audioFrame, uint8_t* outputBuffer); + +#endif /* RESAMPLE_H_ */ diff --git a/spdif-loop.c b/spdif-loop.c index 4c4012b..812d57e 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -10,8 +10,18 @@ #include #include #include +#include +#include + +#include "resample.h" +#include "helper.h" +#include "myspdif.h" +#include "codechandler.h" + +//#define DEBUG +//#define IO_BUFFER_SIZE SPDIF_MAX_OFFSET// ((8+1792+4344)*1) +#define IO_BUFFER_SIZE ((8+1792+4344)*1) -#define IO_BUFFER_SIZE 32768 struct alsa_read_state { AVFormatContext *ctx; @@ -56,7 +66,8 @@ alsa_reader(void *data, uint8_t *buf, int buf_size) if (debug_data) { static int had_zeros = 0; - for (int i = 0; i < read_size; ++i) { + int i; + for (i = 0; i < read_size; ++i) { const char zeros[16] = {0}; if (i % 16 == 0 && read_size - i >= 16 && memcmp((char *)buf + i, zeros, 16) == 0) { @@ -83,88 +94,6 @@ alsa_reader(void *data, uint8_t *buf, int buf_size) return (read_size); } -static enum CodecID -probe_codec(AVFormatContext *s) -{ - AVPacket pkt; - - av_init_packet(&pkt); - if (av_read_frame(s, &pkt) != 0) - return (CODEC_ID_NONE); - - av_free_packet(&pkt); - - if (s->nb_streams == 0) - return (CODEC_ID_NONE); - return (s->streams[0]->codec->codec_id); -} - -static ao_device * -open_output(int driver_id, ao_option *dev_opts, int bits, int channels, int sample_rate) -{ - printf("%d bit, %d channels, %dHz\n", - bits, - channels, - sample_rate); - - ao_sample_format out_fmt = { - .bits = bits, - .channels = channels, - .rate = sample_rate, - .byte_format = AO_FMT_NATIVE, - .matrix = "L,R,C,LFE,BL,BR", - }; - - return (ao_open_live(driver_id, &out_fmt, dev_opts)); -} - -static int -test_audio_out(int driver_id, ao_option *dev_opts) -{ - struct chan_map { - const char *name; - int freq; - int idx; - } map[] = { - /* This needs to match the order in open_output(). */ - { "left", 500, 0 }, - { "center", 500, 2 }, - { "right", 500, 1 }, - { "rear right", 500, 5 }, - { "rear left", 500, 4 }, - { "sub", 50, 3 } - }; - - ao_device *odev = open_output(driver_id, dev_opts, 16, 6, 48000); - if (!odev) - errx(1, "cannot open audio output"); - - for (int ch = 0; ch < 6; ++ch) { - const size_t buflen = 4800; /* 1/10 of a second */ - int16_t buf[buflen * 6]; - - printf("channel %d: %s\n", map[ch].idx, map[ch].name); - - /* prepare sine samples */ - memset(buf, 0, sizeof(buf)); - for (int i = 0; i < buflen; ++i) { - buf[i * 6 + map[ch].idx] = INT16_MAX / 10 * cos(2 * M_PI * map[ch].freq * i / 48000.0); - } - - /* play for 2 sec, 1 sec pause */ - for (int i = 0; i < 30; ++i) { - if (i == 20) { - /* now pause */ - memset(buf, 0, sizeof(buf)); - } - if (!ao_play(odev, (char *)buf, sizeof(buf))) - errx(1, "cannot play test audio"); - } - } - - return (0); -} - int main(int argc, char **argv) { @@ -172,8 +101,8 @@ main(int argc, char **argv) char *alsa_dev_name = NULL; char *out_driver_name = NULL; char *out_dev_name = NULL; - - for (int opt = 0; (opt = getopt(argc, argv, "d:hi:o:tv")) != -1;) { + int opt; + for (opt = 0; (opt = getopt(argc, argv, "d:hi:o:tv")) != -1;) { switch (opt) { case 'd': out_driver_name = optarg; @@ -211,12 +140,14 @@ main(int argc, char **argv) avdevice_register_all(); ao_initialize(); + ao_option *out_dev_opts = NULL; if (out_dev_name) { if (!ao_append_option(&out_dev_opts, "dev", out_dev_name)) errx(1, "cannot set output device `%s'", out_dev_name); } + int out_driver_id = ao_default_driver_id(); if (out_driver_name) out_driver_id = ao_driver_id(out_driver_name); @@ -226,9 +157,10 @@ main(int argc, char **argv) if (opt_test) { exit(test_audio_out(out_driver_id, out_dev_opts)); - /* NOTREACHED */ + // NOTREACHED } + AVInputFormat *alsa_fmt = av_find_input_format("alsa"); if (!alsa_fmt) errx(1, "cannot find alsa input driver"); @@ -272,7 +204,13 @@ main(int argc, char **argv) struct alsa_read_state read_state = { .ctx = alsa_ctx, }; + av_init_packet(&read_state.pkt); + AVIOContext * avio_ctx = avio_alloc_context(alsa_buf, alsa_buf_size, 0, &read_state, alsa_reader, NULL, NULL); + if (!avio_ctx) { + errx(1, "cannot open avio_alloc_context"); + } + spdif_ctx->pb = avio_alloc_context(alsa_buf, alsa_buf_size, 0, &read_state, alsa_reader, NULL, NULL); if (!spdif_ctx->pb) @@ -281,50 +219,238 @@ main(int argc, char **argv) if (avformat_open_input(&spdif_ctx, "internal", spdif_fmt, NULL) != 0) errx(1, "cannot open spdif input"); - enum CodecID spdif_codec_id = probe_codec(spdif_ctx); + av_dump_format(alsa_ctx, 0, alsa_dev_name, 0); -#if HAVE_AVCODEC_GET_NAME +#ifdef HAVE_AVCODEC_GET_NAME printf("detected spdif codec %s\n", avcodec_get_name(spdif_codec_id)); #endif - AVCodec *spdif_codec = avcodec_find_decoder(spdif_codec_id); - if (!spdif_codec) { - printf("could not find codec\n"); - goto retry; - } + AVPacket pkt = {.size = 0, .data = NULL}; + av_init_packet(&pkt); + + char *resamples= malloc(1*1024*1024); + + uint32_t howmuch = 0; + + CodecHandler codecHanlder; + CodecHandler_init(&codecHanlder); + printf("start loop\n"); + while (1) { + int garbagefilled = 0; + int r = my_spdif_read_packet(spdif_ctx, &pkt, (uint8_t*)resamples, IO_BUFFER_SIZE, &garbagefilled); + + if(r == 0){ + //Play rest of garbage before decode newly found codec + if( garbagefilled > 0 + && codecHanlder.currentCodecID == AV_CODEC_ID_NONE + && codecHanlder.currentChannelCount == 2 + && codecHanlder.currentSampleRate == 48000 + && out_dev != NULL){ + if(!ao_play(out_dev, resamples, garbagefilled)){ + goto retry; + } + } + + if(CodecHandler_loadCodec(&codecHanlder, spdif_ctx)!=0){ + goto retry; + } + + if(CodecHandler_decodeCodec(&codecHanlder,&pkt,(uint8_t*)resamples, &howmuch) == 1){ + //channel count has changed + //close out_dev + if (out_dev) { + ao_close(out_dev); + out_dev = NULL; + } + } + if(pkt.size != 0){ + printf("still some bytes left %d\n",pkt.size); + } + + }else{ + codecHanlder.currentCodecID = AV_CODEC_ID_NONE; + if(codecHanlder.currentChannelCount != 2 || + codecHanlder.currentSampleRate != 48000){ + if (out_dev) { + ao_close(out_dev); + out_dev = NULL; + } + } + codecHanlder.currentChannelCount = 2; + codecHanlder.currentSampleRate = 48000; + codecHanlder.currentChannelLayout = 0; + howmuch = garbagefilled; + } +#if 0 + if(r>0){ + //avio_tell() + avio_seek(avio_ctx, avio_ctx->buffer_size, SEEK_CUR); + //found spdif + spdif_ctx->pb = avio_ctx; + int readpak = spdif_fmt->read_packet(spdif_ctx, &pkt); + printf("readpak %d: %X\n", readpak, spdif_ctx->streams[0]->codec->codec_id); + //av_dump_format(spdif_ctx, 0, "test1", 0); + + if(spdif_codec_ctx == NULL){ + if (spdif_ctx->nb_streams == 0){ + printf("could not find a stream\n"); + goto retry; + } - AVCodecContext *spdif_codec_ctx = avcodec_alloc_context3(spdif_codec); - if (!spdif_codec_ctx) - errx(1, "cannot allocate codec"); - spdif_codec_ctx->request_sample_fmt = AV_SAMPLE_FMT_S16; - if (avcodec_open2(spdif_codec_ctx, spdif_codec, NULL) != 0) - errx(1, "cannot open codec"); + spdif_codec = avcodec_find_decoder(spdif_ctx->streams[0]->codec->codec_id); + if (!spdif_codec) { + printf("could not find codec\n"); + goto retry; + }else{ + printf("found codec\n"); + } + spdif_codec_ctx = avcodec_alloc_context3(spdif_codec); + if (!spdif_codec_ctx) + errx(1, "cannot allocate codec"); + if (avcodec_open2(spdif_codec_ctx, spdif_codec, NULL) != 0) + errx(1, "cannot open codec"); + + + //av_dump_format(spdif_ctx, 0, "test1", 0); + /* + printf("c: %d, samples %d, format %d\n", + spdif_codec_ctx->channels, frame->nb_samples, + spdif_codec_ctx->sample_fmt); + */ - AVPacket pkt, pkt1 = {.size = 0, .data = NULL}; - av_init_packet(&pkt1); - pkt = pkt1; + } + int got_frame; + int processed_len = avcodec_decode_audio4(spdif_codec_ctx, frame, &got_frame, &pkt); + if (processed_len < 0) + errx(1, "cannot decode input"); + + pkt.data += processed_len; + pkt.size -= processed_len; + if(swr == NULL){ + swr = resample_init(spdif_codec_ctx); + printf("c: %d, s: %d\n",spdif_codec_ctx->channels, spdif_codec_ctx->sample_rate); + } - AVFrame frame; + swr_convert(swr, &resamples, frame->nb_samples, frame->data, frame->nb_samples); + + channels = spdif_codec_ctx->channels; + sample_rate = spdif_codec_ctx->sample_rate; + whattoplay = (char*)resamples; + howmuch = av_samples_get_buffer_size(NULL, + spdif_codec_ctx->channels, + frame->nb_samples, + AV_SAMPLE_FMT_S16, + 1); + }else{ + //found wav + channels = 2; + sample_rate = 48000; + whattoplay = (char*)pd.buf; + howmuch = pd.buf_size; + } +#endif - for (;;) { + if (!out_dev) { + out_dev = open_output(out_driver_id, + out_dev_opts, + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * 8, + codecHanlder.currentChannelCount, + codecHanlder.currentSampleRate); + if (!out_dev) + errx(1, "cannot open audio output"); + } + //found wav + if(!ao_play(out_dev, resamples, howmuch)){ + goto retry; + } +#if 0 if (pkt.size == 0) { - av_free_packet(&pkt1); - int e = av_read_frame(spdif_ctx, &pkt1); + av_free_packet(&pkt); + int e = av_read_frame(alsa_ctx, &pkt); if (e != 0) { printf("reading frame failed: %d\n", e); goto retry; + + } + /* + if (spdif_ctx->nb_streams == 0){ + if(oldCodec != alsa_ctx->streams[0]->codec->codec_id){ + oldCodec = alsa_ctx->streams[0]->codec->codec_id; + av_dump_format(alsa_ctx, 0, "input", 0); + } + }else{ + //printf("no Stream\n"); } - pkt = pkt1; + */ + /* + if(pkt.size >= 4){ + if(pkt.data[0] !=0 + && pkt.data[1] !=0 + && pkt.data[2] !=0 + && pkt.data[3] !=0){ + if( pkt.data[0] == 0xF8 && pkt.data[1] == 0x72 + || pkt.data[1] == 0xF8 && pkt.data[0] == 0x72){ + printf("size: %d, header %02X %02X %02X %02X\n", pkt.size, + pkt.data[0], pkt.data[1],pkt.data[2],pkt.data[3]); + } + } + uint16_t * ptr = (uint16_t*)pkt.data; + int i=0; + while (inb_streams == 0){ + printf("could not find a stream\n"); + goto retry; + } + + spdif_codec = avcodec_find_decoder(spdif_ctx->streams[0]->codec->codec_id); + if (!spdif_codec) { + printf("could not find codec\n"); + goto retry; + }else{ + printf("found codec\n"); + } + spdif_codec_ctx = avcodec_alloc_context3(spdif_codec); + if (!spdif_codec_ctx) + errx(1, "cannot allocate codec"); + if (avcodec_open2(spdif_codec_ctx, spdif_codec, NULL) != 0) + errx(1, "cannot open codec"); + + av_dump_format(spdif_ctx, 0, "test1", 0); + printf("c: %d, samples %d, format %d\n", + spdif_codec_ctx->channels, frame->nb_samples, + spdif_codec_ctx->sample_fmt); - avcodec_get_frame_defaults(&frame); - int got_frame = 0; - int processed_len = avcodec_decode_audio4(spdif_codec_ctx, &frame, &got_frame, &pkt); + } + + int processed_len = avcodec_decode_audio4(spdif_codec_ctx, frame, &got_frame, &pkt); if (processed_len < 0) errx(1, "cannot decode input"); + */ pkt.data += processed_len; pkt.size -= processed_len; - +#else + pkt.size = 0; +#endif if (!got_frame) continue; @@ -334,25 +460,35 @@ main(int argc, char **argv) * We open the output only here, because we need a full frame decoded * before we can know the output format. */ + out_dev = open_output(out_driver_id, out_dev_opts, - av_get_bytes_per_sample(spdif_codec_ctx->sample_fmt) * 8, + av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * 8, spdif_codec_ctx->channels, spdif_codec_ctx->sample_rate); if (!out_dev) errx(1, "cannot open audio output"); + + swr = resample_init(spdif_codec_ctx); + //av_dump_format(spdif_ctx, 0, "test1", 0); + } int framesize = av_samples_get_buffer_size(NULL, spdif_codec_ctx->channels, - frame.nb_samples, - spdif_codec_ctx->sample_fmt, + frame->nb_samples, + AV_SAMPLE_FMT_S16, 1); +/* + printf("fs: %d, c: %d, samples %d, format %d\n", + framesize, spdif_codec_ctx->channels, frame->nb_samples, + spdif_codec_ctx->sample_fmt); +*/ -#if DEBUG +#ifdef DEBUG int max = 0; - int16_t *fb = (void *)frame.data[0]; - for (int i = 0; i < frame.nb_samples * spdif_codec_ctx->channels; ++i) { + int16_t *fb = (void *)frame->data[0]; + for (int i = 0; i < frame->nb_samples * spdif_codec_ctx->channels; ++i) { int v = fb[i]; if (v < 0) v = -v; @@ -363,14 +499,20 @@ main(int argc, char **argv) /* Debug latency */ for (int i = 0; i < max / 100; ++i) putchar('*'); - printf("\n"); - //printf("%d\n", max); + //printf("\n"); + printf("%d\n", max); #endif - if (!ao_play(out_dev, (void *)frame.data[0], framesize)) { - goto retry; + if(frame->data[0] != NULL){ + swr_convert(swr, &samples, frame->nb_samples, frame->data, frame->nb_samples); + if(!ao_play(out_dev, (char*)samples, framesize)){ + goto retry; + } + }else{ + printf("frame data == NULL\n"); } +#endif } - + CodecHandler_deinit(&codecHanlder); return (0); } From 3e600bd009a9a2676b402d3942c5da878d2f8fc9 Mon Sep 17 00:00:00 2001 From: Sebastian Morgenstern Date: Sat, 9 May 2015 20:37:12 +0200 Subject: [PATCH 02/13] added Readme infos --- README.md | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 80c32fd..54aa2bf 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,36 @@ -spdif-loop -========== +spdif-decoder +============= SPDIF to 5.1 software loop decoder. Use if you want to connect a -digital surround signal (Dolby Digital), such as an Xbox 360 S, via +digital surround signal (Dolby Digital), such as an Xbox 360 S or TV, via your PC to your 5.1 (analog) stereo. Like a digital receiver/decoder, just in software. Requirements ------------ +- libasound2-dev +- libao-dev +- cmake +- ffmpeg from Source https://www.ffmpeg.org/download.html (tested with 2.6.2) -- ffmpeg/libav -- libao - - -Build +Build ffmpeg shared library with a minimal set for alsa and AC3 support ----- - +./configure --enable-shared --disable-static --disable-everything --enable-demuxer=spdif --enable-decoder=ac3 --enable-indev=alsa make +Build spdif-decoder +----- +Prepare CMakeLists.txt - set FFMPEG Var with Path to ffmpeg +cmake . +make Run --- I run it like this: - ./spdif-loop -i hw:CARD=Device -d pulse -o alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-surround51 + ./spdif-decoder -i hw:CARD=Device -d pulse -o alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-surround51 Alsa's `hw:CARD=Device` is my SPDIF input. You can list your alsa devices with @@ -42,4 +47,8 @@ I had to use `amixer` to set the capture source to SPIF. In this case Contact ------- +Sebastian Morgenstern + +Thanks to +------- Simon Schubert <2@0x2c.org> From 1ad737d35a947899ad7fa3bf235c92726c6863ea Mon Sep 17 00:00:00 2001 From: Sebastian Morgenstern Date: Sat, 9 May 2015 20:46:08 +0200 Subject: [PATCH 03/13] marked code as code --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 54aa2bf..fab9e4d 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ Requirements Build ffmpeg shared library with a minimal set for alsa and AC3 support ----- -./configure --enable-shared --disable-static --disable-everything --enable-demuxer=spdif --enable-decoder=ac3 --enable-indev=alsa -make + ./configure --enable-shared --disable-static --disable-everything --enable-demuxer=spdif --enable-decoder=ac3 --enable-indev=alsa + make Build spdif-decoder ----- Prepare CMakeLists.txt - set FFMPEG Var with Path to ffmpeg -cmake . -make + cmake . + make Run --- From a63bf2ded468f791240ad09d80f6a54edd852aae Mon Sep 17 00:00:00 2001 From: Sebastian Morgenstern Date: Sat, 9 May 2015 20:47:23 +0200 Subject: [PATCH 04/13] I can't write MarkDown --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fab9e4d..b5d0ed8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Build ffmpeg shared library with a minimal set for alsa and AC3 support Build spdif-decoder ----- Prepare CMakeLists.txt - set FFMPEG Var with Path to ffmpeg + cmake . make From 705e19e37133173b2f1e6be1bf5a12e9a74768f1 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 00:48:52 +0100 Subject: [PATCH 05/13] Compiles and works with FFMPEG-4.3.1 --- CMakeLists.txt | 2 +- myspdifdec.c | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b274555..c61f36a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ add_executable (spdif-decoder spdif-loop.c ) -SET(FFMPEG /home/sebastian/software/ffmpeg-2.6.1) +SET(FFMPEG ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpeg-4.3.1) target_include_directories (spdif-decoder PUBLIC ${FFMPEG} diff --git a/myspdifdec.c b/myspdifdec.c index 50afdd1..be30759 100644 --- a/myspdifdec.c +++ b/myspdifdec.c @@ -29,15 +29,17 @@ #include #include "myspdif.h" #include -#include +#include "libavcodec/adts_parser.h" +#include "libavutil/bswap.h" static int spdif_get_offset_and_codec(AVFormatContext *s, enum IEC61937DataType data_type, - const uint8_t *buf, int *offset, + const char *buf, int *offset, enum AVCodecID *codec) { - AACADTSHeaderInfo aac_hdr; - GetBitContext gbc; + uint32_t samples; + uint8_t frames; + int ret; switch (data_type & 0xff) { case IEC61937_AC3: @@ -57,13 +59,13 @@ static int spdif_get_offset_and_codec(AVFormatContext *s, *codec = AV_CODEC_ID_MP3; break; case IEC61937_MPEG2_AAC: - init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8); - if (avpriv_aac_parse_header(&gbc, &aac_hdr) < 0) { + ret = av_adts_header_parse(buf, &samples, &frames); + if (ret < 0) { if (s) /* be silent during a probe */ av_log(s, AV_LOG_ERROR, "Invalid AAC packet in IEC 61937\n"); - return AVERROR_INVALIDDATA; + return ret; } - *offset = aac_hdr.samples << 2; + *offset = samples << 2; *codec = AV_CODEC_ID_AAC; break; case IEC61937_MPEG2_LAYER1_LSF: @@ -91,11 +93,6 @@ static int spdif_get_offset_and_codec(AVFormatContext *s, *codec = AV_CODEC_ID_DTS; break; default: - if (s) { /* be silent during a probe */ - - //avpriv_request_sample(s, "Data type 0x%04x in IEC 61937", - // data_type); - } return AVERROR_PATCHWELCOME; } return 0; From f46c59dfa01eee5981921c760c4dabb3e636fc63 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 01:11:18 +0100 Subject: [PATCH 06/13] Fixing a memory leak --- spdif-loop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index 812d57e..9f6b23a 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -178,6 +178,8 @@ main(int argc, char **argv) AVFormatContext *alsa_ctx = NULL; ao_device *out_dev = NULL; + char *resamples = malloc(1*1024*1024); + if (0) { retry: printf("failure...\n"); @@ -211,7 +213,6 @@ main(int argc, char **argv) errx(1, "cannot open avio_alloc_context"); } - spdif_ctx->pb = avio_alloc_context(alsa_buf, alsa_buf_size, 0, &read_state, alsa_reader, NULL, NULL); if (!spdif_ctx->pb) errx(1, "cannot set up alsa reader"); @@ -228,8 +229,6 @@ main(int argc, char **argv) AVPacket pkt = {.size = 0, .data = NULL}; av_init_packet(&pkt); - char *resamples= malloc(1*1024*1024); - uint32_t howmuch = 0; CodecHandler codecHanlder; From b55c6b7520ea9e39ed15073ab485752ebf1a8f89 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 01:15:02 +0100 Subject: [PATCH 07/13] Do not commit commented out code --- spdif-loop.c | 217 --------------------------------------------------- 1 file changed, 217 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index 9f6b23a..cbc0087 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -280,74 +280,6 @@ main(int argc, char **argv) codecHanlder.currentChannelLayout = 0; howmuch = garbagefilled; } -#if 0 - if(r>0){ - //avio_tell() - avio_seek(avio_ctx, avio_ctx->buffer_size, SEEK_CUR); - //found spdif - spdif_ctx->pb = avio_ctx; - int readpak = spdif_fmt->read_packet(spdif_ctx, &pkt); - printf("readpak %d: %X\n", readpak, spdif_ctx->streams[0]->codec->codec_id); - //av_dump_format(spdif_ctx, 0, "test1", 0); - - if(spdif_codec_ctx == NULL){ - if (spdif_ctx->nb_streams == 0){ - printf("could not find a stream\n"); - goto retry; - } - - spdif_codec = avcodec_find_decoder(spdif_ctx->streams[0]->codec->codec_id); - if (!spdif_codec) { - printf("could not find codec\n"); - goto retry; - }else{ - printf("found codec\n"); - } - spdif_codec_ctx = avcodec_alloc_context3(spdif_codec); - if (!spdif_codec_ctx) - errx(1, "cannot allocate codec"); - if (avcodec_open2(spdif_codec_ctx, spdif_codec, NULL) != 0) - errx(1, "cannot open codec"); - - - //av_dump_format(spdif_ctx, 0, "test1", 0); - /* - printf("c: %d, samples %d, format %d\n", - spdif_codec_ctx->channels, frame->nb_samples, - spdif_codec_ctx->sample_fmt); - */ - - } - int got_frame; - int processed_len = avcodec_decode_audio4(spdif_codec_ctx, frame, &got_frame, &pkt); - if (processed_len < 0) - errx(1, "cannot decode input"); - - pkt.data += processed_len; - pkt.size -= processed_len; - if(swr == NULL){ - swr = resample_init(spdif_codec_ctx); - printf("c: %d, s: %d\n",spdif_codec_ctx->channels, spdif_codec_ctx->sample_rate); - } - - swr_convert(swr, &resamples, frame->nb_samples, frame->data, frame->nb_samples); - - channels = spdif_codec_ctx->channels; - sample_rate = spdif_codec_ctx->sample_rate; - whattoplay = (char*)resamples; - howmuch = av_samples_get_buffer_size(NULL, - spdif_codec_ctx->channels, - frame->nb_samples, - AV_SAMPLE_FMT_S16, - 1); - }else{ - //found wav - channels = 2; - sample_rate = 48000; - whattoplay = (char*)pd.buf; - howmuch = pd.buf_size; - } -#endif if (!out_dev) { out_dev = open_output(out_driver_id, @@ -362,155 +294,6 @@ main(int argc, char **argv) if(!ao_play(out_dev, resamples, howmuch)){ goto retry; } -#if 0 - if (pkt.size == 0) { - av_free_packet(&pkt); - int e = av_read_frame(alsa_ctx, &pkt); - if (e != 0) { - printf("reading frame failed: %d\n", e); - goto retry; - - } - /* - if (spdif_ctx->nb_streams == 0){ - if(oldCodec != alsa_ctx->streams[0]->codec->codec_id){ - oldCodec = alsa_ctx->streams[0]->codec->codec_id; - av_dump_format(alsa_ctx, 0, "input", 0); - } - }else{ - //printf("no Stream\n"); - } - */ - /* - if(pkt.size >= 4){ - if(pkt.data[0] !=0 - && pkt.data[1] !=0 - && pkt.data[2] !=0 - && pkt.data[3] !=0){ - if( pkt.data[0] == 0xF8 && pkt.data[1] == 0x72 - || pkt.data[1] == 0xF8 && pkt.data[0] == 0x72){ - printf("size: %d, header %02X %02X %02X %02X\n", pkt.size, - pkt.data[0], pkt.data[1],pkt.data[2],pkt.data[3]); - } - } - uint16_t * ptr = (uint16_t*)pkt.data; - int i=0; - while (inb_streams == 0){ - printf("could not find a stream\n"); - goto retry; - } - - spdif_codec = avcodec_find_decoder(spdif_ctx->streams[0]->codec->codec_id); - if (!spdif_codec) { - printf("could not find codec\n"); - goto retry; - }else{ - printf("found codec\n"); - } - spdif_codec_ctx = avcodec_alloc_context3(spdif_codec); - if (!spdif_codec_ctx) - errx(1, "cannot allocate codec"); - if (avcodec_open2(spdif_codec_ctx, spdif_codec, NULL) != 0) - errx(1, "cannot open codec"); - - av_dump_format(spdif_ctx, 0, "test1", 0); - printf("c: %d, samples %d, format %d\n", - spdif_codec_ctx->channels, frame->nb_samples, - spdif_codec_ctx->sample_fmt); - - } - - int processed_len = avcodec_decode_audio4(spdif_codec_ctx, frame, &got_frame, &pkt); - if (processed_len < 0) - errx(1, "cannot decode input"); - */ - pkt.data += processed_len; - pkt.size -= processed_len; -#else - pkt.size = 0; -#endif - if (!got_frame) - continue; - - - if (!out_dev) { - /** - * We open the output only here, because we need a full frame decoded - * before we can know the output format. - */ - - out_dev = open_output(out_driver_id, - out_dev_opts, - av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * 8, - spdif_codec_ctx->channels, - spdif_codec_ctx->sample_rate); - if (!out_dev) - errx(1, "cannot open audio output"); - - swr = resample_init(spdif_codec_ctx); - //av_dump_format(spdif_ctx, 0, "test1", 0); - - } - - int framesize = av_samples_get_buffer_size(NULL, - spdif_codec_ctx->channels, - frame->nb_samples, - AV_SAMPLE_FMT_S16, - 1); -/* - printf("fs: %d, c: %d, samples %d, format %d\n", - framesize, spdif_codec_ctx->channels, frame->nb_samples, - spdif_codec_ctx->sample_fmt); -*/ - -#ifdef DEBUG - int max = 0; - int16_t *fb = (void *)frame->data[0]; - for (int i = 0; i < frame->nb_samples * spdif_codec_ctx->channels; ++i) { - int v = fb[i]; - if (v < 0) - v = -v; - if (v > max) - max = v; - } - - /* Debug latency */ - for (int i = 0; i < max / 100; ++i) - putchar('*'); - //printf("\n"); - printf("%d\n", max); -#endif - - if(frame->data[0] != NULL){ - swr_convert(swr, &samples, frame->nb_samples, frame->data, frame->nb_samples); - if(!ao_play(out_dev, (char*)samples, framesize)){ - goto retry; - } - }else{ - printf("frame data == NULL\n"); - } -#endif } CodecHandler_deinit(&codecHanlder); return (0); From f5e3da26c0e6a831411595ee2818e0eb5fffac9a Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 01:35:44 +0100 Subject: [PATCH 08/13] Don't play garbage! It could be corrupt packets, this makes a lot of terrible noise! --- spdif-loop.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index cbc0087..f17c811 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -239,17 +239,6 @@ main(int argc, char **argv) int r = my_spdif_read_packet(spdif_ctx, &pkt, (uint8_t*)resamples, IO_BUFFER_SIZE, &garbagefilled); if(r == 0){ - //Play rest of garbage before decode newly found codec - if( garbagefilled > 0 - && codecHanlder.currentCodecID == AV_CODEC_ID_NONE - && codecHanlder.currentChannelCount == 2 - && codecHanlder.currentSampleRate == 48000 - && out_dev != NULL){ - if(!ao_play(out_dev, resamples, garbagefilled)){ - goto retry; - } - } - if(CodecHandler_loadCodec(&codecHanlder, spdif_ctx)!=0){ goto retry; } @@ -265,8 +254,7 @@ main(int argc, char **argv) if(pkt.size != 0){ printf("still some bytes left %d\n",pkt.size); } - - }else{ + } else { codecHanlder.currentCodecID = AV_CODEC_ID_NONE; if(codecHanlder.currentChannelCount != 2 || codecHanlder.currentSampleRate != 48000){ From 6240d622c5a97c290b9ae8e3dc26c32bc80fdbd9 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 01:54:43 +0100 Subject: [PATCH 09/13] Better debug and status messages to know what's going on in there. --- spdif-loop.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index f17c811..c8c54d3 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -167,7 +167,7 @@ main(int argc, char **argv) AVInputFormat *spdif_fmt = av_find_input_format("spdif"); if (!spdif_fmt) - errx(1, "cannot find spdif demux driver"); + errx(1, "cannot find S/PDIF demux driver"); const int alsa_buf_size = IO_BUFFER_SIZE; unsigned char *alsa_buf = av_malloc(alsa_buf_size); @@ -182,7 +182,6 @@ main(int argc, char **argv) if (0) { retry: - printf("failure...\n"); if (spdif_ctx) avformat_close_input(&spdif_ctx); if (alsa_ctx) @@ -198,7 +197,7 @@ main(int argc, char **argv) spdif_ctx = avformat_alloc_context(); if (!spdif_ctx) - errx(1, "cannot allocate spdif context"); + errx(1, "cannot allocate S/PDIF context"); if (avformat_open_input(&alsa_ctx, alsa_dev_name, alsa_fmt, NULL) != 0) errx(1, "cannot open alsa input"); @@ -218,14 +217,10 @@ main(int argc, char **argv) errx(1, "cannot set up alsa reader"); if (avformat_open_input(&spdif_ctx, "internal", spdif_fmt, NULL) != 0) - errx(1, "cannot open spdif input"); + errx(1, "cannot open S/PDIF input"); av_dump_format(alsa_ctx, 0, alsa_dev_name, 0); -#ifdef HAVE_AVCODEC_GET_NAME - printf("detected spdif codec %s\n", avcodec_get_name(spdif_codec_id)); -#endif - AVPacket pkt = {.size = 0, .data = NULL}; av_init_packet(&pkt); @@ -240,6 +235,7 @@ main(int argc, char **argv) if(r == 0){ if(CodecHandler_loadCodec(&codecHanlder, spdif_ctx)!=0){ + printf("Could not load codec %s.\n", avcodec_get_name(codecHanlder.currentCodecID)); goto retry; } @@ -250,6 +246,7 @@ main(int argc, char **argv) ao_close(out_dev); out_dev = NULL; } + printf("Detected S/PDIF codec %s\n", avcodec_get_name(codecHanlder.currentCodecID)); } if(pkt.size != 0){ printf("still some bytes left %d\n",pkt.size); @@ -258,6 +255,8 @@ main(int argc, char **argv) codecHanlder.currentCodecID = AV_CODEC_ID_NONE; if(codecHanlder.currentChannelCount != 2 || codecHanlder.currentSampleRate != 48000){ + printf("Detected S/PDIF uncompressed audio\n"); + if (out_dev) { ao_close(out_dev); out_dev = NULL; @@ -280,6 +279,7 @@ main(int argc, char **argv) } //found wav if(!ao_play(out_dev, resamples, howmuch)){ + printf("Could not play audio to output device..."); goto retry; } } From 171c30a5e359867023e16bee36e649478a9defdf Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 01:55:27 +0100 Subject: [PATCH 10/13] No need for the `garbagefilled` variable when we already have a variable for packet size --- spdif-loop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index c8c54d3..c8534b1 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -230,8 +230,7 @@ main(int argc, char **argv) CodecHandler_init(&codecHanlder); printf("start loop\n"); while (1) { - int garbagefilled = 0; - int r = my_spdif_read_packet(spdif_ctx, &pkt, (uint8_t*)resamples, IO_BUFFER_SIZE, &garbagefilled); + int r = my_spdif_read_packet(spdif_ctx, &pkt, (uint8_t*)resamples, IO_BUFFER_SIZE, &howmuch); if(r == 0){ if(CodecHandler_loadCodec(&codecHanlder, spdif_ctx)!=0){ @@ -265,7 +264,6 @@ main(int argc, char **argv) codecHanlder.currentChannelCount = 2; codecHanlder.currentSampleRate = 48000; codecHanlder.currentChannelLayout = 0; - howmuch = garbagefilled; } if (!out_dev) { From 312c936492196d2c99a2398c887e7d0dd7e7d8d6 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 02:03:54 +0100 Subject: [PATCH 11/13] Updated the README --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b5d0ed8..491c732 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,15 @@ digital surround signal (Dolby Digital), such as an Xbox 360 S or TV, via your PC to your 5.1 (analog) stereo. Like a digital receiver/decoder, just in software. +FFMPEG is licenced under the GNU Lesser General Public License version 2.1 and so is +this piece of software. Requirements ------------ - libasound2-dev - libao-dev - cmake -- ffmpeg from Source https://www.ffmpeg.org/download.html (tested with 2.6.2) +- ffmpeg from Source https://www.ffmpeg.org/download.html (tested with 4.3.1) Build ffmpeg shared library with a minimal set for alsa and AC3 support ----- @@ -21,7 +23,7 @@ Build ffmpeg shared library with a minimal set for alsa and AC3 support Build spdif-decoder ----- -Prepare CMakeLists.txt - set FFMPEG Var with Path to ffmpeg +Prepare CMakeLists.txt - set FFMPEG Var with Path to ffmpeg (if not in ../ffmpeg-4.3.1) cmake . make @@ -45,11 +47,7 @@ I had to use `amixer` to set the capture source to SPIF. In this case amixer -c Device set 'PCM Capture Source' 'IEC958 In' # set input -Contact -------- - -Sebastian Morgenstern - Thanks to ------- +Sebastian Morgenstern Simon Schubert <2@0x2c.org> From f4cfe62c2dfbe7e2db9a436f9b73a34b4ad75571 Mon Sep 17 00:00:00 2001 From: NeoAcheron Date: Sun, 24 Jan 2021 02:04:34 +0100 Subject: [PATCH 12/13] Just some nitpicking... --- spdif-loop.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spdif-loop.c b/spdif-loop.c index c8534b1..148f78e 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -194,7 +194,6 @@ main(int argc, char **argv) printf("retrying.\n"); } - spdif_ctx = avformat_alloc_context(); if (!spdif_ctx) errx(1, "cannot allocate S/PDIF context"); @@ -251,9 +250,10 @@ main(int argc, char **argv) printf("still some bytes left %d\n",pkt.size); } } else { - codecHanlder.currentCodecID = AV_CODEC_ID_NONE; - if(codecHanlder.currentChannelCount != 2 || - codecHanlder.currentSampleRate != 48000){ + if(codecHanlder.currentCodecID != AV_CODEC_ID_NONE || + codecHanlder.currentChannelCount != 2 || + codecHanlder.currentSampleRate != 48000){ + printf("Detected S/PDIF uncompressed audio\n"); if (out_dev) { @@ -261,6 +261,7 @@ main(int argc, char **argv) out_dev = NULL; } } + codecHanlder.currentCodecID = AV_CODEC_ID_NONE; codecHanlder.currentChannelCount = 2; codecHanlder.currentSampleRate = 48000; codecHanlder.currentChannelLayout = 0; From 6029eb0cdc989bc2e78d50f57463717bf58a9959 Mon Sep 17 00:00:00 2001 From: Jaehong Park Date: Mon, 26 Dec 2022 12:40:01 -0800 Subject: [PATCH 13/13] Update spdif-loop.c pkt memory overflows. it needs to be freeing the pkt buffer. --- spdif-loop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/spdif-loop.c b/spdif-loop.c index 148f78e..4faf934 100644 --- a/spdif-loop.c +++ b/spdif-loop.c @@ -281,6 +281,7 @@ main(int argc, char **argv) printf("Could not play audio to output device..."); goto retry; } + av_packet_unref(&pkt); } CodecHandler_deinit(&codecHanlder); return (0);