From 1e6635acba6e5ee479164eec3e8120bf3e76dcc2 Mon Sep 17 00:00:00 2001 From: beveloper Date: Sat, 6 Mar 2004 22:46:49 +0000 Subject: [PATCH] Added the AC3-decoding plugin. It must be treated as GPL licensed, as it' using the GPL'ed liba52. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6923 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/ac3_decoder/Jamfile | 15 + .../media/plugins/ac3_decoder/ac3_decoder.cpp | 391 ++++++++++++++++++ .../media/plugins/ac3_decoder/ac3_decoder.h | 90 ++++ 3 files changed, 496 insertions(+) create mode 100644 src/add-ons/media/plugins/ac3_decoder/Jamfile create mode 100644 src/add-ons/media/plugins/ac3_decoder/ac3_decoder.cpp create mode 100644 src/add-ons/media/plugins/ac3_decoder/ac3_decoder.h diff --git a/src/add-ons/media/plugins/ac3_decoder/Jamfile b/src/add-ons/media/plugins/ac3_decoder/Jamfile new file mode 100644 index 0000000000..c088dfd184 --- /dev/null +++ b/src/add-ons/media/plugins/ac3_decoder/Jamfile @@ -0,0 +1,15 @@ +SubDir OBOS_TOP src add-ons media plugins ac3_decoder ; + +UsePrivateHeaders media ; + +Addon ac3_decoder : media plugins : + ac3_decoder.cpp + : + false + : + liba52.a +; + +LinkSharedOSLibs ac3_decoder : be libmedia.so ; + +SubIncludeGPL OBOS_TOP src add-ons media plugins ac3_decoder liba52 ; diff --git a/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.cpp b/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.cpp new file mode 100644 index 0000000000..5232f1fc42 --- /dev/null +++ b/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.cpp @@ -0,0 +1,391 @@ +/* + * ac3_decoder.cpp + * Copyright (C) 2004 Marcus Overhagen + * + * This file is part of an AC-3 decoder plugin for the OpenBeOS + * media kit. OpenBeOS can be found at http://www.openbeos.org + * + * This file is distributed under the terms of the MIT license. + * You can also use it under the terms of the GPL version 2. + * + * Since this file is linked against a GPL licensed library, + * the combined work of this file and the liba52 library, the + * ac3_decoder plugin must be distributed as GPL licensed. + */ + +#include +#include +#include +#include "ac3_decoder.h" + + +#if 0 + #define TRACE printf +#else + #define TRACE(a...) +#endif + + +AC3Decoder::AC3Decoder() + : fInputBuffer(0) + , fInputBufferSize(0) + , fInputChunk(0) + , fInputChunkSize(0) + , fInputChunkOffset(0) + , fStartTime(0) + , fHasStreamInfo(false) + , fDisableDynamicCompression(false) + , fChannelCount(0) + , fChannelMask(0) +{ + fInputBuffer = malloc(INPUT_BUFFER_MAX_SIZE); + + fState = a52_init(0); + fSamples = a52_samples(fState); +} + + +AC3Decoder::~AC3Decoder() +{ + free(fInputBuffer); + a52_free(fState); +} + + +void +AC3Decoder::GetCodecInfo(media_codec_info *info) +{ + strcpy(info->short_name, "AC-3"); + strcpy(info->pretty_name, "AC-3 audio decoder (liba52)"); +} + + +status_t +AC3Decoder::Setup(media_format *ioEncodedFormat, + const void *infoBuffer, int32 infoSize) +{ + return B_OK; +} + + +status_t +AC3Decoder::NegotiateOutputFormat(media_format *ioDecodedFormat) +{ + if (!fHasStreamInfo) + fHasStreamInfo = GetStreamInfo(); + if (!fHasStreamInfo) { + TRACE("AC3Decoder::NegotiateOutputFormat: couldn't get stream info\n"); + return false; + } + + TRACE("AC3Decoder::NegotiateOutputFormat: fFlags 0x%x, fFrameRate %d, fBitRate %d\n", fFlags, fFrameRate, fBitRate); + + // Sample offsets of the output buffer are stored in fInterleaveOffset + // When a channel is not present, it is skipped, the rest is shifted left + // The block of samples order presented by liba52 is + // 0 = LFE, 1 = left, 2 = center, 3 = right, 4 = left-surround, 5 = right-surround + // The required sample order in the output buffer is: + // 0 = B_CHANNEL_LEFT, 1 = B_CHANNEL_RIGHT, 2 = B_CHANNEL_CENTER, 3 = B_CHANNEL_SUB + // 4 = B_CHANNEL_REARLEFT, 5 = B_CHANNEL_REARRIGHT, 6 = B_CHANNEL_BACK_CENTER + // y = fInterleaveOffset[x] with y = media kit and x = liba52 (beware of non present channels, they are skipped) + + if (fFlags & A52_LFE) { + fChannelCount = 1; + fChannelMask = B_CHANNEL_SUB; + } else { + fChannelCount = 0; + fChannelMask = 0; + } + fInterleaveOffset = 0; + + switch (fFlags & A52_CHANNEL_MASK) { + case A52_CHANNEL: // XXX two independant mono channels + { + fChannelCount += 2; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + static int lfe_offsets[6] = { 2, 0, 1 }; + static int nrm_offsets[6] = { 0, 1 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_MONO: { + fChannelCount += 1; + fChannelMask |= B_CHANNEL_LEFT; + static int lfe_offsets[6] = { 1, 0 }; + static int nrm_offsets[6] = { 0 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_STEREO: + { + fChannelCount += 2; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + static int lfe_offsets[6] = { 2, 0, 1 }; + static int nrm_offsets[6] = { 0, 1 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_3F: // 3 front channels (left, center, right) + { + fChannelCount += 3; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT | B_CHANNEL_CENTER; + static int lfe_offsets[6] = { 3, 0, 2, 1 }; + static int nrm_offsets[6] = { 0, 2, 1 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_2F1R: // 2 front, 1 rear surround channel (L, R, S) + { + fChannelCount += 3; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT | B_CHANNEL_BACK_CENTER; + static int lfe_offsets[6] = { 2, 0, 3, 1 }; + static int nrm_offsets[6] = { 0, 2, 1 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + + case A52_3F1R: // 3 front, 1 rear surround channel (L, C, R, S) + { + fChannelCount += 4; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT | B_CHANNEL_CENTER | B_CHANNEL_BACK_CENTER; + static int lfe_offsets[6] = { 3, 0, 2, 1, 4 }; + static int nrm_offsets[6] = { 0, 2, 1, 3 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_2F2R: // 2 front, 2 rear surround channels (L, R, LS, RS) + { + fChannelCount += 4; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT | B_CHANNEL_REARLEFT | B_CHANNEL_REARRIGHT; + static int lfe_offsets[6] = { 2, 0, 1, 3, 4}; + static int nrm_offsets[6] = { 0, 1, 2, 3 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_3F2R: // 3 front, 2 rear surround channels (L, C, R, LS, RS) + { + fChannelCount += 5; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT | B_CHANNEL_CENTER | B_CHANNEL_REARLEFT | B_CHANNEL_REARRIGHT; + static int lfe_offsets[6] = { 3, 0, 2, 1, 4, 5 }; + static int nrm_offsets[6] = { 0, 2, 1, 3, 4 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + case A52_DOLBY: + { + fChannelCount += 2; + fChannelMask |= B_CHANNEL_LEFT | B_CHANNEL_RIGHT; + static int lfe_offsets[6] = { 2, 0, 1 }; + static int nrm_offsets[6] = { 0, 1 }; + fInterleaveOffset = (fFlags & A52_LFE) ? lfe_offsets : nrm_offsets; + break; + } + + default: + TRACE("AC3Decoder::NegotiateOutputFormat: can't decode channel setup\n"); + return B_ERROR; + } + + ioDecodedFormat->type = B_MEDIA_RAW_AUDIO; + ioDecodedFormat->u.raw_audio.frame_rate = fFrameRate; + ioDecodedFormat->u.raw_audio.channel_count = fChannelCount; + ioDecodedFormat->u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT; + ioDecodedFormat->u.raw_audio.byte_order = B_MEDIA_HOST_ENDIAN; + ioDecodedFormat->u.raw_audio.buffer_size = 6 * 256 * fChannelCount * sizeof(float); + ioDecodedFormat->u.raw_audio.channel_mask = fChannelMask; + + return B_OK; +} + + +status_t +AC3Decoder::Seek(uint32 seekTo, + int64 seekFrame, int64 *frame, + bigtime_t seekTime, bigtime_t *time) +{ + fInputChunkSize = 0; + fInputBufferSize = 0; + return B_OK; +} + + +status_t +AC3Decoder::Decode(void *buffer, int64 *frameCount, + media_header *mediaHeader, media_decode_info *info) +{ +// bigtime_t start = system_time(); + + mediaHeader->start_time = fStartTime; + +// printf("AC3Decoder::Decode: start time %Ld\n", fStartTime); + + set_thread_priority(find_thread(0), 7); + + if (!DecodeNext()) { + TRACE("AC3Decoder::Decode: DecodeNext failed\n"); + return B_ERROR; + } + + /* + Every A/52 frame is composed of 6 blocks, each with an output of 256 + samples for each channel. The a52_block() function decodes the next + block in the frame, and should be called 6 times to decode all of the + audio in the frame. After each call, you should extract the audio data + from the sample buffer. + */ + + // fInterleaveOffset[] is used to provide a correct interleave of + // multi channel audio + + for (int i = 0; i < 6; i++) { + a52_block(fState); + for (int j = 0; j < fChannelCount; j++) { + float *buf = (float *) buffer; + buf += i * 256 * fChannelCount + fInterleaveOffset[j]; + for (int k = 0; k < 256; k++) { + *buf = fSamples[j * 256 + k]; + buf += fChannelCount; + } + } + } + + fStartTime += 6 * 256 * 1000000LL / fFrameRate; + *frameCount = 6 * 256; + +// printf("AC3Decoder::Decode: finished, %Ld usec\n", system_time() - start); + + return B_OK; +} + +bool +AC3Decoder::InputGetData(void **buffer, int size) +{ + TRACE("AC3Decoder::InputGetData: %d bytes enter\n", size); + + for (;;) { + if (fInputBufferSize >= size) { + *buffer = fInputBuffer; + TRACE("AC3Decoder::InputGetData: finished\n"); + return true; + } + if (fInputChunkSize > 0) { + int bytes = min_c(size - fInputBufferSize, fInputChunkSize); + memcpy((char *)fInputBuffer + fInputBufferSize, (char *)fInputChunk + fInputChunkOffset, bytes); + fInputChunkOffset += bytes; + fInputChunkSize -= bytes; + fInputBufferSize += bytes; + continue; + } + while (fInputChunkSize == 0) { + fInputChunkOffset = 0; + media_header mh; + if (B_OK != GetNextChunk(&fInputChunk, &fInputChunkSize, &mh)) { + TRACE("AC3Decoder::InputGetData: failed\n"); + return false; + } + fStartTime = mh.start_time; + TRACE("AC3Decoder::InputGetData: got new chunk, %ld bytes, start time %Ld\n", fInputChunkSize, fStartTime); + } + } +} + +void +AC3Decoder::InputRemoveData(int size) +{ + fInputBufferSize -= size; + + if (fInputBufferSize) + memmove(fInputBuffer, (char *)fInputBuffer + size, fInputBufferSize); +} + +bool +AC3Decoder::GetStreamInfo() +{ + for (;;) { + void *input; + if (!InputGetData(&input, 7)) { + TRACE("AC3Decoder::GetStreamInfo: can't get 7 bytes\n"); + return false; + } + if (0 != a52_syncinfo((uint8_t *)input, &fFlags, &fFrameRate, &fBitRate)) + return true; + TRACE("AC3Decoder::GetStreamInfo: a52_syncinfo failed\n"); + InputRemoveData(1); + } +} + +bool +AC3Decoder::DecodeNext() +{ + for (;;) { + void *input; + if (!InputGetData(&input, 7)) { + TRACE("AC3Decoder::DecodeNext: can't get 7 bytes\n"); + return false; + } + + int flags, sample_rate, bit_rate; + + int bytes = a52_syncinfo((uint8_t *)input, &flags, &sample_rate, &bit_rate); + if (bytes == 0) { + TRACE("AC3Decoder::DecodeNext: syncinfo failed\n"); + InputRemoveData(1); + continue; + } + + if (!InputGetData(&input, bytes)) { + TRACE("AC3Decoder::DecodeNext: can't get %d data bytes\n", bytes); + return false; + } + +// printf("fFlags 0x%x, flags 0x%x\n", fFlags, flags); + + flags = fFlags | A52_ADJUST_LEVEL; + sample_t level = 1.0; + if (0 != a52_frame(fState, (uint8_t *)input, &flags, &level, 0)) { + TRACE("AC3Decoder::DecodeNext: a52_frame failed\n"); + return false; + } + + TRACE("decoded %d bytes, flags 0x%x\n", bytes, fFlags); + + InputRemoveData(bytes); + + if (fDisableDynamicCompression) + a52_dynrng(fState, NULL, 0); + + return true; + } +} + + +Decoder * +AC3DecoderPlugin::NewDecoder(uint index) +{ + return new AC3Decoder; +} + +status_t +AC3DecoderPlugin::GetSupportedFormats(media_format ** formats, size_t * count) +{ + static media_format format; + + format.type = B_MEDIA_ENCODED_AUDIO; + format.u.encoded_audio = media_encoded_audio_format::wildcard; + + *formats = &format; + *count = 1; + + media_format_description desc; + desc.family = B_WAV_FORMAT_FAMILY; + desc.u.wav.codec = 0x2000; + + BMediaFormats mediaFormats; + return mediaFormats.MakeFormatFor(&desc, 1, &format); +} + + +MediaPlugin *instantiate_plugin() +{ + return new AC3DecoderPlugin; +} diff --git a/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.h b/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.h new file mode 100644 index 0000000000..37fc9f84c5 --- /dev/null +++ b/src/add-ons/media/plugins/ac3_decoder/ac3_decoder.h @@ -0,0 +1,90 @@ +/* + * ac3_decoder.h + * Copyright (C) 2004 Marcus Overhagen + * + * This file is part of an AC-3 decoder plugin for the OpenBeOS + * media kit. OpenBeOS can be found at http://www.openbeos.org + * + * This file is distributed under the terms of the MIT license. + * You can also use it under the terms of the GPL version 2. + * + * Since this file is linked against a GPL licensed library, + * the combined work of this file and the liba52 library, the + * ac3_decoder plugin must be distributed as GPL licensed. + */ + +#ifndef _AC3_DECODER_H +#define _AC3_DECODER_H + +#include +#include "DecoderPlugin.h" + +extern "C" { + #include "liba52/config.h" + #include "liba52/a52.h" +} + + +class AC3Decoder : public Decoder +{ +public: + AC3Decoder(); + ~AC3Decoder(); + + void GetCodecInfo(media_codec_info *info); + + status_t Setup(media_format *ioEncodedFormat, + const void *infoBuffer, int32 infoSize); + + status_t NegotiateOutputFormat(media_format *ioDecodedFormat); + + status_t Seek(uint32 seekTo, + int64 seekFrame, int64 *frame, + bigtime_t seekTime, bigtime_t *time); + + bool GetStreamInfo(); + + status_t Decode(void *buffer, int64 *frameCount, + media_header *mediaHeader, media_decode_info *info); + + bool InputGetData(void **buffer, int size); + void InputRemoveData(int size); + + bool DecodeNext(); + +private: + enum { INPUT_BUFFER_MAX_SIZE = 4000 }; // must be >= 3840 + void * fInputBuffer; + int fInputBufferSize; + + void * fInputChunk; + int32 fInputChunkSize; + int fInputChunkOffset; + + bigtime_t fStartTime; + bool fHasStreamInfo; + bool fDisableDynamicCompression; + + float * fSamples; + + a52_state_t *fState; + + int fFlags; + int fFrameRate; + int fBitRate; + + int fChannelCount; + int fChannelMask; + int *fInterleaveOffset; +}; + + +class AC3DecoderPlugin : public DecoderPlugin +{ +public: + Decoder * NewDecoder(uint index); + status_t GetSupportedFormats(media_format ** formats, size_t * count); +}; + + +#endif // _AC3_DECODER_H