mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
libeplayer3: move audio resampling to dedicated ipcm writer
This commit is contained in:
@@ -194,10 +194,10 @@ static char *Codec2Encoding(AVCodecContext * codec, int *version)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long int calcPts(AVStream * stream, int64_t pts)
|
int64_t calcPts(AVFormatContext *avfc, AVStream * stream, int64_t pts)
|
||||||
{
|
{
|
||||||
if (!stream) {
|
if (!avfc || !stream) {
|
||||||
ffmpeg_err("stream / packet null\n");
|
ffmpeg_err("context / stream null\n");
|
||||||
return INVALID_PTS_VALUE;
|
return INVALID_PTS_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ long long int calcPts(AVStream * stream, int64_t pts)
|
|||||||
else if (avContext->start_time == AV_NOPTS_VALUE)
|
else if (avContext->start_time == AV_NOPTS_VALUE)
|
||||||
pts = 90000.0 * (double) pts * av_q2d(stream->time_base);
|
pts = 90000.0 * (double) pts * av_q2d(stream->time_base);
|
||||||
else
|
else
|
||||||
pts = 90000.0 * (double) pts * av_q2d(stream->time_base) - 90000.0 * avContext->start_time / AV_TIME_BASE;
|
pts = 90000.0 * (double) pts * av_q2d(stream->time_base) - 90000.0 * avfc->start_time / AV_TIME_BASE;
|
||||||
|
|
||||||
if (pts & 0x8000000000000000ull)
|
if (pts & 0x8000000000000000ull)
|
||||||
pts = INVALID_PTS_VALUE;
|
pts = INVALID_PTS_VALUE;
|
||||||
@@ -234,16 +234,7 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
|
|
||||||
hasPlayThreadStarted = 1;
|
hasPlayThreadStarted = 1;
|
||||||
|
|
||||||
int64_t currentVideoPts = -1, currentAudioPts = -1, showtime = 0, bofcount = 0;
|
int64_t currentVideoPts = 0, currentAudioPts = 0, showtime = 0, bofcount = 0;
|
||||||
AudioVideoOut_t avOut;
|
|
||||||
|
|
||||||
SwrContext *swr = NULL;
|
|
||||||
AVFrame *decoded_frame = NULL;
|
|
||||||
int out_sample_rate = 44100;
|
|
||||||
int out_channels = 2;
|
|
||||||
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
int restart_audio_resampling = 0;
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "\n");
|
ffmpeg_printf(10, "\n");
|
||||||
|
|
||||||
while (context->playback->isCreationPhase) {
|
while (context->playback->isCreationPhase) {
|
||||||
@@ -253,6 +244,9 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
ffmpeg_printf(10, "Running!\n");
|
ffmpeg_printf(10, "Running!\n");
|
||||||
|
|
||||||
while (context && context->playback && context->playback->isPlaying && !context->playback->abortRequested) {
|
while (context && context->playback && context->playback->isPlaying && !context->playback->abortRequested) {
|
||||||
|
AudioVideoOut_t avOut;
|
||||||
|
avOut.restart_audio_resampling = 0;
|
||||||
|
|
||||||
|
|
||||||
//IF MOVIE IS PAUSED, WAIT
|
//IF MOVIE IS PAUSED, WAIT
|
||||||
if (context->playback->isPaused) {
|
if (context->playback->isPaused) {
|
||||||
@@ -321,7 +315,7 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
if (res < 0 && context->playback->BackWard)
|
if (res < 0 && context->playback->BackWard)
|
||||||
bofcount = 1;
|
bofcount = 1;
|
||||||
seek_target = INT64_MIN;
|
seek_target = INT64_MIN;
|
||||||
restart_audio_resampling = 1;
|
avOut.restart_audio_resampling = 1;
|
||||||
|
|
||||||
// flush streams
|
// flush streams
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@@ -370,13 +364,14 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
ffmpeg_printf(200, "packet_size %d - index %d\n", packet_size, pid);
|
ffmpeg_printf(200, "packet_size %d - index %d\n", packet_size, pid);
|
||||||
|
|
||||||
if (videoTrack && (videoTrack->Id == pid)) {
|
if (videoTrack && (videoTrack->Id == pid)) {
|
||||||
currentVideoPts = videoTrack->pts = pts = calcPts(videoTrack->stream, packet.pts);
|
currentVideoPts = /* CHECK videoTrack->pts = */pts = calcPts(avContext, videoTrack->stream, packet.pts);
|
||||||
|
|
||||||
ffmpeg_printf(200, "VideoTrack index = %d %lld\n", pid, currentVideoPts);
|
ffmpeg_printf(200, "VideoTrack index = %d %lld\n", pid, currentVideoPts);
|
||||||
|
|
||||||
avOut.data = packet_data;
|
avOut.data = packet_data;
|
||||||
avOut.len = packet_size;
|
avOut.len = packet_size;
|
||||||
avOut.pts = pts;
|
avOut.pts = pts;
|
||||||
|
avOut.packet = &packet;
|
||||||
|
|
||||||
avOut.type = "video";
|
avOut.type = "video";
|
||||||
avOut.stream = videoTrack->stream;
|
avOut.stream = videoTrack->stream;
|
||||||
@@ -386,8 +381,9 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
ffmpeg_err("writing data to video device failed\n");
|
ffmpeg_err("writing data to video device failed\n");
|
||||||
}
|
}
|
||||||
} else if (audioTrack && (audioTrack->Id == pid)) {
|
} else if (audioTrack && (audioTrack->Id == pid)) {
|
||||||
|
context->currentAudioPtsP = ¤tAudioPts; //FIXME, temporary workaround only
|
||||||
if (!context->playback->BackWard) {
|
if (!context->playback->BackWard) {
|
||||||
currentAudioPts = audioTrack->pts = pts = calcPts(audioTrack->stream, packet.pts);
|
currentAudioPts = /* CHECK audioTrack->pts = */pts = calcPts(avContext, audioTrack->stream, packet.pts);
|
||||||
|
|
||||||
ffmpeg_printf(200, "AudioTrack index = %d\n", pid);
|
ffmpeg_printf(200, "AudioTrack index = %d\n", pid);
|
||||||
if (audioTrack->inject_raw_pcm == 1) {
|
if (audioTrack->inject_raw_pcm == 1) {
|
||||||
@@ -401,137 +397,18 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
avOut.data = packet_data;
|
avOut.data = packet_data;
|
||||||
avOut.len = packet_size;
|
avOut.len = packet_size;
|
||||||
avOut.pts = pts;
|
avOut.pts = pts;
|
||||||
|
avOut.packet = &packet;
|
||||||
avOut.type = "audio";
|
avOut.type = "audio";
|
||||||
avOut.stream = audioTrack->stream;
|
avOut.stream = audioTrack->stream;
|
||||||
avOut.avfc = avContext;
|
avOut.avfc = avContext;
|
||||||
|
|
||||||
if (context->output->audio->Write(context, &avOut) < 0)
|
if (context->output->audio->Write(context, &avOut) < 0)
|
||||||
ffmpeg_err("(raw pcm) writing data to audio device failed\n");
|
ffmpeg_err("(raw pcm) writing data to audio device failed\n");
|
||||||
} else if (audioTrack->inject_as_pcm == 1) {
|
|
||||||
AVCodecContext *c = ((AVStream *) (audioTrack->stream))->codec;
|
|
||||||
|
|
||||||
if (restart_audio_resampling) {
|
|
||||||
restart_audio_resampling = 0;
|
|
||||||
if (swr) {
|
|
||||||
swr_free(&swr);
|
|
||||||
swr = NULL;
|
|
||||||
}
|
|
||||||
if (decoded_frame) {
|
|
||||||
av_frame_free(&decoded_frame);
|
|
||||||
decoded_frame = NULL;
|
|
||||||
}
|
|
||||||
context->output->Command(context, OUTPUT_CLEAR, NULL);
|
|
||||||
context->output->Command(context, OUTPUT_PLAY, NULL);
|
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder(c->codec_id);
|
|
||||||
|
|
||||||
if (!codec || avcodec_open2(c, codec, NULL))
|
|
||||||
fprintf(stderr, "%s %d: avcodec_open2 failed\n", __func__, __LINE__);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (packet_size > 0) {
|
|
||||||
int got_frame = 0;
|
|
||||||
if (!decoded_frame) {
|
|
||||||
if (!(decoded_frame = av_frame_alloc())) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
av_frame_unref(decoded_frame);
|
|
||||||
|
|
||||||
int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, &packet);
|
|
||||||
if (len < 0) {
|
|
||||||
restart_audio_resampling = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet_data += len;
|
|
||||||
packet_size -= len;
|
|
||||||
|
|
||||||
if (!got_frame)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int e;
|
|
||||||
if (!swr) {
|
|
||||||
int rates[] = { 48000, 96000, 192000, 44100, 88200, 176400, 0 };
|
|
||||||
int *rate = rates;
|
|
||||||
int in_rate = c->sample_rate;
|
|
||||||
while (*rate && ((*rate / in_rate) * in_rate != *rate)
|
|
||||||
&& (in_rate / *rate) * *rate != in_rate)
|
|
||||||
rate++;
|
|
||||||
out_sample_rate = *rate ? *rate : 44100;
|
|
||||||
swr = swr_alloc();
|
|
||||||
out_channels = c->channels;
|
|
||||||
if (c->channel_layout == 0) {
|
|
||||||
// FIXME -- need to guess, looks pretty much like a bug in the FFMPEG WMA decoder
|
|
||||||
c->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
}
|
|
||||||
|
|
||||||
out_channel_layout = c->channel_layout;
|
|
||||||
// player2 won't play mono
|
|
||||||
if (out_channel_layout == AV_CH_LAYOUT_MONO) {
|
|
||||||
out_channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
out_channels = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
av_opt_set_int(swr, "in_channel_layout", c->channel_layout, 0);
|
|
||||||
av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
|
|
||||||
av_opt_set_int(swr, "in_sample_rate", c->sample_rate, 0);
|
|
||||||
av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
|
|
||||||
av_opt_set_int(swr, "in_sample_fmt", c->sample_fmt, 0);
|
|
||||||
av_opt_set_int(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
|
||||||
|
|
||||||
e = swr_init(swr);
|
|
||||||
if (e < 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"swr_init: %d (icl=%d ocl=%d isr=%d osr=%d isf=%d osf=%d\n",
|
|
||||||
-e, (int) c->channel_layout,
|
|
||||||
(int) out_channel_layout, c->sample_rate, out_sample_rate, c->sample_fmt, AV_SAMPLE_FMT_S16);
|
|
||||||
swr_free(&swr);
|
|
||||||
swr = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *output = NULL;
|
|
||||||
int in_samples = decoded_frame->nb_samples;
|
|
||||||
int out_samples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + in_samples, out_sample_rate, c->sample_rate, AV_ROUND_UP);
|
|
||||||
e = av_samples_alloc(&output, NULL, out_channels, out_samples, AV_SAMPLE_FMT_S16, 1);
|
|
||||||
if (e < 0) {
|
|
||||||
fprintf(stderr, "av_samples_alloc: %d\n", -e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// FIXME. PTS calculation is probably broken.
|
|
||||||
int64_t next_in_pts = av_rescale(av_frame_get_best_effort_timestamp(decoded_frame),
|
|
||||||
((AVStream *) audioTrack->stream)->time_base.num * (int64_t) out_sample_rate * c->sample_rate,
|
|
||||||
((AVStream *) audioTrack->stream)->time_base.den);
|
|
||||||
int64_t next_out_pts = av_rescale(swr_next_pts(swr, next_in_pts),
|
|
||||||
((AVStream *) audioTrack->stream)->time_base.den,
|
|
||||||
((AVStream *) audioTrack->stream)->time_base.num * (int64_t) out_sample_rate * c->sample_rate);
|
|
||||||
currentAudioPts = audioTrack->pts = pts = calcPts(audioTrack->stream, next_out_pts);
|
|
||||||
out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **)
|
|
||||||
&decoded_frame->data[0], in_samples);
|
|
||||||
|
|
||||||
avOut.uSampleRate = out_sample_rate;
|
|
||||||
avOut.uNoOfChannels = av_get_channel_layout_nb_channels(out_channel_layout);
|
|
||||||
avOut.uBitsPerSample = 16;
|
|
||||||
avOut.bLittleEndian = 1;
|
|
||||||
|
|
||||||
avOut.data = output;
|
|
||||||
avOut.len = out_samples * sizeof(short) * out_channels;
|
|
||||||
|
|
||||||
avOut.pts = videoTrack ? pts : 0;
|
|
||||||
avOut.type = "audio";
|
|
||||||
avOut.stream = audioTrack->stream;
|
|
||||||
avOut.avfc = avContext;
|
|
||||||
|
|
||||||
if (context->output->audio->Write(context, &avOut) < 0)
|
|
||||||
ffmpeg_err("writing data to audio device failed\n");
|
|
||||||
av_freep(&output);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
avOut.data = packet_data;
|
avOut.data = packet_data;
|
||||||
avOut.len = packet_size;
|
avOut.len = packet_size;
|
||||||
avOut.pts = pts;
|
avOut.pts = pts;
|
||||||
|
avOut.packet = &packet;
|
||||||
avOut.type = "audio";
|
avOut.type = "audio";
|
||||||
avOut.stream = audioTrack->stream;
|
avOut.stream = audioTrack->stream;
|
||||||
avOut.avfc = avContext;
|
avOut.avfc = avContext;
|
||||||
@@ -544,7 +421,7 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
float duration = 3.0;
|
float duration = 3.0;
|
||||||
ffmpeg_printf(100, "subtitleTrack->stream %p \n", subtitleTrack->stream);
|
ffmpeg_printf(100, "subtitleTrack->stream %p \n", subtitleTrack->stream);
|
||||||
|
|
||||||
pts = calcPts(subtitleTrack->stream, packet.pts);
|
pts = calcPts(avContext, subtitleTrack->stream, packet.pts);
|
||||||
|
|
||||||
if (duration > 0.0) {
|
if (duration > 0.0) {
|
||||||
/* is there a decoder ? */
|
/* is there a decoder ? */
|
||||||
@@ -586,11 +463,6 @@ static void FFMPEGThread(Context_t * context)
|
|||||||
|
|
||||||
dvbsub_ass_clear();
|
dvbsub_ass_clear();
|
||||||
|
|
||||||
if (swr)
|
|
||||||
swr_free(&swr);
|
|
||||||
if (decoded_frame)
|
|
||||||
av_frame_free(&decoded_frame);
|
|
||||||
|
|
||||||
if (context->playback)
|
if (context->playback)
|
||||||
context->playback->abortPlayback = 1;
|
context->playback->abortPlayback = 1;
|
||||||
hasPlayThreadStarted = 0;
|
hasPlayThreadStarted = 0;
|
||||||
@@ -903,11 +775,6 @@ int container_ffmpeg_update_tracks(Context_t * context, char *filename)
|
|||||||
track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0;
|
track.duration = (double) stream->duration * av_q2d(stream->time_base) * 1000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(encoding, "A_IPCM", 6)) {
|
|
||||||
track.inject_as_pcm = 1;
|
|
||||||
ffmpeg_printf(10, " Handle inject_as_pcm = %d\n", track.inject_as_pcm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context->manager->audio) {
|
if (context->manager->audio) {
|
||||||
if (context->manager->audio->Command(context, MANAGER_ADD, &track) < 0) {
|
if (context->manager->audio->Command(context, MANAGER_ADD, &track) < 0) {
|
||||||
/* konfetti: fixme: is this a reason to return with error? */
|
/* konfetti: fixme: is this a reason to return with error? */
|
||||||
|
@@ -6,12 +6,14 @@
|
|||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct Context_s {
|
typedef struct Context_s {
|
||||||
PlaybackHandler_t *playback;
|
PlaybackHandler_t *playback;
|
||||||
ContainerHandler_t *container;
|
ContainerHandler_t *container;
|
||||||
OutputHandler_t *output;
|
OutputHandler_t *output;
|
||||||
ManagerHandler_t *manager;
|
ManagerHandler_t *manager;
|
||||||
|
int64_t *currentAudioPtsP;
|
||||||
} Context_t;
|
} Context_t;
|
||||||
|
|
||||||
int container_ffmpeg_update_tracks(Context_t * context, char *filename);
|
int container_ffmpeg_update_tracks(Context_t * context, char *filename);
|
||||||
|
@@ -38,8 +38,8 @@ typedef struct Track_s {
|
|||||||
char *language;
|
char *language;
|
||||||
|
|
||||||
/* length of track */
|
/* length of track */
|
||||||
uint64_t duration;
|
int64_t duration;
|
||||||
uint64_t pts;
|
//CHECK int64_t pts;
|
||||||
|
|
||||||
/* context from ffmpeg */
|
/* context from ffmpeg */
|
||||||
AVFormatContext *avfc;
|
AVFormatContext *avfc;
|
||||||
@@ -53,7 +53,6 @@ typedef struct Track_s {
|
|||||||
|
|
||||||
/* If player2 or the elf do not support decoding of audio codec set this.
|
/* If player2 or the elf do not support decoding of audio codec set this.
|
||||||
* AVCodec is than used for softdecoding and stream will be injected as PCM */
|
* AVCodec is than used for softdecoding and stream will be injected as PCM */
|
||||||
int inject_as_pcm;
|
|
||||||
int inject_raw_pcm;
|
int inject_raw_pcm;
|
||||||
|
|
||||||
int pending;
|
int pending;
|
||||||
|
@@ -42,15 +42,15 @@ typedef struct {
|
|||||||
int uSampleRate;
|
int uSampleRate;
|
||||||
int uBitsPerSample;
|
int uBitsPerSample;
|
||||||
int bLittleEndian;
|
int bLittleEndian;
|
||||||
|
int restart_audio_resampling;
|
||||||
|
|
||||||
uint64_t pts;
|
int64_t pts;
|
||||||
|
|
||||||
char *type;
|
char *type;
|
||||||
|
|
||||||
/* context from ffmpeg */
|
|
||||||
AVFormatContext *avfc;
|
AVFormatContext *avfc;
|
||||||
/* stream from ffmpeg */
|
|
||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
|
AVPacket *packet;
|
||||||
} AudioVideoOut_t;
|
} AudioVideoOut_t;
|
||||||
|
|
||||||
struct Context_s;
|
struct Context_s;
|
||||||
|
@@ -12,19 +12,23 @@
|
|||||||
|
|
||||||
typedef enum { eNone, eAudio, eVideo, eGfx } eWriterType_t;
|
typedef enum { eNone, eAudio, eVideo, eGfx } eWriterType_t;
|
||||||
|
|
||||||
|
struct Context_s;
|
||||||
|
typedef struct Context_s Context_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int fd;
|
int fd;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
uint64_t Pts;
|
int64_t Pts;
|
||||||
int uNoOfChannels;
|
int uNoOfChannels;
|
||||||
int uSampleRate;
|
int uSampleRate;
|
||||||
int uBitsPerSample;
|
int uBitsPerSample;
|
||||||
int bLittleEndian;
|
int bLittleEndian;
|
||||||
/* context from ffmpeg */
|
int restart_audio_resampling;
|
||||||
AVFormatContext *avfc;
|
AVFormatContext *avfc;
|
||||||
/* stream from ffmpeg */
|
|
||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
|
AVPacket *packet;
|
||||||
|
Context_t *context;
|
||||||
} WriterAVCallData_t;
|
} WriterAVCallData_t;
|
||||||
|
|
||||||
typedef struct WriterCaps_s {
|
typedef struct WriterCaps_s {
|
||||||
|
@@ -940,6 +940,7 @@ static int Write(Context_t *context, AudioVideoOut_t *out)
|
|||||||
call.data = out->data;
|
call.data = out->data;
|
||||||
call.len = out->len;
|
call.len = out->len;
|
||||||
call.Pts = out->pts;
|
call.Pts = out->pts;
|
||||||
|
call.packet = out->packet;
|
||||||
|
|
||||||
if (writer->writeData)
|
if (writer->writeData)
|
||||||
res = writer->writeData(&call);
|
res = writer->writeData(&call);
|
||||||
@@ -977,12 +978,14 @@ static int Write(Context_t *context, AudioVideoOut_t *out)
|
|||||||
call.data = out->data;
|
call.data = out->data;
|
||||||
call.len = out->len;
|
call.len = out->len;
|
||||||
call.Pts = out->pts;
|
call.Pts = out->pts;
|
||||||
|
call.packet = out->packet;
|
||||||
|
|
||||||
call.uNoOfChannels = out->uNoOfChannels;
|
call.uNoOfChannels = out->uNoOfChannels;
|
||||||
call.uSampleRate = out->uSampleRate;
|
call.uSampleRate = out->uSampleRate;
|
||||||
call.uBitsPerSample = out->uBitsPerSample;
|
call.uBitsPerSample = out->uBitsPerSample;
|
||||||
call.bLittleEndian = out->bLittleEndian;
|
call.bLittleEndian = out->bLittleEndian;
|
||||||
|
call.restart_audio_resampling = out->restart_audio_resampling;
|
||||||
|
call.context = context;
|
||||||
|
|
||||||
if (writer->writeData)
|
if (writer->writeData)
|
||||||
res = writer->writeData(&call);
|
res = writer->writeData(&call);
|
||||||
|
@@ -289,34 +289,187 @@ static int writeData(WriterAVCallData_t *call)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SwrContext *swr = NULL;
|
||||||
|
AVFrame *decoded_frame = NULL;
|
||||||
|
int out_sample_rate = 44100;
|
||||||
|
int out_channels = 2;
|
||||||
|
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||||
|
int restart_audio_resampling = 0;
|
||||||
|
|
||||||
|
static int resetIpcm()
|
||||||
|
{
|
||||||
|
if (swr)
|
||||||
|
swr_free(&swr);
|
||||||
|
if (decoded_frame)
|
||||||
|
av_frame_free(&decoded_frame);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t calcPts(AVFormatContext *, AVStream *, int64_t);
|
||||||
|
|
||||||
|
static int writeDataIpcm(WriterAVCallData_t *call)
|
||||||
|
{
|
||||||
|
AVCodecContext *c = call->stream->codec;
|
||||||
|
AVPacket *packet = call->packet;
|
||||||
|
uint8_t *packet_data = packet->data;
|
||||||
|
unsigned int packet_size = packet->size;
|
||||||
|
|
||||||
|
if (call->restart_audio_resampling)
|
||||||
|
call->restart_audio_resampling = 1;
|
||||||
|
|
||||||
|
if (restart_audio_resampling) {
|
||||||
|
restart_audio_resampling = 0;
|
||||||
|
if (swr) {
|
||||||
|
swr_free(&swr);
|
||||||
|
swr = NULL;
|
||||||
|
}
|
||||||
|
if (decoded_frame) {
|
||||||
|
av_frame_free(&decoded_frame);
|
||||||
|
decoded_frame = NULL;
|
||||||
|
}
|
||||||
|
call->context->output->Command(call->context, OUTPUT_CLEAR, NULL);
|
||||||
|
call->context->output->Command(call->context, OUTPUT_PLAY, NULL);
|
||||||
|
|
||||||
|
AVCodec *codec = avcodec_find_decoder(c->codec_id);
|
||||||
|
|
||||||
|
if (!codec || avcodec_open2(c, codec, NULL))
|
||||||
|
fprintf(stderr, "%s %d: avcodec_open2 failed\n", __func__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (packet_size > 0) {
|
||||||
|
int got_frame = 0;
|
||||||
|
if (!decoded_frame) {
|
||||||
|
if (!(decoded_frame = av_frame_alloc())) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
av_frame_unref(decoded_frame);
|
||||||
|
|
||||||
|
int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, packet);
|
||||||
|
if (len < 0) {
|
||||||
|
restart_audio_resampling = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_data += len;
|
||||||
|
packet_size -= len;
|
||||||
|
|
||||||
|
if (!got_frame)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int e;
|
||||||
|
if (!swr) {
|
||||||
|
int rates[] = { 48000, 96000, 192000, 44100, 88200, 176400, 0 };
|
||||||
|
int *rate = rates;
|
||||||
|
int in_rate = c->sample_rate;
|
||||||
|
while (*rate && ((*rate / in_rate) * in_rate != *rate) && (in_rate / *rate) * *rate != in_rate)
|
||||||
|
rate++;
|
||||||
|
out_sample_rate = *rate ? *rate : 44100;
|
||||||
|
swr = swr_alloc();
|
||||||
|
out_channels = c->channels;
|
||||||
|
if (c->channel_layout == 0) {
|
||||||
|
// FIXME -- need to guess, looks pretty much like a bug in the FFMPEG WMA decoder
|
||||||
|
c->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_channel_layout = c->channel_layout;
|
||||||
|
// player2 won't play mono
|
||||||
|
if (out_channel_layout == AV_CH_LAYOUT_MONO) {
|
||||||
|
out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||||
|
out_channels = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_opt_set_int(swr, "in_channel_layout", c->channel_layout, 0);
|
||||||
|
av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
|
||||||
|
av_opt_set_int(swr, "in_sample_rate", c->sample_rate, 0);
|
||||||
|
av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
|
||||||
|
av_opt_set_int(swr, "in_sample_fmt", c->sample_fmt, 0);
|
||||||
|
av_opt_set_int(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
|
||||||
|
|
||||||
|
e = swr_init(swr);
|
||||||
|
if (e < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"swr_init: %d (icl=%d ocl=%d isr=%d osr=%d isf=%d osf=%d\n",
|
||||||
|
-e, (int) c->channel_layout,
|
||||||
|
(int) out_channel_layout, c->sample_rate, out_sample_rate, c->sample_fmt, AV_SAMPLE_FMT_S16);
|
||||||
|
swr_free(&swr);
|
||||||
|
swr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *output = NULL;
|
||||||
|
int in_samples = decoded_frame->nb_samples;
|
||||||
|
int out_samples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + in_samples, out_sample_rate, c->sample_rate, AV_ROUND_UP);
|
||||||
|
e = av_samples_alloc(&output, NULL, out_channels, out_samples, AV_SAMPLE_FMT_S16, 1);
|
||||||
|
if (e < 0) {
|
||||||
|
fprintf(stderr, "av_samples_alloc: %d\n", -e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// FIXME. PTS calculation is probably broken.
|
||||||
|
int64_t pts;
|
||||||
|
int64_t next_in_pts = av_rescale(av_frame_get_best_effort_timestamp(decoded_frame),
|
||||||
|
call->stream->time_base.num * (int64_t) out_sample_rate * c->sample_rate,
|
||||||
|
call->stream->time_base.den);
|
||||||
|
int64_t next_out_pts = av_rescale(swr_next_pts(swr, next_in_pts),
|
||||||
|
call->stream->time_base.den,
|
||||||
|
call->stream->time_base.num * (int64_t) out_sample_rate * c->sample_rate);
|
||||||
|
*(call->context->currentAudioPtsP) = /* audioTrack->pts = */ pts = calcPts(call->avfc, call->stream, next_out_pts);
|
||||||
|
out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **)
|
||||||
|
&decoded_frame->data[0], in_samples);
|
||||||
|
|
||||||
|
WriterAVCallData_t pcmOut;
|
||||||
|
pcmOut.fd = call->fd;
|
||||||
|
pcmOut.uSampleRate = out_sample_rate;
|
||||||
|
pcmOut.uNoOfChannels = av_get_channel_layout_nb_channels(out_channel_layout);
|
||||||
|
pcmOut.uBitsPerSample = 16;
|
||||||
|
pcmOut.bLittleEndian = 1;
|
||||||
|
|
||||||
|
pcmOut.data = output;
|
||||||
|
pcmOut.len = out_samples * sizeof(short) * out_channels;
|
||||||
|
|
||||||
|
pcmOut.Pts = pts; // FIXME videoTrack ? pts : 0;
|
||||||
|
pcmOut.stream = call->stream;
|
||||||
|
pcmOut.avfc = call->avfc;
|
||||||
|
pcmOut.packet = NULL;
|
||||||
|
|
||||||
|
writeData(&pcmOut);
|
||||||
|
|
||||||
|
av_freep(&output);
|
||||||
|
}
|
||||||
|
return packet->size;
|
||||||
|
}
|
||||||
|
|
||||||
/* ***************************** */
|
/* ***************************** */
|
||||||
/* Writer Definition */
|
/* Writer Definition */
|
||||||
/* ***************************** */
|
/* ***************************** */
|
||||||
|
|
||||||
static WriterCaps_t caps_pcm = {
|
static WriterCaps_t caps_pcm = {
|
||||||
"pcm",
|
"pcm",
|
||||||
eAudio,
|
eAudio,
|
||||||
"A_PCM",
|
"A_PCM",
|
||||||
AUDIO_ENCODING_LPCMA
|
AUDIO_ENCODING_LPCMA
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Writer_s WriterAudioPCM = {
|
struct Writer_s WriterAudioPCM = {
|
||||||
&reset,
|
&reset,
|
||||||
&writeData,
|
&writeData,
|
||||||
NULL,
|
NULL,
|
||||||
&caps_pcm
|
&caps_pcm
|
||||||
};
|
};
|
||||||
|
|
||||||
static WriterCaps_t caps_ipcm = {
|
static WriterCaps_t caps_ipcm = {
|
||||||
"ipcm",
|
"ipcm",
|
||||||
eAudio,
|
eAudio,
|
||||||
"A_IPCM",
|
"A_IPCM",
|
||||||
AUDIO_ENCODING_LPCMA
|
AUDIO_ENCODING_LPCMA
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Writer_s WriterAudioIPCM = {
|
struct Writer_s WriterAudioIPCM = {
|
||||||
&reset,
|
&resetIpcm,
|
||||||
&writeData,
|
&writeDataIpcm,
|
||||||
NULL,
|
NULL,
|
||||||
&caps_ipcm
|
&caps_ipcm
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user