diff options
Diffstat (limited to 'decoder.c')
-rw-r--r-- | decoder.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/decoder.c b/decoder.c new file mode 100644 index 0000000..266ef3c --- /dev/null +++ b/decoder.c @@ -0,0 +1,335 @@ +/* + * _ _ _ _ _____ + * __ ____| |_ __ _ __ | |_ _ __ _(_)_ __ __| |_ ___ _|___ / + * \ \ / / _` | '__|____| '_ \| | | | |/ _` | | '_ \ _____ / _` \ \/ / '__||_ \ + * \ V / (_| | | |_____| |_) | | |_| | (_| | | | | |_____| (_| |> <| | ___) | + * \_/ \__,_|_| | .__/|_|\__,_|\__, |_|_| |_| \__,_/_/\_\_| |____/ + * |_| |___/ + * + * Copyright (C) 2002-2004 Kai Möller + * Copyright (C) 2004-2010 Christian Gmeiner + * + * This file is part of vdr-plugin-dxr3. + * + * vdr-plugin-dxr3 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 version 2. + * + * vdr-plugin-dxr3 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 dxr3-plugin. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <algorithm> +#include "decoder.h" +#include "dxr3pesframe.h" +#include "dxr3audio.h" +#include "settings.h" + +// ================================== +const int LPCM_HEADER_LENGTH = 7; + +// ================================== +//! constructor +cDecoder::cDecoder() : rbuf(50000), ac3dtsDecoder(&rbuf) +{ + // setup ffmpeg + avcodec_init(); + avcodec_register_all(); + + // look for decoder + audio = avcodec_find_decoder(CODEC_ID_MP3); + + if (!audio) { + esyslog("[dxr3-decoder] no suitable audio codec found."); + esyslog("[dxr3-decoder] check your ffmpeg installation."); + exit(-1); + } + + // create a new codec context + contextAudio = avcodec_alloc_context(); + int ret = avcodec_open(contextAudio, audio); + + if (ret < 0) { + esyslog("[dxr3-decoder] failed to open codec %s.", audio->name); + exit(-1); + } + + lastBitrate = 0xff; // init with an invalid value - see checkMpegAudioHdr; +} + +// ================================== +//! deconst. +cDecoder::~cDecoder() +{ + // close codec, if it is open + avcodec_close(contextAudio); +} + +// ================================== +//! (re)init ffmpeg codec +void cDecoder::Init() +{ + avcodec_close(contextAudio); + + // create a new codec context + contextAudio = avcodec_alloc_context(); + int ret = avcodec_open(contextAudio, audio); + + if (ret < 0) { + esyslog("[dxr3-decoder] failed to open codec %s.", audio->name); + exit(-1); + } +} + +void cDecoder::decode(cDxr3PesFrame *frame, iAudio *audio) +{ + int len, out_size; + + const uint8_t *buf = frame->payload(); + int length = frame->payloadSize(); + + if (checkMpegAudioHdr(buf)) { + + // look if Bitrate has changed + if ((buf[2] & 0xf0) != (lastBitrate & 0xf0)) { + dsyslog("[dxr3-audiodecoder] found new audio header"); + + // recalculate used framesize + frameSize = calcFrameSize(buf); + dsyslog("[dxr3-audiodecoder] calculated frame size %d", frameSize); + + // we need now to reinit the deocder and to store the new + // part from the audio header + Init(); + lastBitrate = buf[2]; + } + } + + // setup AVPacket + avpkt.data = const_cast<uint8_t *>(buf); + avpkt.size = frameSize; + + while (length > 0) { + out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(51, 29, 0) + len = avcodec_decode_audio(contextAudio, (short *)(&pcmbuf), &out_size, avpkt.data, frameSize); +#elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 26, 0) + len = avcodec_decode_audio2(contextAudio, (short *)(&pcmbuf), &out_size, avpkt.data, frameSize); +#else + len = avcodec_decode_audio3(contextAudio, (short *)(&pcmbuf), &out_size, &avpkt); +#endif + + if (len <= 0) { + esyslog("[dxr3-decoder] failed to decode audio"); + return; + } + + if (out_size) { + audio->setup(contextAudio->channels, contextAudio->sample_rate); + audio->changeVolume((short *)pcmbuf, out_size); + audio->write(pcmbuf, out_size); + } + + length -= len; + avpkt.data += len; + } +} + +void cDecoder::ac3dts(cDxr3PesFrame *frame, iAudio *audio) +{ + if (cSettings::instance()->ac3AudioMode() == PCM_ENCAPSULATION) { + + uint8_t *buf = (uint8_t *)frame->payload(); + int length = frame->payloadSize(); + + ac3dtsDecoder.Check(buf, length, (uint8_t *)frame->pesStart()); + ac3dtsDecoder.Encapsulate(buf, length); + + cFrame* pFrame = 0; + while ((pFrame = rbuf.Get())) { + if (pFrame && pFrame->Count()) { + cDxr3PesFrame tempPes; + tempPes.parse(pFrame->Data(), pFrame->Count()); + int pesHeaderLength = (int) (tempPes.payload() - tempPes.pesStart()); + uint8_t* pData = pFrame->Data() + pesHeaderLength + LPCM_HEADER_LENGTH; + + for (int i = 0; i < pFrame->Count() - pesHeaderLength - LPCM_HEADER_LENGTH; i += 2) { + std::swap(pData[i], pData[i + 1]); + } + + audio->write(pFrame->Data() + pesHeaderLength + LPCM_HEADER_LENGTH, pFrame->Count() - pesHeaderLength - 7); + rbuf.Drop(pFrame); + } + } + } +} + +#if 0 +// ================================== +//! decode lpcm +void cDecoder::DecodeLpcm(cDxr3PesFrame *frame, uint32_t pts, cDxr3SyncBuffer &aBuf) +{ + const uint8_t *buf = frame->GetPayload(); + int length = frame->GetPayloadLength(); + + // all informations about the LPCM header can be found + // here http://dvd.sourceforge.net/dvdinfo/lpcm.html + + if (length > (LPCM_HEADER_LENGTH + 2)) { + // only even number of bytes are allowed + if ((length - LPCM_HEADER_LENGTH) % 2 != 0) { + esyslog("[dxr3-audiodecoder] skipping %d lpcm bytes", length); + return; + } + + // remove header and set pointer to first data byte + const uint8_t *data = buf + LPCM_HEADER_LENGTH; + length -= LPCM_HEADER_LENGTH; + + int codedSpeed = (buf[5] >> 4) & 0x03; + int speed = 0; + + switch (codedSpeed) { + case 1: + speed = 96000; + break; + + case 2: + speed = 44100; + break; + + case 3: + speed = 32000; + break; + + default: + speed = 48000; + break; + } + + int channels = (buf[5] & 0x03); + channels++; + + cFixedLengthFrame* pTempFrame = aBuf.Push(data, length, pts); + if (pTempFrame) { + pTempFrame->channels(channels); + pTempFrame->samplerate(speed); + } + } +} +#endif +// ================================== +//! checking routine +bool cDecoder::checkMpegAudioHdr(const uint8_t *head) +{ + // all informations about the mpeg audio header + // can be found at http://www.datavoyage.com/mpgscript/mpeghdr.htm + + // the header conists of four 8bit elements, described as + // AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM + + // all As must be set to 1 + if (head[0] != 0xff || (head[1] & 0xe0) != 0xe0) { + return false; + } + + // B is not allowed to be 01 (bits), as it is a reserved value + if ((head[1] & 0x18) == 0x8) { + return false; + } + + // C is not allowed to be 00 (bits), as it is a reserved value + if ((head[1] & 0x6) == 0x0) { + return false; + } + + // all Es are not allowed to be 1 + if ((head[2] & 0xf0) == 0xf0) { + return false; + } + + // all Fs are not allowed to be 1 + if ((head[2] & 0xc) == 0xc) { + return false; + } + + return true; +} + +int cDecoder::calcFrameSize(const uint8_t *header) +{ + static const int bitrates[2][3][15] = + { + { // MPEG 1 + {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, // Layer1 + {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, // Layer2 + {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} // Layer3 + }, + { // MPEG 2, 2.5 + {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, // Layer1 + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, // Layer2 + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} // Layer3 + } + }; + + static const int samplingRates[4][3] = + { + {11025, 12000, 8000, }, // MPEG 2.5 + {0, 0, 0, }, // reserved + {22050, 24000, 16000, }, // MPEG 2 + {44100, 48000, 32000 } // MPEG 1 + }; + + static const int MPEG1 = 0x3; + + // in B the version is stored + int ver = (header[1] >> 3) & 0x03; + int version = 1; // default to MPEG2 + + // determine index into arrays based on ver + if (ver == MPEG1) { + version = 0; + } + + // in C the layer version is stored + int layer = 3 - ((header[1] >> 1) & 0x03); + + // in E the bitrate index is stored + int bitrateIndex = (header[2] >> 4) & 0x0f; + + // in F the sampling rate frequency index is stored + int samplingIndex = (header[2] >> 2) & 0x03; + + // in G the padding bit is stored + int padding = (header[2] >> 1) & 0x01; + + int bitrate = bitrates[version][layer][bitrateIndex]; // kbit + bitrate *= 1000; // kbit -> bit + int samplesrate = samplingRates[ver][samplingIndex]; + + // formulars used to calculate frame size + // layer I + // FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4 + // layer II & III + // FrameLengthInBytes = 144 * BitRate / SampleRate + Padding + if (layer == 0) { + return (12 * bitrate / samplesrate + padding) * 4; + } else { + return 144 * bitrate / samplesrate + padding; + } +} + +// Local variables: +// mode: c++ +// c-file-style: "stroustrup" +// c-file-offsets: ((inline-open . 0)) +// tab-width: 4; +// indent-tabs-mode: nil +// End: |