summaryrefslogtreecommitdiff
path: root/decoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'decoder.c')
-rw-r--r--decoder.c335
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: