summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohns <johns98@gmx.net>2012-02-21 20:55:28 +0100
committerJohns <johns98@gmx.net>2012-02-21 20:55:28 +0100
commit5d8dea1b6b9e15048f425f13b349e785a494cdb3 (patch)
treeac2fc34f5cff60e63b59fa8ed5bcd5d9f6a1d7fa
parent1f232db5b499169e3c354b4af4bb59053009f210 (diff)
downloadvdr-plugin-softhddevice-5d8dea1b6b9e15048f425f13b349e785a494cdb3.tar.gz
vdr-plugin-softhddevice-5d8dea1b6b9e15048f425f13b349e785a494cdb3.tar.bz2
New audio PES handling.
New easier and more flexible audio PES packet parser, which includes own codec parser. Removed av_parser use. Reduced audio buffer time, faster channel switch. New audio transport stream parser (not enabled as default).
-rw-r--r--ChangeLog1
-rw-r--r--Makefile9
-rw-r--r--Todo9
-rw-r--r--audio.c101
-rw-r--r--audio.h2
-rw-r--r--codec.c246
-rw-r--r--codec.h3
-rw-r--r--softhddev.c1229
-rw-r--r--softhddev.h2
-rw-r--r--softhddevice.cpp22
-rw-r--r--video.c47
-rw-r--r--video.h1
12 files changed, 1277 insertions, 395 deletions
diff --git a/ChangeLog b/ChangeLog
index a2f4d51..e689e71 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
User johns
Date:
+ New audio PES packet parser.
Fix bug: Grabbing a JPG image fails while suspended.
Add support for hot keys.
Add support to use characters input in edit mode.
diff --git a/Makefile b/Makefile
index 8cae7eb..6b78105 100644
--- a/Makefile
+++ b/Makefile
@@ -19,8 +19,9 @@ GIT_REV = $(shell git describe --always 2>/dev/null)
### Configuration (edit this for your needs)
CONFIG := #-DDEBUG
-CONFIG += -DAV_INFO
-#CONFIG += -DHAVE_PTHREAD_NAME
+CONFIG += -DAV_INFO # debug a/v sync
+#CONFIG += -DHAVE_PTHREAD_NAME # supports new pthread_setname_np
+#CONFIG += -DUSE_TS_AUDIO # build new ts audio parser
CONFIG += $(shell pkg-config --exists vdpau && echo "-DUSE_VDPAU")
CONFIG += $(shell pkg-config --exists libva && echo "-DUSE_VAAPI")
CONFIG += $(shell pkg-config --exists alsa && echo "-DUSE_ALSA")
@@ -66,7 +67,7 @@ DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \
$(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"')
_CFLAGS = $(DEFINES) $(INCLUDES) \
- $(shell pkg-config --cflags libavcodec libavformat) \
+ $(shell pkg-config --cflags libavcodec) \
`pkg-config --cflags x11 x11-xcb xcb xcb-xv xcb-shm xcb-dpms xcb-atom\
xcb-screensaver xcb-randr xcb-glx xcb-icccm xcb-keysyms`\
`pkg-config --cflags gl glu` \
@@ -82,7 +83,7 @@ override CXXFLAGS += $(_CFLAGS)
override CFLAGS += $(_CFLAGS)
LIBS += -lrt \
- $(shell pkg-config --libs libavcodec libavformat) \
+ $(shell pkg-config --libs libavcodec) \
`pkg-config --libs x11 x11-xcb xcb xcb-xv xcb-shm xcb-dpms xcb-atom\
xcb-screensaver xcb-randr xcb-glx xcb-icccm xcb-keysyms`\
`pkg-config --libs gl glu` \
diff --git a/Todo b/Todo
index 92a56a6..1847d02 100644
--- a/Todo
+++ b/Todo
@@ -26,6 +26,8 @@ missing:
Option deinterlace off / deinterlace force!
ColorSpace aren't configurable with the gui.
Inverse telecine isn't configurable with the gui.
+ Replay of old vdr 1.6 recordings.
+ svdrp support for hot-keys.
crash:
AudioPlayHandlerThread -> pthread_cond_wait
@@ -99,8 +101,11 @@ HDMI/SPDIF Passthrough:
only AC-3 written
playback of recording
- pause is not reset, when replay exit
- replay/pause need 100% cpu
+ pause is not reset, when replay exit (fixed?)
+ replay/pause need 100% cpu (fixed?)
+
+plugins:
+ mp3 plugin needs 100% cpu (OSD updates?)
setup:
Setup of decoder type.
diff --git a/audio.c b/audio.c
index 20b4c80..3922aed 100644
--- a/audio.c
+++ b/audio.c
@@ -141,7 +141,7 @@ static unsigned AudioSampleRate; ///< audio sample rate in hz
static unsigned AudioChannels; ///< number of audio channels
static const int AudioBytesProSample = 2; ///< number of bytes per sample
static int64_t AudioPTS; ///< audio pts clock
-static const int AudioBufferTime = 350; ///< audio buffer time in ms
+static int AudioBufferTime = 216; ///< audio buffer time in ms
#ifdef USE_AUDIO_THREAD
static pthread_t AudioThread; ///< audio play thread
@@ -151,7 +151,8 @@ static pthread_cond_t AudioStartCond; ///< condition variable
static const int AudioThread; ///< dummy audio thread
#endif
-extern int VideoAudioDelay; /// import audio/video delay
+extern int VideoAudioDelay; ///< import audio/video delay
+extern int VideoGetBuffers(void); ///< Get number of input buffers.
#ifdef USE_AUDIORING
@@ -297,7 +298,17 @@ static int AlsaAddToRingbuffer(const void *samples, int count)
}
if (!AudioRunning) {
- if (AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) {
+ Debug(3, "audio/alsa: start %zd ms %d\n",
+ (RingBufferUsedBytes(AlsaRingBuffer) * 1000)
+ / (AudioSampleRate * AudioChannels * AudioBytesProSample),
+ VideoGetBuffers());
+ // forced start
+ if (AlsaStartThreshold * 2 < RingBufferUsedBytes(AlsaRingBuffer)) {
+ return 1;
+ }
+ // enough video + audio buffered
+ if (VideoGetBuffers() > 1
+ && AlsaStartThreshold < RingBufferUsedBytes(AlsaRingBuffer)) {
// restart play-back
return 1;
}
@@ -654,7 +665,8 @@ static void AlsaThread(void)
}
state = snd_pcm_state(AlsaPCMHandle);
if (state != SND_PCM_STATE_RUNNING) {
- Debug(3, "audio/alsa: stopping play\n");
+ Debug(3, "audio/alsa: stopping play '%s'\n",
+ snd_pcm_state_name(state));
break;
}
pthread_yield();
@@ -720,12 +732,11 @@ static snd_pcm_t *AlsaOpenPCM(int use_ac3)
// &&|| hell
if (!(use_ac3 && ((device = AudioAC3Device)
- || (device = getenv("ALSA_AC3_DEVICE"))
- || (device = getenv("ALSA_PASSTHROUGH_DEVICE"))))
+ || (device = getenv("ALSA_AC3_DEVICE"))))
&& !(device = AudioPCMDevice) && !(device = getenv("ALSA_DEVICE"))) {
device = "default";
}
- Debug(3, "audio/alsa: &&|| hell '%s'\n", device);
+ Info(_("audio/alsa: using device '%s'\n"), device);
// open none blocking; if device is already used, we don't want wait
if ((err =
@@ -752,7 +763,8 @@ static void AlsaInitPCM(void)
snd_pcm_t *handle;
snd_pcm_hw_params_t *hw_params;
int err;
- snd_pcm_uframes_t buffer_size;
+
+ //snd_pcm_uframes_t buffer_size;
if (!(handle = AlsaOpenPCM(0))) {
return;
@@ -767,8 +779,9 @@ static void AlsaInitPCM(void)
}
AlsaCanPause = snd_pcm_hw_params_can_pause(hw_params);
Info(_("audio/alsa: supports pause: %s\n"), AlsaCanPause ? "yes" : "no");
- snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
- Info(_("audio/alsa: max buffer size %lu\n"), buffer_size);
+ // needs audio setup
+ //snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
+ //Info(_("audio/alsa: max buffer size %lu\n"), buffer_size);
AlsaPCMHandle = handle;
}
@@ -941,7 +954,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
snd_pcm_set_params(AlsaPCMHandle, SND_PCM_FORMAT_S16,
AlsaUseMmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED :
SND_PCM_ACCESS_RW_INTERLEAVED, *channels, *freq, 1,
- 125 * 1000))) {
+ 96 * 1000))) {
Error(_("audio/alsa: set params error: %s\n"), snd_strerror(err));
/*
@@ -1053,7 +1066,7 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
// FIXME: use hw_params for buffer_size period_size
#endif
-#if 1
+#if 0
if (0) { // no underruns allowed, play silence
snd_pcm_sw_params_t *sw_params;
snd_pcm_uframes_t boundary;
@@ -1091,16 +1104,21 @@ static int AlsaSetup(int *freq, int *channels, int use_ac3)
// update buffer
snd_pcm_get_params(AlsaPCMHandle, &buffer_size, &period_size);
- Info(_("audio/alsa: buffer size %lu, period size %lu\n"), buffer_size,
- period_size);
+ Info(_("audio/alsa: buffer size %lu %lums, period size %lu %lums\n"),
+ buffer_size, snd_pcm_frames_to_bytes(AlsaPCMHandle,
+ buffer_size) * 1000 / (AudioSampleRate * AudioChannels *
+ AudioBytesProSample), period_size,
+ snd_pcm_frames_to_bytes(AlsaPCMHandle,
+ period_size) * 1000 / (AudioSampleRate * AudioChannels *
+ AudioBytesProSample));
Debug(3, "audio/alsa: state %s\n",
snd_pcm_state_name(snd_pcm_state(AlsaPCMHandle)));
AlsaStartThreshold = snd_pcm_frames_to_bytes(AlsaPCMHandle, period_size);
// buffer time/delay in ms
delay = AudioBufferTime;
- if (VideoAudioDelay > -100) {
- delay += 100 + VideoAudioDelay / 90;
+ if (VideoAudioDelay > 0) {
+ delay += VideoAudioDelay / 90;
}
if (AlsaStartThreshold <
(*freq * *channels * AudioBytesProSample * delay) / 1000U) {
@@ -1284,7 +1302,17 @@ static int OssAddToRingbuffer(const void *samples, int count)
}
if (!AudioRunning) {
- if (OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) {
+ Debug(3, "audio/oss: start %zd ms %d\n",
+ (RingBufferUsedBytes(OssRingBuffer) * 1000)
+ / (AudioSampleRate * AudioChannels * AudioBytesProSample),
+ VideoGetBuffers());
+ // forced start
+ if (OssStartThreshold * 2 < RingBufferUsedBytes(OssRingBuffer)) {
+ return 1;
+ }
+ // enough video + audio buffered
+ if (VideoGetBuffers() > 1
+ && OssStartThreshold < RingBufferUsedBytes(OssRingBuffer)) {
// restart play-back
return 1;
}
@@ -1522,7 +1550,7 @@ static int OssOpenPCM(int use_ac3)
&& !(device = AudioPCMDevice) && !(device = getenv("OSS_AUDIODEV"))) {
device = "/dev/dsp";
}
- Debug(3, "audio/oss: &&|| hell '%s'\n", device);
+ Info(_("audio/oss: using device '%s'\n"), device);
if ((fildes = open(device, O_WRONLY)) < 0) {
Error(_("audio/oss: can't open dsp device '%s': %s\n"), device,
@@ -1774,8 +1802,8 @@ static int OssSetup(int *freq, int *channels, int use_ac3)
OssStartThreshold = bi.bytes + tmp;
// buffer time/delay in ms
delay = AudioBufferTime;
- if (VideoAudioDelay > -100) {
- delay += 100 + VideoAudioDelay / 90;
+ if (VideoAudioDelay > 0) {
+ delay += VideoAudioDelay / 90;
}
if (OssStartThreshold <
(*freq * *channels * AudioBytesProSample * delay) / 1000U) {
@@ -1964,6 +1992,9 @@ static void *AudioPlayHandlerThread(void *dummy)
pthread_cond_wait(&AudioStartCond, &AudioMutex);
// cond_wait can return, without signal!
} while (!AudioRunning);
+ Debug(3, "audio/alsa: ----> %zd ms\n",
+ (RingBufferUsedBytes(AlsaRingBuffer) * 1000)
+ / (AudioSampleRate * AudioChannels * AudioBytesProSample));
pthread_mutex_unlock(&AudioMutex);
#ifdef USE_AUDIORING
@@ -2064,6 +2095,21 @@ static const AudioModule *AudioModules[] = {
*/
void AudioEnqueue(const void *samples, int count)
{
+ if (0) {
+ static uint32_t last;
+ static uint32_t tick;
+ static uint32_t max = 110;
+ uint64_t delay;
+
+ delay = AudioGetDelay();
+ tick = GetMsTicks();
+ if ((last && tick - last > max) || delay < 80 * 90) {
+
+ //max = tick - last;
+ Debug(3, "audio: packet delta %d %lu\n", tick - last, delay / 90);
+ }
+ last = tick;
+ }
AudioUsedModule->Enqueue(samples, count);
}
@@ -2183,7 +2229,7 @@ int AudioSetup(int *freq, int *channels, int use_ac3)
void AudioPlay(void)
{
if (!AudioPaused) {
- Warning("audio: not paused, check the code\n");
+ Debug(3, "audio: not paused, check the code\n");
return;
}
Debug(3, "audio: resumed\n");
@@ -2197,7 +2243,7 @@ void AudioPlay(void)
void AudioPause(void)
{
if (AudioPaused) {
- Warning("audio: already paused, check the code\n");
+ Debug(3, "audio: already paused, check the code\n");
return;
}
Debug(3, "audio: paused\n");
@@ -2205,6 +2251,17 @@ void AudioPause(void)
}
/**
+** Set audio buffer time.
+*/
+void AudioSetBufferTime(int delay)
+{
+ if (!delay) {
+ delay = 216;
+ }
+ AudioBufferTime = delay;
+}
+
+/**
** Set pcm audio device.
**
** @param device name of pcm device (fe. "hw:0,9" or "/dev/dsp")
diff --git a/audio.h b/audio.h
index 677fbcf..9163f72 100644
--- a/audio.h
+++ b/audio.h
@@ -42,6 +42,8 @@ extern int AudioSetup(int *, int *, int); ///< setup audio output
extern void AudioPlay(void); ///< play audio
extern void AudioPause(void); ///< pause audio
+extern void AudioSetBufferTime(int); ///< set audio buffer time
+
extern void AudioSetDevice(const char *); ///< set PCM audio device
extern void AudioSetDeviceAC3(const char *); ///< set pass-through device
extern void AudioSetChannel(const char *); ///< set mixer channel
diff --git a/codec.c b/codec.c
index 1c65afb..8a23207 100644
--- a/codec.c
+++ b/codec.c
@@ -33,7 +33,7 @@
/**
** use av_parser to support insane dvb audio streams.
*/
-#define USE_AVPARSER
+#define noUSE_AVPARSER
/// compile with passthrough support (experimental)
#define USE_PASSTHROUGH
@@ -603,8 +603,10 @@ struct _audio_decoder_
AVCodec *AudioCodec; ///< audio codec
AVCodecContext *AudioCtx; ///< audio codec context
+#ifdef USE_AVPARSER
/// audio parser to support insane dvb streaks
AVCodecParserContext *AudioParser;
+#endif
int PassthroughAC3; ///< current ac-3 pass-through
int SampleRate; ///< current stream sample rate
int Channels; ///< current stream channels
@@ -697,10 +699,12 @@ void CodecAudioOpen(AudioDecoder * audio_decoder, const char *name,
// we do not send complete frames
audio_decoder->AudioCtx->flags |= CODEC_FLAG_TRUNCATED;
}
+#ifdef USE_AVPARSER
if (!(audio_decoder->AudioParser =
av_parser_init(audio_decoder->AudioCtx->codec_id))) {
Fatal(_("codec: can't init audio parser\n"));
}
+#endif
audio_decoder->SampleRate = 0;
audio_decoder->Channels = 0;
audio_decoder->HwSampleRate = 0;
@@ -719,10 +723,12 @@ void CodecAudioClose(AudioDecoder * audio_decoder)
audio_resample_close(audio_decoder->ReSample);
audio_decoder->ReSample = NULL;
}
+#ifdef USE_AVPARSER
if (audio_decoder->AudioParser) {
av_parser_close(audio_decoder->AudioParser);
audio_decoder->AudioParser = NULL;
}
+#endif
if (audio_decoder->AudioCtx) {
pthread_mutex_lock(&CodecLockMutex);
avcodec_close(audio_decoder->AudioCtx);
@@ -808,7 +814,7 @@ static void CodecReorderAudioFrame(int16_t * buf, int size, int channels)
** @param audio_decoder audio decoder data
** @param avpkt audio packet
*/
-void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
+void CodecAudioDecodeOld(AudioDecoder * audio_decoder, const AVPacket * avpkt)
{
int16_t buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
FF_INPUT_BUFFER_PADDING_SIZE] __attribute__ ((aligned(16)));
@@ -844,8 +850,8 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
av_init_packet(dpkt);
n = av_parser_parse2(audio_decoder->AudioParser, audio_ctx,
&dpkt->data, &dpkt->size, spkt->data + index, spkt->size - index,
- !index ? (uint64_t) spkt->pts : AV_NOPTS_VALUE,
- !index ? (uint64_t) spkt->dts : AV_NOPTS_VALUE, -1);
+ !index ? spkt->pts : (int64_t) AV_NOPTS_VALUE,
+ !index ? spkt->dts : (int64_t) AV_NOPTS_VALUE, -1);
// FIXME: make this a function for both #ifdef cases
if (dpkt->size) {
@@ -871,7 +877,7 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
#else
#endif
// Update audio clock
- if ((uint64_t) dpkt->pts != AV_NOPTS_VALUE) {
+ if (dpkt->pts != (int64_t) AV_NOPTS_VALUE) {
AudioSetClock(dpkt->pts);
}
// FIXME: must first play remainings bytes, than change and play new.
@@ -1059,6 +1065,8 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
#else
+#endif
+
/**
** Decode an audio packet.
**
@@ -1074,61 +1082,205 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
AVCodecContext *audio_ctx;
int index;
-//#define spkt avpkt
-#if 1
- AVPacket spkt[1];
-
- // av_new_packet reserves FF_INPUT_BUFFER_PADDING_SIZE and clears it
- if (av_new_packet(spkt, avpkt->size)) {
- Error(_("codec: out of memory\n"));
- return;
- }
- memcpy(spkt->data, avpkt->data, avpkt->size);
- spkt->pts = avpkt->pts;
- spkt->dts = avpkt->dts;
-#endif
audio_ctx = audio_decoder->AudioCtx;
index = 0;
- while (spkt->size > index) {
- int n;
+ while (avpkt->size > index) {
+ int l;
int buf_sz;
- AVPacket dpkt[1];
-
- av_init_packet(dpkt);
- dpkt->data = spkt->data + index;
- dpkt->size = spkt->size - index;
buf_sz = sizeof(buf);
- n = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, dpkt);
- if (n < 0) { // no audio frame could be decompressed
- Error(_("codec: error audio data at %d\n"), index);
+ l = avcodec_decode_audio3(audio_ctx, buf, &buf_sz, (AVPacket *)avpkt);
+ if (l == AVERROR(EAGAIN)) {
+ Error(_("codec: latm\n"));
break;
}
-#ifdef DEBUG
- Debug(4, "codec/audio: -> %d\n", buf_sz);
- if ((unsigned)buf_sz > sizeof(buf)) {
- abort();
+ if (l < 0) { // no audio frame could be decompressed
+ Error(_("codec: error audio data at %d\n"), index);
+ break;
}
-#endif
#ifdef notyetFF_API_OLD_DECODE_AUDIO
// FIXME: ffmpeg git comeing
int got_frame;
- avcodec_decode_audio4(audio_ctx, frame, &got_frame, dpkt);
+ avcodec_decode_audio4(audio_ctx, frame, &got_frame, avpkt);
#else
#endif
- // FIXME: see above, old code removed
+ // Update audio clock
+ if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
+ AudioSetClock(avpkt->pts);
+ }
+ // FIXME: must first play remainings bytes, than change and play new.
+ if (audio_decoder->PassthroughAC3 != CodecPassthroughAC3
+ || audio_decoder->SampleRate != audio_ctx->sample_rate
+ || audio_decoder->Channels != audio_ctx->channels) {
+ int err;
+ int isAC3;
+
+ audio_decoder->PassthroughAC3 = CodecPassthroughAC3;
+ // FIXME: use swr_convert from swresample (only in ffmpeg!)
+ // FIXME: tell ac3 decoder to use downmix
+ if (audio_decoder->ReSample) {
+ audio_resample_close(audio_decoder->ReSample);
+ audio_decoder->ReSample = NULL;
+ }
- index += n;
- }
+ audio_decoder->SampleRate = audio_ctx->sample_rate;
+ audio_decoder->HwSampleRate = audio_ctx->sample_rate;
+ audio_decoder->Channels = audio_ctx->channels;
+ // SPDIF/HDMI passthrough
+ if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
+ audio_decoder->HwChannels = 2;
+ isAC3 = 1;
+ } else {
+ audio_decoder->HwChannels = audio_ctx->channels;
+ isAC3 = 0;
+ }
-#if 1
- // or av_free_packet, make no difference here
- av_destruct_packet(spkt);
-#endif
-}
+ // channels not support?
+ if ((err =
+ AudioSetup(&audio_decoder->HwSampleRate,
+ &audio_decoder->HwChannels, isAC3))) {
+ Debug(3, "codec/audio: resample %dHz *%d -> %dHz *%d\n",
+ audio_ctx->sample_rate, audio_ctx->channels,
+ audio_decoder->HwSampleRate, audio_decoder->HwChannels);
+
+ if (err == 1) {
+ audio_decoder->ReSample =
+ av_audio_resample_init(audio_decoder->HwChannels,
+ audio_ctx->channels, audio_decoder->HwSampleRate,
+ audio_ctx->sample_rate, audio_ctx->sample_fmt,
+ audio_ctx->sample_fmt, 16, 10, 0, 0.8);
+ // libav-0.8_pre didn't support 6 -> 2 channels
+ if (!audio_decoder->ReSample) {
+ Error(_("codec/audio: resample setup error\n"));
+ audio_decoder->HwChannels = 0;
+ audio_decoder->HwSampleRate = 0;
+ }
+ } else {
+ Debug(3, "codec/audio: audio setup error\n");
+ // FIXME: handle errors
+ audio_decoder->HwChannels = 0;
+ audio_decoder->HwSampleRate = 0;
+ break;
+ }
+ }
+ }
+ if (audio_decoder->HwSampleRate && audio_decoder->HwChannels) {
+ // need to resample audio
+ if (audio_decoder->ReSample) {
+ int16_t outbuf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 4 +
+ FF_INPUT_BUFFER_PADDING_SIZE]
+ __attribute__ ((aligned(16)));
+ int outlen;
+
+ // FIXME: libav-0.7.2 crash here
+ outlen =
+ audio_resample(audio_decoder->ReSample, outbuf, buf,
+ buf_sz);
+#ifdef DEBUG
+ if (outlen != buf_sz) {
+ Debug(3, "codec/audio: possible fixed ffmpeg\n");
+ }
+#endif
+ if (outlen) {
+ // outlen seems to be wrong in ffmpeg-0.9
+ outlen /= audio_decoder->Channels *
+ av_get_bytes_per_sample(audio_ctx->sample_fmt);
+ outlen *=
+ audio_decoder->HwChannels *
+ av_get_bytes_per_sample(audio_ctx->sample_fmt);
+ Debug(4, "codec/audio: %d -> %d\n", buf_sz, outlen);
+ CodecReorderAudioFrame(outbuf, outlen,
+ audio_decoder->HwChannels);
+ AudioEnqueue(outbuf, outlen);
+ }
+ } else {
+#ifdef USE_PASSTHROUGH
+ // SPDIF/HDMI passthrough
+ if (CodecPassthroughAC3 && audio_ctx->codec_id == CODEC_ID_AC3) {
+ // build SPDIF header and append A52 audio to it
+ // avpkt is the original data
+ buf_sz = 6144;
+ if (buf_sz < avpkt->size + 8) {
+ Error(_
+ ("codec/audio: decoded data smaller than encoded\n"));
+ break;
+ }
+ // copy original data for output
+ // FIXME: not 100% sure, if endian is correct
+ buf[0] = htole16(0xF872); // iec 61937 sync word
+ buf[1] = htole16(0x4E1F);
+ buf[2] = htole16(0x01 | (avpkt->data[5] & 0x07) << 8);
+ buf[3] = htole16(avpkt->size * 8);
+ swab(avpkt->data, buf + 4, avpkt->size);
+ memset(buf + 4 + avpkt->size / 2, 0,
+ buf_sz - 8 - avpkt->size);
+ }
+#if 0
+ //
+ // old experimental code
+ //
+ if (1) {
+ // FIXME: need to detect dts
+ // copy original data for output
+ // FIXME: buf is sint
+ buf[0] = 0x72;
+ buf[1] = 0xF8;
+ buf[2] = 0x1F;
+ buf[3] = 0x4E;
+ buf[4] = 0x00;
+ switch (avpkt->size) {
+ case 512:
+ buf[5] = 0x0B;
+ break;
+ case 1024:
+ buf[5] = 0x0C;
+ break;
+ case 2048:
+ buf[5] = 0x0D;
+ break;
+ default:
+ Debug(3,
+ "codec/audio: dts sample burst not supported\n");
+ buf[5] = 0x00;
+ break;
+ }
+ buf[6] = (avpkt->size * 8);
+ buf[7] = (avpkt->size * 8) >> 8;
+ //buf[8] = 0x0B;
+ //buf[9] = 0x77;
+ //printf("%x %x\n", avpkt->data[0],avpkt->data[1]);
+ // swab?
+ memcpy(buf + 8, avpkt->data, avpkt->size);
+ memset(buf + 8 + avpkt->size, 0, buf_sz - 8 - avpkt->size);
+ } else if (1) {
+ // FIXME: need to detect mp2
+ // FIXME: mp2 passthrough
+ // see softhddev.c version/layer
+ // 0x04 mpeg1 layer1
+ // 0x05 mpeg1 layer23
+ // 0x06 mpeg2 ext
+ // 0x07 mpeg2.5 layer 1
+ // 0x08 mpeg2.5 layer 2
+ // 0x09 mpeg2.5 layer 3
+ }
+ // DTS HD?
+ // True HD?
#endif
+#endif
+ CodecReorderAudioFrame(buf, buf_sz, audio_decoder->HwChannels);
+ AudioEnqueue(buf, buf_sz);
+ }
+ }
+
+ if (avpkt->size > l) {
+ Error(_("codec: error more than one frame data\n"));
+ }
+
+ index += l;
+ }
+}
/**
** Flush the audio decoder.
@@ -1137,7 +1289,17 @@ void CodecAudioDecode(AudioDecoder * audio_decoder, const AVPacket * avpkt)
*/
void CodecAudioFlushBuffers(AudioDecoder * decoder)
{
+#ifdef USE_AVPARSER
// FIXME: reset audio parser
+ if (decoder->AudioParser) {
+ av_parser_close(decoder->AudioParser);
+ decoder->AudioParser = NULL;
+ if (!(decoder->AudioParser =
+ av_parser_init(decoder->AudioCtx->codec_id))) {
+ Fatal(_("codec: can't init audio parser\n"));
+ }
+ }
+#endif
avcodec_flush_buffers(decoder->AudioCtx);
}
diff --git a/codec.h b/codec.h
index 32049fe..fbc32ec 100644
--- a/codec.h
+++ b/codec.h
@@ -68,6 +68,9 @@ extern void CodecAudioOpen(AudioDecoder *, const char *, int);
extern void CodecAudioClose(AudioDecoder *);
/// Decode an audio packet.
+extern void CodecAudioDecodeOld(AudioDecoder *, const AVPacket *);
+
+ /// Decode an audio packet.
extern void CodecAudioDecode(AudioDecoder *, const AVPacket *);
/// Flush audio buffers.
diff --git a/softhddev.c b/softhddev.c
index f33d739..3ea5e1c 100644
--- a/softhddev.c
+++ b/softhddev.c
@@ -71,16 +71,22 @@ static volatile char StreamFreezed; ///< stream freezed
static volatile char NewAudioStream; ///< new audio stream
static volatile char SkipAudio; ///< skip audio stream
-static char AudioRawAc3; ///< flag raw ac3 stream
static AudioDecoder *MyAudioDecoder; ///< audio decoder
static enum CodecID AudioCodecID; ///< current codec id
static int AudioChannelID; ///< current audio channel id
-/**
-** mpeg bitrate table.
-**
-** BitRateTable[Version][Layer][Index]
-*/
+#define AUDIO_BUFFER_SIZE (512 * 1024) ///< audio PES buffer default size
+static AVPacket AudioAvPkt[1]; ///< audio a/v packet
+
+//////////////////////////////////////////////////////////////////////////////
+// Audio codec parser
+//////////////////////////////////////////////////////////////////////////////
+
+///
+/// Mpeg bitrate table.
+///
+/// BitRateTable[Version][Layer][Index]
+///
static const uint16_t BitRateTable[2][4][16] = {
// MPEG Version 1
{{},
@@ -96,109 +102,187 @@ static const uint16_t BitRateTable[2][4][16] = {
}
};
-/**
-** mpeg samperate table.
-*/
+///
+/// Mpeg samperate table.
+///
static const uint16_t SampleRateTable[4] = {
44100, 48000, 32000, 0
};
-/**
-** Find sync in audio packet.
-**
-** @param avpkt audio packet
-**
-** From: http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
-**
-** AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
-**
-** o a 11x frame sync
-** o b 2x mpeg audio version (2.5, reserved, 2, 1)
-** o c 2x layer (reserved, III, II, I)
-** o e 2x BitRate index
-** o f 2x SampleRate index
-** o g 1x Paddding bit
-** o .. doesn't care
-**
-** frame length:
-** Layer I:
-** FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
-** Layer II & III:
-** FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
-**
-** @todo sometimes detects wrong position
-*/
-static int FindAudioSync(const AVPacket * avpkt)
+///
+/// Fast check for Mpeg audio.
+///
+/// 4 bytes 0xFFExxxxx Mpeg audio
+///
+static inline int FastMpegCheck(const uint8_t * p)
{
- int i;
- const uint8_t *data;
-
- i = 0;
- data = avpkt->data;
- while (i < avpkt->size - 4) {
- if (data[i] == 0xFF && (data[i + 1] & 0xFC) == 0xFC) {
- int mpeg2;
- int mpeg25;
- int layer;
- int bit_rate_index;
- int sample_rate_index;
- int padding;
- int bit_rate;
- int sample_rate;
- int frame_size;
-
- mpeg2 = !(data[i + 1] & 0x08) && (data[i + 1] & 0x10);
- mpeg25 = !(data[i + 1] & 0x08) && !(data[i + 1] & 0x10);
- layer = 4 - ((data[i + 1] >> 1) & 0x03);
- bit_rate_index = (data[i + 2] >> 4) & 0x0F;
- sample_rate_index = (data[i + 2] >> 2) & 0x03;
- padding = (data[i + 2] >> 1) & 0x01;
-
- sample_rate = SampleRateTable[sample_rate_index];
- if (!sample_rate) { // no valid sample rate try next
- i++;
- continue;
- }
- sample_rate >>= mpeg2; // mpeg 2 half rate
- sample_rate >>= mpeg25; // mpeg 2.5 quarter rate
-
- bit_rate = BitRateTable[mpeg2 | mpeg25][layer][bit_rate_index];
- bit_rate *= 1000;
- switch (layer) {
- case 1:
- frame_size = (12 * bit_rate) / sample_rate;
- frame_size = (frame_size + padding) * 4;
- break;
- case 2:
- case 3:
- default:
- frame_size = (144 * bit_rate) / sample_rate;
- frame_size = frame_size + padding;
- break;
- }
- Debug(3,
- "audio: mpeg%s layer%d bitrate=%d samplerate=%d %d bytes\n",
- mpeg25 ? "2.5" : mpeg2 ? "2" : "1", layer, bit_rate,
- sample_rate, frame_size);
- // check if after this frame a new mpeg frame starts
- if (i + frame_size < avpkt->size - 3
- && data[i + frame_size] == 0xFF
- && (data[i + frame_size + 1] & 0xFC) == 0xFC) {
- Debug(3, "audio: mpeg1/2 found at %d\n", i);
- return i;
- }
- // no valid frame size or no continuation, try next
- }
- i++;
+ if (p[0] != 0xFF) { // 11bit frame sync
+ return 0;
+ }
+ if ((p[1] & 0xE0) != 0xE0) {
+ return 0;
+ }
+ if ((p[1] & 0x18) == 0x08) { // version ID - 01 reserved
+ return 0;
+ }
+ if (!(p[1] & 0x06)) { // layer description - 00 reserved
+ return 0;
+ }
+ if ((p[2] & 0xF0) == 0xF0) { // bitrate index - 1111 reserved
+ return 0;
+ }
+ if ((p[2] & 0x0C) == 0x0C) { // sampling rate index - 11 reserved
+ return 0;
}
- return -1;
+ return 1;
}
-/**
-** Possible AC3 frame sizes.
-**
-** from ATSC A/52 table 5.18 frame size code table.
-*/
+///
+/// Check for Mpeg audio.
+///
+/// 0xFFEx already checked.
+///
+/// @param data incomplete PES packet
+/// @param size number of bytes
+///
+/// @retval <0 possible mpeg audio, but need more data
+/// @retval 0 no valid mpeg audio
+/// @retval >0 valid mpeg audio
+///
+/// From: http://www.mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
+///
+/// AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
+///
+/// o a 11x Frame sync
+/// o b 2x Mpeg audio version (2.5, reserved, 2, 1)
+/// o c 2x Layer (reserved, III, II, I)
+/// o e 2x BitRate index
+/// o f 2x SampleRate index (4100, 48000, 32000, 0)
+/// o g 1x Paddding bit
+/// o .. Doesn't care
+///
+/// frame length:
+/// Layer I:
+/// FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4
+/// Layer II & III:
+/// FrameLengthInBytes = 144 * BitRate / SampleRate + Padding
+///
+static int MpegCheck(const uint8_t * data, int size)
+{
+ int mpeg2;
+ int mpeg25;
+ int layer;
+ int bit_rate_index;
+ int sample_rate_index;
+ int padding;
+ int bit_rate;
+ int sample_rate;
+ int frame_size;
+
+ mpeg2 = !(data[1] & 0x08) && (data[1] & 0x10);
+ mpeg25 = !(data[1] & 0x08) && !(data[1] & 0x10);
+ layer = 4 - ((data[1] >> 1) & 0x03);
+ bit_rate_index = (data[2] >> 4) & 0x0F;
+ sample_rate_index = (data[2] >> 2) & 0x03;
+ padding = (data[2] >> 1) & 0x01;
+
+ sample_rate = SampleRateTable[sample_rate_index];
+ if (!sample_rate) { // no valid sample rate try next
+ // moved into fast check
+ abort();
+ return 0;
+ }
+ sample_rate >>= mpeg2; // mpeg 2 half rate
+ sample_rate >>= mpeg25; // mpeg 2.5 quarter rate
+
+ bit_rate = BitRateTable[mpeg2 | mpeg25][layer][bit_rate_index];
+ if (!bit_rate) { // no valid bit-rate try next
+ // FIXME: move into fast check?
+ return 0;
+ }
+ bit_rate *= 1000;
+ switch (layer) {
+ case 1:
+ frame_size = (12 * bit_rate) / sample_rate;
+ frame_size = (frame_size + padding) * 4;
+ break;
+ case 2:
+ case 3:
+ default:
+ frame_size = (144 * bit_rate) / sample_rate;
+ frame_size = frame_size + padding;
+ break;
+ }
+ if (0) {
+ Debug(3,
+ "pesdemux: mpeg%s layer%d bitrate=%d samplerate=%d %d bytes\n",
+ mpeg25 ? "2.5" : mpeg2 ? "2" : "1", layer, bit_rate, sample_rate,
+ frame_size);
+ }
+
+ if (frame_size + 4 > size) {
+ return -frame_size - 4;
+ }
+ // check if after this frame a new mpeg frame starts
+ if (FastMpegCheck(data + frame_size)) {
+ return frame_size;
+ }
+
+ return 0;
+}
+
+///
+/// Fast check for AAC LATM audio.
+///
+/// 3 bytes 0x56Exxx AAC LATM audio
+///
+static inline int FastLatmCheck(const uint8_t * p)
+{
+ if (p[0] != 0x56) { // 11bit sync
+ return 0;
+ }
+ if ((p[1] & 0xE0) != 0xE0) {
+ return 0;
+ }
+ return 1;
+}
+
+///
+/// Check for AAC LATM audio.
+///
+/// 0x56Exxx already checked.
+///
+/// @param data incomplete PES packet
+/// @param size number of bytes
+///
+/// @retval <0 possible AAC LATM audio, but need more data
+/// @retval 0 no valid AAC LATM audio
+/// @retval >0 valid AAC LATM audio
+///
+static int LatmCheck(const uint8_t * data, int size)
+{
+ int frame_size;
+
+ // 13 bit frame size without header
+ frame_size = ((data[1] & 0x1F) << 8) + data[2];
+ frame_size += 3;
+
+ if (frame_size + 2 > size) {
+ return -frame_size - 2;
+ }
+ // check if after this frame a new AAC LATM frame starts
+ if (FastLatmCheck(data + frame_size)) {
+ return frame_size;
+ }
+
+ return 0;
+}
+
+///
+/// Possible AC3 frame sizes.
+///
+/// from ATSC A/52 table 5.18 frame size code table.
+///
const uint16_t Ac3FrameSizeTable[38][3] = {
{64, 69, 96}, {64, 70, 96}, {80, 87, 120}, {80, 88, 120},
{96, 104, 144}, {96, 105, 144}, {112, 121, 168}, {112, 122, 168},
@@ -212,43 +296,544 @@ const uint16_t Ac3FrameSizeTable[38][3] = {
{1152, 1254, 1728}, {1280, 1393, 1920}, {1280, 1394, 1920},
};
-/**
-** Find dolby sync in audio packet.
-**
-** @param avpkt audio packet
-*/
-static int FindDolbySync(const AVPacket * avpkt)
+///
+/// Fast check for AC3 audio.
+///
+/// 5 bytes 0x0B77xxxxxx AC3 audio
+///
+static inline int FastAc3Check(const uint8_t * p)
{
- int i;
- const uint8_t *data;
-
- i = 0;
- data = avpkt->data;
- while (i < avpkt->size - 5) {
- if (data[i] == 0x0B && data[i + 1] == 0x77) {
- int fscod;
- int frmsizcod;
- int frame_size;
-
- // crc1 crc1 fscod|frmsizcod
- fscod = data[i + 4] >> 6;
- frmsizcod = data[i + 4] & 0x3F;
- frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2;
-
- // check if after this frame a new ac-3 frame starts
- if (i + frame_size < avpkt->size - 5
- && data[i + frame_size] == 0x0B
- && data[i + frame_size + 1] == 0x77) {
- Debug(3, "audio: ac-3 found at %d\n", i);
- return i;
- }
- // no valid frame size or no continuation, try next
+ if (p[0] != 0x0B) { // 16bit sync
+ return 0;
+ }
+ if (p[1] != 0x77) {
+ return 0;
+ }
+ return 1;
+}
+
+///
+/// Check for AC-3 audio.
+///
+/// 0x0B77xxxxxx already checked.
+///
+/// @param data incomplete PES packet
+/// @param size number of bytes
+///
+/// @retval <0 possible AC-3 audio, but need more data
+/// @retval 0 no valid AC-3 audio
+/// @retval >0 valid AC-3 audio
+///
+static int Ac3Check(const uint8_t * data, int size)
+{
+ int fscod;
+ int frmsizcod;
+ int frame_size;
+
+ // crc1 crc1 fscod|frmsizcod
+ fscod = data[4] >> 6;
+ frmsizcod = data[4] & 0x3F;
+ frame_size = Ac3FrameSizeTable[frmsizcod][fscod] * 2;
+
+ if (frame_size + 2 > size) {
+ return -frame_size - 2;
+ }
+ // check if after this frame a new AC-3 frame starts
+ if (FastAc3Check(data + frame_size)) {
+ return frame_size;
+ }
+
+ return 0;
+}
+
+#ifdef USE_TS_AUDIO
+
+//////////////////////////////////////////////////////////////////////////////
+// PES Demux
+//////////////////////////////////////////////////////////////////////////////
+
+///
+/// PES type.
+///
+enum
+{
+ PES_PROG_STREAM_MAP = 0xBC,
+ PES_PRIVATE_STREAM1 = 0xBD,
+ PES_PADDING_STREAM = 0xBE, ///< filler, padding stream
+ PES_PRIVATE_STREAM2 = 0xBF,
+ PES_AUDIO_STREAM_S = 0xC0,
+ PES_AUDIO_STREAM_E = 0xDF,
+ PES_VIDEO_STREAM_S = 0xE0,
+ PES_VIDEO_STREAM_E = 0xEF,
+ PES_ECM_STREAM = 0xF0,
+ PES_EMM_STREAM = 0xF1,
+ PES_DSM_CC_STREAM = 0xF2,
+ PES_ISO13522_STREAM = 0xF3,
+ PES_TYPE_E_STREAM = 0xF8, ///< ITU-T rec. h.222.1 type E stream
+ PES_PROG_STREAM_DIR = 0xFF,
+};
+
+///
+/// PES parser state.
+///
+enum
+{
+ PES_INIT, ///< unknown codec
+
+ PES_SKIP, ///< skip packet
+ PES_SYNC, ///< search packet sync byte
+ PES_HEADER, ///< copy header
+ PES_START, ///< pes packet start found
+ PES_PAYLOAD, ///< copy payload
+
+ PES_LPCM_HEADER, ///< copy lcpm header
+ PES_LPCM_PAYLOAD, ///< copy lcpm payload
+};
+
+#define PES_START_CODE_SIZE 6 ///< size of pes start code with length
+#define PES_HEADER_SIZE 9 ///< size of pes header
+#define PES_MAX_HEADER_SIZE (PES_HEADER_SIZE + 256) ///< maximal header size
+#define PES_MAX_PAYLOAD (512 * 1024) ///< max pay load size
+
+///
+/// PES demuxer.
+///
+typedef struct _pes_demux_
+{
+ //int Pid; ///< packet id
+ //int PcrPid; ///< program clock reference pid
+ //int StreamType; ///< stream type
+
+ int State; ///< parsing state
+ uint8_t Header[PES_MAX_HEADER_SIZE]; ///< buffer for pes header
+ int HeaderIndex; ///< header index
+ int HeaderSize; ///< size of pes header
+ uint8_t *Buffer; ///< payload buffer
+ int Index; ///< buffer index
+ int Skip; ///< buffer skip
+ int Size; ///< size of payload buffer
+
+ uint8_t StartCode; ///< pes packet start code
+
+ int64_t PTS; ///< presentation time stamp
+ int64_t DTS; ///< decode time stamp
+} PesDemux;
+
+///
+/// Initialize a packetized elementary stream demuxer.
+///
+/// @param pesdx packetized elementary stream demuxer
+///
+void PesInit(PesDemux * pesdx)
+{
+ memset(pesdx, 0, sizeof(*pesdx));
+ pesdx->Size = PES_MAX_PAYLOAD;
+ pesdx->Buffer = av_malloc(PES_MAX_PAYLOAD + FF_INPUT_BUFFER_PADDING_SIZE);
+ if (!pesdx->Buffer) {
+ Fatal(_("pesdemux: out of memory\n"));
+ }
+ pesdx->PTS = AV_NOPTS_VALUE; // reset
+ pesdx->DTS = AV_NOPTS_VALUE;
+}
+
+///
+/// Reset packetized elementary stream demuxer.
+///
+void PesReset(PesDemux * pesdx)
+{
+ pesdx->State = PES_INIT;
+ pesdx->Skip = 0;
+ pesdx->PTS = AV_NOPTS_VALUE;
+ pesdx->DTS = AV_NOPTS_VALUE;
+}
+
+///
+/// Parse packetized elementary stream.
+///
+/// @param pesdx packetized elementary stream demuxer
+/// @param data payload data of transport stream
+/// @param size number of payload data bytes
+/// @param is_start flag, start of pes packet
+///
+void PesParse(PesDemux * pesdx, const uint8_t * data, int size, int is_start)
+{
+ const uint8_t *p;
+ const uint8_t *q;
+
+ if (is_start) { // start of pes packet
+ if (pesdx->Index && pesdx->Skip) {
+ // copy remaining bytes down
+ pesdx->Index -= pesdx->Skip;
+ memmove(pesdx->Buffer, pesdx->Buffer + pesdx->Skip, pesdx->Index);
+ pesdx->Skip = 0;
}
- i++;
+ pesdx->State = PES_SYNC;
+ pesdx->HeaderIndex = 0;
+ pesdx->PTS = AV_NOPTS_VALUE; // reset if not yet used
+ pesdx->DTS = AV_NOPTS_VALUE;
}
- return -1;
+
+ p = data;
+ do {
+ int n;
+
+ switch (pesdx->State) {
+ case PES_SKIP: // skip this packet
+ return;
+
+ case PES_START: // at start of pes packet payload
+ // FIXME: need 0x80 -- 0xA0 state
+ if (AudioCodecID == CODEC_ID_NONE) {
+ if ((*p & 0xF0) == 0x80) { // AC-3 & DTS
+ Debug(3, "pesdemux: dvd ac-3\n");
+ } else if ((*p & 0xFF) == 0xA0) { // LPCM
+ Debug(3, "pesdemux: dvd lpcm\n");
+ pesdx->State = PES_LPCM_HEADER;
+ pesdx->HeaderIndex = 0;
+ pesdx->HeaderSize = 7;
+ break;
+ }
+ }
+
+ case PES_INIT: // find start of audio packet
+ // FIXME: increase if needed the buffer
+
+ // fill buffer
+ n = pesdx->Size - pesdx->Index;
+ if (n > size) {
+ n = size;
+ }
+ memcpy(pesdx->Buffer + pesdx->Index, p, n);
+ pesdx->Index += n;
+ p += n;
+ size -= n;
+
+ q = pesdx->Buffer + pesdx->Skip;
+ n = pesdx->Index - pesdx->Skip;
+ while (n >= 5) {
+ int r;
+ unsigned codec_id;
+
+ // 4 bytes 0xFFExxxxx Mpeg audio
+ // 3 bytes 0x56Exxx AAC LATM audio
+ // 5 bytes 0x0B77xxxxxx AC3 audio
+ // PCM audio can't be found
+ r = 0;
+ if (FastMpegCheck(q)) {
+ r = MpegCheck(q, n);
+ codec_id = CODEC_ID_MP2;
+ }
+ if (!r && FastAc3Check(q)) {
+ r = Ac3Check(q, n);
+ codec_id = CODEC_ID_AC3;
+ }
+ if (!r && FastLatmCheck(q)) {
+ r = LatmCheck(q, n);
+ codec_id = CODEC_ID_AAC_LATM;
+ }
+ if (r < 0) { // need more bytes
+ break;
+ }
+ if (r > 0) {
+ AVPacket avpkt[1];
+
+ // new codec id, close and open new
+ if (AudioCodecID != codec_id) {
+ CodecAudioClose(MyAudioDecoder);
+ CodecAudioOpen(MyAudioDecoder, NULL, codec_id);
+ AudioCodecID = codec_id;
+ }
+ av_init_packet(avpkt);
+ avpkt->data = (void *)q;
+ avpkt->size = r;
+ avpkt->pts = pesdx->PTS;
+ avpkt->dts = pesdx->DTS;
+ CodecAudioDecode(MyAudioDecoder, avpkt);
+ pesdx->PTS = AV_NOPTS_VALUE;
+ pesdx->DTS = AV_NOPTS_VALUE;
+ pesdx->Skip += r;
+ // FIXME: switch to decoder state
+ //pesdx->State = PES_MPEG_DECODE;
+ break;
+ }
+ // try next byte
+ ++pesdx->Skip;
+ ++q;
+ --n;
+ }
+ break;
+
+ case PES_SYNC: // wait for pes sync
+ n = PES_START_CODE_SIZE - pesdx->HeaderIndex;
+ if (n > size) {
+ n = size;
+ }
+ memcpy(pesdx->Header + pesdx->HeaderIndex, p, n);
+ pesdx->HeaderIndex += n;
+ p += n;
+ size -= n;
+
+ // have complete packet start code
+ if (pesdx->HeaderIndex >= PES_START_CODE_SIZE) {
+ unsigned code;
+
+ // bad mpeg pes packet start code prefix 0x00001xx
+ if (pesdx->Header[0] || pesdx->Header[1]
+ || pesdx->Header[2] != 0x01) {
+ Debug(3, "pesdemux: bad pes packet\n");
+ pesdx->State = PES_SKIP;
+ return;
+ }
+ code = pesdx->Header[3];
+ if (code != pesdx->StartCode) {
+ Debug(3, "pesdemux: pes start code id %#02x\n", code);
+ // FIXME: need to save start code id?
+ pesdx->StartCode = code;
+ }
+
+ pesdx->State = PES_HEADER;
+ pesdx->HeaderSize = PES_HEADER_SIZE;
+ }
+ break;
+
+ case PES_HEADER: // parse PES header
+ n = pesdx->HeaderSize - pesdx->HeaderIndex;
+ if (n > size) {
+ n = size;
+ }
+ memcpy(pesdx->Header + pesdx->HeaderIndex, p, n);
+ pesdx->HeaderIndex += n;
+ p += n;
+ size -= n;
+
+ // have header upto size bits
+ if (pesdx->HeaderIndex == PES_HEADER_SIZE) {
+ if ((pesdx->Header[6] & 0xC0) == 0x80) {
+ pesdx->HeaderSize += pesdx->Header[8];
+ } else {
+ Error(_("pesdemux: mpeg1 pes packet unsupported\n"));
+ pesdx->State = PES_SKIP;
+ return;
+ }
+ // have complete header
+ } else if (pesdx->HeaderIndex == pesdx->HeaderSize) {
+ int64_t pts;
+ int64_t dts;
+
+ if ((pesdx->Header[7] & 0xC0) == 0x80) {
+ pts =
+ (int64_t) (data[9] & 0x0E) << 29 | data[10] << 22 |
+ (data[11] & 0xFE) << 14 | data[12] << 7 | (data[13]
+ & 0xFE) >> 1;
+ Debug(4, "pesdemux: pts %#012" PRIx64 "\n", pts);
+ pesdx->PTS = pts;
+ } else if ((pesdx->Header[7] & 0xC0) == 0xC0) {
+ pts =
+ (int64_t) (data[9] & 0x0E) << 29 | data[10] << 22 |
+ (data[11] & 0xFE) << 14 | data[12] << 7 | (data[13]
+ & 0xFE) >> 1;
+ pesdx->PTS = pts;
+ dts =
+ (int64_t) (data[14] & 0x0E) << 29 | data[15] << 22
+ | (data[16] & 0xFE) << 14 | data[17] << 7 |
+ (data[18] & 0xFE) >> 1;
+ pesdx->DTS = dts;
+ Debug(4,
+ "pesdemux: pts %#012" PRIx64 " %#012" PRIx64 "\n",
+ pts, dts);
+ }
+
+ pesdx->State = PES_INIT;
+ if (pesdx->StartCode == PES_PRIVATE_STREAM1) {
+ // only private stream 1, has sub streams
+ pesdx->State = PES_START;
+ }
+ //pesdx->HeaderIndex = 0;
+ //pesdx->Index = 0;
+ }
+ break;
+
+ case PES_LPCM_HEADER: // lpcm header
+ n = pesdx->HeaderSize - pesdx->HeaderIndex;
+ if (n > size) {
+ n = size;
+ }
+ memcpy(pesdx->Header + pesdx->HeaderIndex, p, n);
+ pesdx->HeaderIndex += n;
+ p += n;
+ size -= n;
+
+ if (pesdx->HeaderIndex == pesdx->HeaderSize) {
+ static int samplerates[] = { 48000, 96000, 44100, 32000 };
+ int samplerate;
+ int channels;
+ int bits_per_sample;
+ const uint8_t *q;
+
+ if (AudioCodecID != CODEC_ID_PCM_DVD) {
+
+ q = pesdx->Header;
+ Debug(3, "pesdemux: LPCM %d sr:%d bits:%d chan:%d\n",
+ q[0], q[5] >> 4, (((q[5] >> 6) & 0x3) + 4) * 4,
+ (q[5] & 0x7) + 1);
+ CodecAudioClose(MyAudioDecoder);
+
+ bits_per_sample = (((q[5] >> 6) & 0x3) + 4) * 4;
+ if (bits_per_sample != 16) {
+ Error(_
+ ("softhddev: LPCM %d bits per sample aren't supported\n"),
+ bits_per_sample);
+ // FIXME: handle unsupported formats.
+ }
+ samplerate = samplerates[q[5] >> 4];
+ channels = (q[5] & 0x7) + 1;
+ AudioSetup(&samplerate, &channels, 0);
+ if (samplerate != samplerates[q[5] >> 4]) {
+ Error(_
+ ("softhddev: LPCM %d sample-rate is unsupported\n"),
+ samplerates[q[5] >> 4]);
+ // FIXME: support resample
+ }
+ if (channels != (q[5] & 0x7) + 1) {
+ Error(_
+ ("softhddev: LPCM %d channels are unsupported\n"),
+ (q[5] & 0x7) + 1);
+ // FIXME: support resample
+ }
+ //CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_PCM_DVD);
+ AudioCodecID = CODEC_ID_PCM_DVD;
+ }
+ pesdx->State = PES_LPCM_PAYLOAD;
+ pesdx->Index = 0;
+ pesdx->Skip = 0;
+ }
+ break;
+
+ case PES_LPCM_PAYLOAD: // lpcm payload
+ // fill buffer
+ n = pesdx->Size - pesdx->Index;
+ if (n > size) {
+ n = size;
+ }
+ memcpy(pesdx->Buffer + pesdx->Index, p, n);
+ pesdx->Index += n;
+ p += n;
+ size -= n;
+
+ if (pesdx->PTS != (int64_t) AV_NOPTS_VALUE) {
+ // FIXME: needs bigger buffer
+ AudioSetClock(pesdx->PTS);
+ pesdx->PTS = AV_NOPTS_VALUE;
+ }
+ swab(pesdx->Buffer, pesdx->Buffer, pesdx->Index);
+ AudioEnqueue(pesdx->Buffer, pesdx->Index);
+ pesdx->Index = 0;
+ break;
+ }
+ } while (size > 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Transport stream demux
+//////////////////////////////////////////////////////////////////////////////
+
+ /// Transport stream packet size
+#define TS_PACKET_SIZE 188
+ /// Transport stream packet sync byte
+#define TS_PACKET_SYNC 0x47
+
+///
+/// transport stream demuxer typedef.
+///
+typedef struct _ts_demux_ TsDemux;
+
+///
+/// transport stream demuxer structure.
+///
+struct _ts_demux_
+{
+ int Packets; ///< packets between PCR
+};
+
+static PesDemux PesDemuxAudio[1]; ///< audio demuxer
+
+///
+/// Transport stream demuxer.
+///
+/// @param tsdx transport stream demuxer
+/// @param data buffer of transport stream packets
+/// @param size size of buffer
+///
+/// @returns number of bytes consumed from buffer.
+///
+int TsDemuxer(TsDemux * tsdx, const uint8_t * data, int size)
+{
+ const uint8_t *p;
+
+ p = data;
+ while (size >= TS_PACKET_SIZE) {
+ int pid;
+ int payload;
+
+ if (p[0] != TS_PACKET_SYNC) {
+ Error(_("tsdemux: transport stream out of sync\n"));
+ // FIXME: kill all buffers
+ return size;
+ }
+ ++tsdx->Packets;
+ if (p[1] & 0x80) { // error indicator
+ Debug(3, "tsdemux: transport error\n");
+ // FIXME: kill all buffers
+ goto next_packet;
+ }
+
+ pid = (p[1] & 0x1F) << 8 | p[2];
+ Debug(4, "tsdemux: PID: %#04x%s%s\n", pid, p[1] & 0x40 ? " start" : "",
+ p[3] & 0x10 ? " payload" : "");
+
+ // skip adaptation field
+ switch (p[3] & 0x30) { // adaption field
+ case 0x00: // reserved
+ case 0x20: // adaptation field only
+ default:
+ goto next_packet;
+ case 0x10: // only payload
+ payload = 4;
+ break;
+ case 0x30: // skip adapation field
+ payload = 5 + p[4];
+ // illegal length, ignore packet
+ if (payload >= TS_PACKET_SIZE) {
+ Debug(3, "tsdemux: illegal adaption field length\n");
+ goto next_packet;
+ }
+ break;
+ }
+
+ PesParse(PesDemuxAudio, p + payload, TS_PACKET_SIZE - payload,
+ p[1] & 0x40);
+#if 0
+ int tmp;
+
+ // check continuity
+ tmp = p[3] & 0x0F; // continuity counter
+ if (((tsdx->CC + 1) & 0x0F) != tmp) {
+ Debug(3, "tsdemux: OUT OF SYNC: %d %d\n", tmp, tsdx->CC);
+ //TS discontinuity (received 8, expected 0) for PID
+ }
+ tsdx->CC = tmp;
+#endif
+
+ next_packet:
+ p += TS_PACKET_SIZE;
+ size -= TS_PACKET_SIZE;
+ }
+
+ return p - data;
}
+#endif
+
/**
** Play audio packet.
**
@@ -259,8 +844,7 @@ static int FindDolbySync(const AVPacket * avpkt)
int PlayAudio(const uint8_t * data, int size, uint8_t id)
{
int n;
- int osize;
- AVPacket avpkt[1];
+ const uint8_t *p;
// channel switch: SetAudioChannelDevice: SetDigitalAudioDevice:
@@ -270,206 +854,226 @@ int PlayAudio(const uint8_t * data, int size, uint8_t id)
if (SkipAudio || !MyAudioDecoder) { // skip audio
return size;
}
+
if (NewAudioStream) {
// FIXME: does this clear the audio ringbuffer?
CodecAudioClose(MyAudioDecoder);
+ AudioSetBufferTime(0);
AudioCodecID = CODEC_ID_NONE;
+ AudioChannelID = -1;
NewAudioStream = 0;
}
- // PES header 0x00 0x00 0x01 ID
- // ID 0xBD 0xC0-0xCF
-
- if (size < 9) {
- Error(_("[softhddev] invalid audio packet\n"));
- return size;
- }
// Don't overrun audio buffers on replay
if (AudioFreeBytes() < 3072 * 8 * 8) { // 8 channels 8 packets
return 0;
}
+ // PES header 0x00 0x00 0x01 ID
+ // ID 0xBD 0xC0-0xCF
+ // must be a PES start code
+ if (size < 9 || !data || data[0] || data[1] || data[2] != 0x01) {
+ Error(_("[softhddev] invalid PES audio packet\n"));
+ return size;
+ }
n = data[8]; // header size
- av_init_packet(avpkt);
+ if (size < 9 + n + 4) { // wrong size
+ if (size == 9 + n) {
+ Warning(_("[softhddev] empty audio packet\n"));
+ } else {
+ Error(_("[softhddev] invalid audio packet %d bytes\n"), size);
+ }
+ return size;
+ }
+
if (data[7] & 0x80 && n >= 5) {
- avpkt->pts =
+ AudioAvPkt->pts =
(int64_t) (data[9] & 0x0E) << 29 | data[10] << 22 | (data[11] &
0xFE) << 14 | data[12] << 7 | (data[13] & 0xFE) >> 1;
- //Debug(3, "audio: pts %#012" PRIx64 "\n", avpkt->pts);
+ //Debug(3, "audio: pts %#012" PRIx64 "\n", AudioAvPkt->pts);
}
if (0) { // dts is unused
if (data[7] & 0x40) {
- avpkt->dts =
+ AudioAvPkt->dts =
(int64_t) (data[14] & 0x0E) << 29 | data[15] << 22 | (data[16]
& 0xFE) << 14 | data[17] << 7 | (data[18] & 0xFE) >> 1;
- Debug(3, "audio: dts %#012" PRIx64 "\n", avpkt->dts);
+ Debug(3, "audio: dts %#012" PRIx64 "\n", AudioAvPkt->dts);
}
}
- osize = size;
- data += 9 + n;
- size -= 9 + n; // skip pes header
- if (size <= 0) {
- Error(_("[softhddev] invalid audio packet\n"));
- return osize;
+ p = data + 9 + n;
+ n = size - 9 - n; // skip pes header
+ if (n + AudioAvPkt->stream_index > AudioAvPkt->size) {
+ Fatal(_("[softhddev] audio buffer too small\n"));
+ AudioAvPkt->stream_index = 0;
}
- // Detect audio code
- // MPEG-PS mp2 MPEG1, MPEG2, AC3, LPCM, AAC LATM
- // only if unknown or new channel id
- if (AudioCodecID == CODEC_ID_NONE || AudioChannelID != id) {
+ if (AudioChannelID != id) {
AudioChannelID = id;
- // Syncword - 0x0B77
- if (data[0] == 0x0B && data[1] == 0x77) {
- if (AudioCodecID != CODEC_ID_AC3) {
- Debug(3, "[softhddev]%s: AC-3 %d\n", __FUNCTION__, id);
- CodecAudioClose(MyAudioDecoder);
- CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_AC3);
- AudioCodecID = CODEC_ID_AC3;
- }
- AudioRawAc3 = 1;
- // Syncword - 0xFFFC - 0xFFFF
- } else if (data[0] == 0xFF && (data[1] & 0xFC) == 0xFC) {
- if (AudioCodecID != CODEC_ID_MP2) {
- Debug(3, "[softhddev]%s: MP2 %d\n", __FUNCTION__, id);
- CodecAudioClose(MyAudioDecoder);
- CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_MP2);
- AudioCodecID = CODEC_ID_MP2;
- }
- // latm header 0x56E0 11bits: 0x2B7
- } else if (data[0] == 0x56 && (data[1] & 0xE0) == 0xE0
- && (((data[1] & 0x1F) << 8) + (data[2] & 0xFF)) < size) {
- if (AudioCodecID != CODEC_ID_AAC_LATM) {
-#if 0
- // test harder check
- printf("%d %d\n", (((data[1] & 0x1F) << 8) + (data[2] & 0xFF)),
- size);
- printf("%p %x %x\n", data,
- data[3 + (((data[1] & 0x1F) << 8) + (data[2] & 0xFF))],
- data[4 + (((data[1] & 0x1F) << 8) + (data[2] & 0xFF))]);
-#endif
- Debug(3, "[softhddev]%s: AAC LATM %d\n", __FUNCTION__, id);
- CodecAudioClose(MyAudioDecoder);
- CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_AAC_LATM);
- AudioCodecID = CODEC_ID_AAC_LATM;
+ AudioCodecID = CODEC_ID_NONE;
+ }
+ // Private stream + LPCM ID
+ if ((id & 0xF0) == 0xA0) {
+ if (n < 7) {
+ Error(_("[softhddev] invalid LPCM audio packet %d bytes\n"), size);
+ return size;
+ }
+ if (AudioCodecID != CODEC_ID_PCM_DVD) {
+ static int samplerates[] = { 48000, 96000, 44100, 32000 };
+ int samplerate;
+ int channels;
+ int bits_per_sample;
+
+ Debug(3, "[softhddev]%s: LPCM %d sr:%d bits:%d chan:%d\n",
+ __FUNCTION__, id, p[5] >> 4, (((p[5] >> 6) & 0x3) + 4) * 4,
+ (p[5] & 0x7) + 1);
+ CodecAudioClose(MyAudioDecoder);
+
+ bits_per_sample = (((p[5] >> 6) & 0x3) + 4) * 4;
+ if (bits_per_sample != 16) {
+ Error(_
+ ("softhddev: LPCM %d bits per sample aren't supported\n"),
+ bits_per_sample);
+ // FIXME: handle unsupported formats.
}
- // Private stream + DVD Track ID Syncword - 0x0B77
- } else if (data[-n - 9 + 3] == 0xBD && (data[0] & 0xF0) == 0x80
- && data[4] == 0x0B && data[5] == 0x77) {
- if (AudioCodecID != CODEC_ID_AC3) {
- Debug(3, "[softhddev]%s: DVD Audio %d\n", __FUNCTION__, id);
- CodecAudioClose(MyAudioDecoder);
- CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_AC3);
- AudioCodecID = CODEC_ID_AC3;
+ samplerate = samplerates[p[5] >> 4];
+ channels = (p[5] & 0x7) + 1;
+
+ AudioSetBufferTime(400);
+ AudioSetup(&samplerate, &channels, 0);
+ if (samplerate != samplerates[p[5] >> 4]) {
+ Error(_("softhddev: LPCM %d sample-rate is unsupported\n"),
+ samplerates[p[5] >> 4]);
+ // FIXME: support resample
}
- AudioRawAc3 = 0;
- // Private stream + LPCM ID
- } else if (data[-n - 9 + 3] == 0xBD && data[0] == 0xA0) {
- if (AudioCodecID != CODEC_ID_PCM_DVD) {
- static int samplerates[] = { 48000, 96000, 44100, 32000 };
- int samplerate;
- int channels;
- int bits_per_sample;
-
- Debug(3, "[softhddev]%s: LPCM %d sr:%d bits:%d chan:%d\n",
- __FUNCTION__, id, data[5] >> 4,
- (((data[5] >> 6) & 0x3) + 4) * 4, (data[5] & 0x7) + 1);
- CodecAudioClose(MyAudioDecoder);
-
- bits_per_sample = (((data[5] >> 6) & 0x3) + 4) * 4;
- if (bits_per_sample != 16) {
- Error(_
- ("softhddev: LPCM %d bits per sample aren't supported\n"),
- bits_per_sample);
- // FIXME: handle unsupported formats.
- }
- samplerate = samplerates[data[5] >> 4];
- channels = (data[5] & 0x7) + 1;
- AudioSetup(&samplerate, &channels, 0);
- if (samplerate != samplerates[data[5] >> 4]) {
- Error(_("softhddev: LPCM %d sample-rate is unsupported\n"),
- samplerates[data[5] >> 4]);
- // FIXME: support resample
- }
- if (channels != (data[5] & 0x7) + 1) {
- Error(_("softhddev: LPCM %d channels are unsupported\n"),
- (data[5] & 0x7) + 1);
- // FIXME: support resample
- }
- //CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_PCM_DVD);
- AudioCodecID = CODEC_ID_PCM_DVD;
+ if (channels != (p[5] & 0x7) + 1) {
+ Error(_("softhddev: LPCM %d channels are unsupported\n"),
+ (p[5] & 0x7) + 1);
+ // FIXME: support resample
}
- } else {
- // no start package
- // FIXME: Nick/Viva sends this shit, need to find sync in packet
- // FIXME: otherwise it takes too long until sound appears
-
- if (AudioCodecID == CODEC_ID_NONE) {
- int codec_id;
-
- Debug(3, "[softhddev]%s: ??? %d\n", __FUNCTION__, id);
- avpkt->data = (void *)data;
- avpkt->size = size;
- if (AudioChannelID == 0xBD) {
- n = FindDolbySync(avpkt);
- codec_id = CODEC_ID_AC3;
- AudioRawAc3 = 1;
- } else if (0xC0 <= AudioChannelID && AudioChannelID <= 0xDF) {
- n = FindAudioSync(avpkt);
- codec_id = CODEC_ID_MP2;
- } else {
- n = -1;
- }
- if (n < 0) {
- return osize;
- }
+ //CodecAudioOpen(MyAudioDecoder, NULL, CODEC_ID_PCM_DVD);
+ AudioCodecID = CODEC_ID_PCM_DVD;
+ }
- CodecAudioOpen(MyAudioDecoder, NULL, codec_id);
- AudioCodecID = codec_id;
- data += n;
- size -= n;
- avpkt->pts = AV_NOPTS_VALUE;
- }
+ if (AudioAvPkt->pts != (int64_t) AV_NOPTS_VALUE) {
+ AudioSetClock(AudioAvPkt->pts);
+ AudioAvPkt->pts = AV_NOPTS_VALUE;
}
- // still no decoder or codec known
+ swab(p + 7, AudioAvPkt->data, n - 7);
+ AudioEnqueue(AudioAvPkt->data, n - 7);
+
+ return size;
+ }
+ // DVD track header
+ if ((id & 0xF0) == 0x80 && (p[0] & 0xF0) == 0x80) {
+ p += 4;
+ n -= 4; // skip track header
if (AudioCodecID == CODEC_ID_NONE) {
- return osize;
+ AudioSetBufferTime(400);
}
}
- // convert data, if needed for ffmpeg
- if (AudioCodecID == CODEC_ID_AC3 && !AudioRawAc3
- && (data[0] & 0xF0) == 0x80) {
- avpkt->data = (void *)data + 4; // skip track header
- avpkt->size = size - 4;
- if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
- avpkt->pts += 200 * 90; // FIXME: needs bigger buffer
+ // append new packet, to partial old data
+ memcpy(AudioAvPkt->data + AudioAvPkt->stream_index, p, n);
+ AudioAvPkt->stream_index += n;
+
+ n = AudioAvPkt->stream_index;
+ p = AudioAvPkt->data;
+ while (n >= 5) {
+ int r;
+ unsigned codec_id;
+
+ // 4 bytes 0xFFExxxxx Mpeg audio
+ // 3 bytes 0x56Exxx AAC LATM audio
+ // 5 bytes 0x0B77xxxxxx AC3 audio
+ // PCM audio can't be found
+ r = 0;
+ codec_id = CODEC_ID_NONE; // keep compiler happy
+ if (id != 0xbd && FastMpegCheck(p)) {
+ r = MpegCheck(p, n);
+ codec_id = CODEC_ID_MP2;
}
- CodecAudioDecode(MyAudioDecoder, avpkt);
- } else if (AudioCodecID == CODEC_ID_PCM_DVD) {
- if (size > 7) {
- char *buf;
-
- if (avpkt->pts != (int64_t) AV_NOPTS_VALUE) {
- // FIXME: needs bigger buffer
- AudioSetClock(avpkt->pts + 200 * 90);
- }
- if (!(buf = malloc(size - 7))) {
- Error(_("softhddev: out of memory\n"));
- } else {
- swab(data + 7, buf, size - 7);
- AudioEnqueue(buf, size - 7);
- free(buf);
+ if (id != 0xbd && !r && FastLatmCheck(p)) {
+ r = LatmCheck(p, n);
+ codec_id = CODEC_ID_AAC_LATM;
+ }
+ if ((id == 0xbd || (id & 0xF0) == 0x80) && !r && FastAc3Check(p)) {
+ r = Ac3Check(p, n);
+ codec_id = CODEC_ID_AC3;
+ }
+ if (r < 0) { // need more bytes
+ break;
+ }
+ if (r > 0) {
+ AVPacket avpkt[1];
+
+ // new codec id, close and open new
+ if (AudioCodecID != codec_id) {
+ CodecAudioClose(MyAudioDecoder);
+ CodecAudioOpen(MyAudioDecoder, NULL, codec_id);
+ AudioCodecID = codec_id;
}
+ av_init_packet(avpkt);
+ avpkt->data = (void *)p;
+ avpkt->size = r;
+ avpkt->pts = AudioAvPkt->pts;
+ avpkt->dts = AudioAvPkt->dts;
+ CodecAudioDecode(MyAudioDecoder, avpkt);
+ AudioAvPkt->pts = AV_NOPTS_VALUE;
+ AudioAvPkt->dts = AV_NOPTS_VALUE;
+ p += r;
+ n -= r;
+ continue;
}
- } else {
- avpkt->data = (void *)data;
- avpkt->size = size;
- CodecAudioDecode(MyAudioDecoder, avpkt);
+ ++p;
+ --n;
+ }
+
+ // copy remaining bytes to start of packet
+ if (n) {
+ memmove(AudioAvPkt->data, p, n);
}
+ AudioAvPkt->stream_index = n;
- return osize;
+ return size;
}
+#ifdef USE_TS_AUDIO
+
+/**
+** Play transport stream audio packet.
+**
+** @param data data of exactly one complete TS packet
+** @param size size of TS packet (always TS_PACKET_SIZE)
+**
+** @returns number of bytes consumed;
+*/
+int PlayTsAudio(const uint8_t * data, int size)
+{
+ static TsDemux tsdx[1];
+
+ if (StreamFreezed) { // stream freezed
+ return 0;
+ }
+ if (SkipAudio || !MyAudioDecoder) { // skip audio
+ return size;
+ }
+ // Don't overrun audio buffers on replay
+ if (AudioFreeBytes() < 3072 * 8 * 8) { // 8 channels 8 packets
+ return 0;
+ }
+
+ if (NewAudioStream) {
+ // FIXME: does this clear the audio ringbuffer?
+ CodecAudioClose(MyAudioDecoder);
+ AudioCodecID = CODEC_ID_NONE;
+ NewAudioStream = 0;
+ PesReset(PesDemuxAudio);
+ }
+ return TsDemuxer(tsdx, data, size);
+}
+
+#endif
+
/**
** Set volume of audio device.
**
@@ -495,14 +1099,13 @@ static enum CodecID VideoCodecID; ///< current codec id
static const char *X11DisplayName; ///< x11 display name
static volatile char Usr1Signal; ///< true got usr1 signal
- /// video PES buffer default size
-#define VIDEO_BUFFER_SIZE (512 * 1024)
-#define VIDEO_PACKET_MAX 128 ///< max number of video packets
+#define VIDEO_BUFFER_SIZE (512 * 1024) ///< video PES buffer default size
+#define VIDEO_PACKET_MAX 192 ///< max number of video packets
/// video PES packet ring buffer
static AVPacket VideoPacketRb[VIDEO_PACKET_MAX];
static int VideoPacketWrite; ///< write pointer
static int VideoPacketRead; ///< read pointer
-atomic_t VideoPacketsFilled; ///< how many of the buffer is used
+static atomic_t VideoPacketsFilled; ///< how many of the buffer is used
static volatile char VideoClearBuffers; ///< clear video buffers
static volatile char SkipVideo; ///< skip video
@@ -813,6 +1416,14 @@ int VideoDecode(void)
}
/**
+** Get number of video buffers.
+*/
+int VideoGetBuffers(void)
+{
+ return atomic_read(&VideoPacketsFilled);
+}
+
+/**
** Try video start.
**
** NOT TRUE: Could be called, when already started.
@@ -949,9 +1560,13 @@ int PlayVideo(const uint8_t * data, int size)
return size;
}
n = data[8]; // header size
- // wrong size
- if (size < 9 + n + 4) {
- Error(_("[softhddev] invalid video packet %d bytes\n"), size);
+
+ if (size < 9 + n + 4) { // wrong size
+ if (size == 9 + n) {
+ Warning(_("[softhddev] empty video packet\n"));
+ } else {
+ Error(_("[softhddev] invalid video packet %d bytes\n"), size);
+ }
return size;
}
// buffer full: needed for replay
@@ -1374,11 +1989,11 @@ void StillPicture(const uint8_t * data, int size)
int Poll(int timeout)
{
// buffers are too full
- if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX / 2) {
+ if (atomic_read(&VideoPacketsFilled) >= VIDEO_PACKET_MAX * 2 / 3) {
if (timeout) { // let display thread work
usleep(timeout * 1000);
}
- return atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX / 2;
+ return atomic_read(&VideoPacketsFilled) < VIDEO_PACKET_MAX * 2 / 3;
}
return 1;
}
@@ -1645,6 +2260,7 @@ void SoftHdDeviceExit(void)
MyAudioDecoder = NULL;
}
NewAudioStream = 0;
+ av_free_packet(AudioAvPkt);
StopVideo();
@@ -1702,8 +2318,10 @@ void Start(void)
if (!ConfigStartSuspended) {
// FIXME: AudioInit for HDMI after X11 startup
AudioInit();
+ av_new_packet(AudioAvPkt, AUDIO_BUFFER_SIZE);
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
+ AudioChannelID = -1;
if (!ConfigStartX11Server) {
StartVideo();
@@ -1713,6 +2331,10 @@ void Start(void)
SkipAudio = 1;
}
pthread_mutex_init(&SuspendLockMutex, NULL);
+
+#ifdef USE_TS_AUDIO
+ PesInit(PesDemuxAudio);
+#endif
}
/**
@@ -1770,6 +2392,7 @@ void Suspend(int video, int audio, int dox11)
MyAudioDecoder = NULL;
}
NewAudioStream = 0;
+ av_free_packet(AudioAvPkt);
}
if (video) {
StopVideo();
@@ -1801,8 +2424,10 @@ void Resume(void)
}
if (!MyAudioDecoder) { // audio not running
AudioInit();
+ av_new_packet(AudioAvPkt, AUDIO_BUFFER_SIZE);
MyAudioDecoder = CodecAudioNewDecoder();
AudioCodecID = CODEC_ID_NONE;
+ AudioChannelID = -1;
}
SkipVideo = 0;
diff --git a/softhddev.h b/softhddev.h
index 631cd56..573c37a 100644
--- a/softhddev.h
+++ b/softhddev.h
@@ -37,6 +37,8 @@ extern "C"
/// C plugin play audio packet
extern int PlayAudio(const uint8_t *, int, uint8_t);
+ /// C plugin play TS audio packet
+ extern int PlayTsAudio(const uint8_t *, int);
/// C plugin set audio volume
extern void SetVolumeDevice(int);
diff --git a/softhddevice.cpp b/softhddevice.cpp
index d02580d..962d8d1 100644
--- a/softhddevice.cpp
+++ b/softhddevice.cpp
@@ -835,9 +835,11 @@ class cSoftHdDevice:public cDevice
virtual void GetVideoSize(int &, int &, double &);
virtual void GetOsdSize(int &, int &, double &);
virtual int PlayVideo(const uchar *, int);
-
+#ifndef xxUSE_TS_AUDIO
+ virtual int PlayAudio(const uchar *, int, uchar);
+#endif
//virtual int PlayTsVideo(const uchar *, int);
-#ifndef USE_AUDIO_THREAD // FIXME: testing none threaded
+#if !defined(USE_AUDIO_THREAD) || defined(USE_TS_AUDIO)
virtual int PlayTsAudio(const uchar *, int);
#endif
virtual void SetAudioChannelDevice(int);
@@ -845,7 +847,6 @@ class cSoftHdDevice:public cDevice
virtual void SetDigitalAudioDevice(bool);
virtual void SetAudioTrackDevice(eTrackType);
virtual void SetVolumeDevice(int);
- virtual int PlayAudio(const uchar *, int, uchar);
// Image Grab facilities
@@ -1066,8 +1067,8 @@ bool cSoftHdDevice::Flush(int timeout_ms)
** Sets the video display format to the given one (only useful if this
** device has an MPEG decoder).
*/
-void cSoftHdDevice::
-SetVideoDisplayFormat(eVideoDisplayFormat video_display_format)
+void cSoftHdDevice:: SetVideoDisplayFormat(eVideoDisplayFormat
+ video_display_format)
{
static int last = -1;
@@ -1124,8 +1125,14 @@ void cSoftHdDevice::GetOsdSize(int &width, int &height, double &pixel_aspect)
// ----------------------------------------------------------------------------
+#ifndef xxUSE_TS_AUDIO
+
/**
** Play a audio packet.
+**
+** @param data exactly one complete PES packet (which is incomplete)
+** @param length length of PES packet
+** @param id type of audio data this packet holds
*/
int cSoftHdDevice::PlayAudio(const uchar * data, int length, uchar id)
{
@@ -1134,6 +1141,8 @@ int cSoftHdDevice::PlayAudio(const uchar * data, int length, uchar id)
return::PlayAudio(data, length, id);
}
+#endif
+
void cSoftHdDevice::SetAudioTrackDevice(
__attribute__ ((unused)) eTrackType type)
{
@@ -1173,6 +1182,9 @@ void cSoftHdDevice::SetVolumeDevice(int volume)
/**
** Play a video packet.
+**
+** @param data exactly one complete PES packet (which is incomplete)
+** @param length length of PES packet
*/
int cSoftHdDevice::PlayVideo(const uchar * data, int length)
{
diff --git a/video.c b/video.c
index 857cca7..a147988 100644
--- a/video.c
+++ b/video.c
@@ -329,7 +329,6 @@ static xcb_atom_t NetWmState; ///< wm-state message atom
static xcb_atom_t NetWmStateFullscreen; ///< fullscreen wm-state message atom
extern uint32_t VideoSwitch; ///< ticks for channel switch
-extern atomic_t VideoPacketsFilled; ///< how many of the buffer is used
#ifdef USE_VIDEO_THREAD
@@ -396,19 +395,19 @@ static void VideoSetPts(int64_t * pts_p, int interlaced, const AVFrame * frame)
int64_t pts;
// update video clock
- if ((uint64_t) * pts_p != AV_NOPTS_VALUE) {
+ if (*pts_p != (int64_t) AV_NOPTS_VALUE) {
*pts_p += interlaced ? 40 * 90 : 20 * 90;
}
//pts = frame->best_effort_timestamp;
pts = frame->pkt_pts;
- if ((uint64_t) pts == AV_NOPTS_VALUE || !pts) {
+ if (pts == (int64_t) AV_NOPTS_VALUE || !pts) {
// libav: 0.8pre didn't set pts
pts = frame->pkt_dts;
}
// libav: sets only pkt_dts which can be 0
- if (pts && (uint64_t) pts != AV_NOPTS_VALUE) {
+ if (pts && pts != (int64_t) AV_NOPTS_VALUE) {
// build a monotonic pts
- if ((uint64_t) * pts_p != AV_NOPTS_VALUE) {
+ if (*pts_p != (int64_t) AV_NOPTS_VALUE) {
int64_t delta;
delta = pts - *pts_p;
@@ -4224,7 +4223,7 @@ static void VaapiAdvanceFrame(void)
Warning(_
("video: display buffer empty, duping frame (%d/%d) %d\n"),
decoder->FramesDuped, decoder->FrameCounter,
- atomic_read(&VideoPacketsFilled));
+ VideoGetBuffers());
}
last_warned_frame = decoder->FrameCounter;
if (!(decoder->FramesDisplayed % 300)) {
@@ -4358,10 +4357,10 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
// FIXME: audio not known assume 333ms delay
if (decoder->DupNextFrame) {
- ++decoder->FramesDuped;
decoder->DupNextFrame--;
- } else if ((uint64_t) audio_clock != AV_NOPTS_VALUE
- && (uint64_t) video_clock != AV_NOPTS_VALUE) {
+ ++decoder->FramesDuped;
+ } else if (audio_clock != (int64_t) AV_NOPTS_VALUE
+ && video_clock != (int64_t) AV_NOPTS_VALUE) {
// both clocks are known
if (abs(video_clock - audio_clock) > 5000 * 90) {
@@ -4377,6 +4376,12 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
Debug(3, "video: speed up video\n");
decoder->DropNextFrame = 1;
}
+ } else if (audio_clock == (int64_t) AV_NOPTS_VALUE
+ && video_clock != (int64_t) AV_NOPTS_VALUE) {
+ if (VideoGetBuffers() < 4) {
+ Debug(3, "video: initial slow down video\n");
+ decoder->DupNextFrame++;
+ }
}
#if defined(DEBUG) || defined(AV_INFO)
// debug audio/video sync
@@ -4386,8 +4391,7 @@ static void VaapiSyncDisplayFrame(VaapiDecoder * decoder)
VideoTimeStampString(video_clock),
abs((video_clock - audio_clock) / 90) <
9999 ? ((video_clock - audio_clock) / 90) : 88888,
- AudioGetDelay() / 90, (int)VideoDeltaPTS / 90,
- atomic_read(&VideoPacketsFilled));
+ AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers());
}
#endif
}
@@ -4463,7 +4467,7 @@ static void VaapiSyncRenderFrame(VaapiDecoder * decoder,
static int64_t VaapiGetClock(const VaapiDecoder * decoder)
{
// pts is the timestamp of the latest decoded frame
- if (!decoder || (uint64_t) decoder->PTS == AV_NOPTS_VALUE) {
+ if (!decoder || decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
return AV_NOPTS_VALUE;
}
// subtract buffered decoded frames
@@ -7235,7 +7239,7 @@ static void VdpauAdvanceFrame(void)
Warning(_
("video: display buffer empty, duping frame (%d/%d) %d\n"),
decoder->FramesDuped, decoder->FrameCounter,
- atomic_read(&VideoPacketsFilled));
+ VideoGetBuffers());
}
last_warned_frame = decoder->FrameCounter;
if (!(decoder->FramesDisplayed % 300)) {
@@ -7371,8 +7375,9 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
if (decoder->DupNextFrame) {
decoder->DupNextFrame--;
- } else if ((uint64_t) audio_clock != AV_NOPTS_VALUE
- && (uint64_t) video_clock != AV_NOPTS_VALUE) {
+ ++decoder->FramesDuped;
+ } else if (audio_clock != (int64_t) AV_NOPTS_VALUE
+ && video_clock != (int64_t) AV_NOPTS_VALUE) {
// both clocks are known
if (abs(video_clock - audio_clock) > 5000 * 90) {
@@ -7388,6 +7393,12 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
Debug(3, "video: speed up video\n");
decoder->DropNextFrame = 1;
}
+ } else if (audio_clock == (int64_t) AV_NOPTS_VALUE
+ && video_clock != (int64_t) AV_NOPTS_VALUE) {
+ if (VideoGetBuffers() < 4) {
+ Debug(3, "video: initial slow down video\n");
+ decoder->DupNextFrame++;
+ }
}
#if defined(DEBUG) || defined(AV_INFO)
// debug audio/video sync
@@ -7397,8 +7408,7 @@ static void VdpauSyncDisplayFrame(VdpauDecoder * decoder)
VideoTimeStampString(video_clock),
abs((video_clock - audio_clock) / 90) <
9999 ? ((video_clock - audio_clock) / 90) : 88888,
- AudioGetDelay() / 90, (int)VideoDeltaPTS / 90,
- atomic_read(&VideoPacketsFilled));
+ AudioGetDelay() / 90, (int)VideoDeltaPTS / 90, VideoGetBuffers());
}
#endif
}
@@ -7480,7 +7490,7 @@ static void VdpauSyncRenderFrame(VdpauDecoder * decoder,
static int64_t VdpauGetClock(const VdpauDecoder * decoder)
{
// pts is the timestamp of the latest decoded frame
- if (!decoder || (uint64_t) decoder->PTS == AV_NOPTS_VALUE) {
+ if (!decoder || decoder->PTS == (int64_t) AV_NOPTS_VALUE) {
return AV_NOPTS_VALUE;
}
// subtract buffered decoded frames
@@ -9324,6 +9334,7 @@ void VideoExit(void)
if (VideoUsedModule) {
VideoUsedModule->Exit();
}
+ VideoUsedModule = NULL; // FIXME: NoopModule;
#ifdef USE_GLX
if (GlxEnabled) {
GlxExit();
diff --git a/video.h b/video.h
index d9fd608..90c8c32 100644
--- a/video.h
+++ b/video.h
@@ -144,5 +144,6 @@ extern void VideoExit(void); ///< Cleanup and exit video module.
extern void VideoFlushInput(void); ///< Flush video input buffers.
extern int VideoDecode(void); ///< Decode video input buffers.
+extern int VideoGetBuffers(void); ///< Get number of input buffers.
/// @}