Bugfixes: - Previous seqaudio-patch broke mixdown functionality (no need to init SDL on mixdown case... || -> && ) - hddaudio-crash at end of track finally fixed.
542 lines
12 KiB
C
542 lines
12 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Contributor(s): Peter Schlaile <peter [at] schlaile [dot] de> 2005
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#ifdef WITH_FFMPEG
|
|
#include <ffmpeg/avformat.h>
|
|
#include <ffmpeg/avcodec.h>
|
|
#include <ffmpeg/rational.h>
|
|
#if LIBAVFORMAT_VERSION_INT < (49 << 16)
|
|
#define FFMPEG_OLD_FRAME_RATE 1
|
|
#else
|
|
#define FFMPEG_CODEC_IS_POINTER 1
|
|
#endif
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BIF_editsound.h"
|
|
|
|
#include "blendef.h"
|
|
|
|
extern void do_init_ffmpeg();
|
|
|
|
struct hdaudio {
|
|
int sample_rate;
|
|
int channels;
|
|
int audioStream;
|
|
|
|
#ifdef WITH_FFMPEG
|
|
char * filename;
|
|
AVCodec *pCodec;
|
|
AVFormatContext *pFormatCtx;
|
|
AVCodecContext *pCodecCtx;
|
|
int frame_position;
|
|
int frame_duration;
|
|
int frame_alloc_duration;
|
|
int decode_pos;
|
|
int frame_size;
|
|
short * decode_cache;
|
|
short * decode_cache_zero;
|
|
short * resample_cache;
|
|
int decode_cache_size;
|
|
int target_channels;
|
|
int target_rate;
|
|
ReSampleContext *resampler;
|
|
#else
|
|
|
|
|
|
#endif
|
|
};
|
|
|
|
#ifdef WITH_FFMPEG
|
|
#ifdef FFMPEG_CODEC_IS_POINTER
|
|
static AVCodecContext* get_codec_from_stream(AVStream* stream)
|
|
{
|
|
return stream->codec;
|
|
}
|
|
#else
|
|
static AVCodecContext* get_codec_from_stream(AVStream* stream)
|
|
{
|
|
return &stream->codec;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
struct hdaudio * sound_open_hdaudio(char * filename)
|
|
{
|
|
#ifdef WITH_FFMPEG
|
|
struct hdaudio * rval;
|
|
int i, audioStream;
|
|
|
|
AVCodec *pCodec;
|
|
AVFormatContext *pFormatCtx;
|
|
AVCodecContext *pCodecCtx;
|
|
|
|
do_init_ffmpeg();
|
|
|
|
if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) {
|
|
return 0;
|
|
}
|
|
|
|
if(av_find_stream_info(pFormatCtx)<0) {
|
|
av_close_input_file(pFormatCtx);
|
|
return 0;
|
|
}
|
|
|
|
dump_format(pFormatCtx, 0, filename, 0);
|
|
|
|
|
|
/* Find the first audio stream */
|
|
audioStream=-1;
|
|
for(i=0; i<pFormatCtx->nb_streams; i++)
|
|
if(get_codec_from_stream(pFormatCtx->streams[i])
|
|
->codec_type == CODEC_TYPE_AUDIO)
|
|
{
|
|
audioStream=i;
|
|
break;
|
|
}
|
|
|
|
if(audioStream == -1) {
|
|
av_close_input_file(pFormatCtx);
|
|
return 0;
|
|
}
|
|
|
|
pCodecCtx = get_codec_from_stream(pFormatCtx->streams[audioStream]);
|
|
|
|
/* Find the decoder for the audio stream */
|
|
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
|
|
if(pCodec == NULL) {
|
|
av_close_input_file(pFormatCtx);
|
|
return 0;
|
|
}
|
|
|
|
if(avcodec_open(pCodecCtx, pCodec)<0) {
|
|
av_close_input_file(pFormatCtx);
|
|
return 0;
|
|
}
|
|
|
|
rval = (struct hdaudio *)MEM_mallocN(sizeof(struct hdaudio),
|
|
"hdaudio struct");
|
|
|
|
rval->filename = strdup(filename);
|
|
rval->sample_rate = pCodecCtx->sample_rate;
|
|
rval->channels = pCodecCtx->channels;
|
|
|
|
rval->pFormatCtx = pFormatCtx;
|
|
rval->pCodecCtx = pCodecCtx;
|
|
rval->pCodec = pCodec;
|
|
rval->audioStream = audioStream;
|
|
rval->frame_position = -10;
|
|
|
|
rval->frame_duration = AV_TIME_BASE / 10;
|
|
rval->frame_alloc_duration = AV_TIME_BASE;
|
|
rval->decode_cache_size =
|
|
(long long) rval->sample_rate * rval->channels
|
|
* rval->frame_alloc_duration / AV_TIME_BASE
|
|
* 2;
|
|
|
|
rval->decode_cache = (short*) MEM_mallocN(
|
|
rval->decode_cache_size * sizeof(short)
|
|
+ AVCODEC_MAX_AUDIO_FRAME_SIZE,
|
|
"hdaudio decode cache");
|
|
rval->decode_pos = 0;
|
|
rval->target_channels = -1;
|
|
rval->target_rate = -1;
|
|
rval->resampler = 0;
|
|
rval->resample_cache = 0;
|
|
return rval;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
struct hdaudio * sound_copy_hdaudio(struct hdaudio * c)
|
|
{
|
|
#ifdef WITH_FFMPEG
|
|
return sound_open_hdaudio(c->filename);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
long sound_hdaudio_get_duration(struct hdaudio * hdaudio, int frame_rate)
|
|
{
|
|
#ifdef WITH_FFMPEG
|
|
return hdaudio->pFormatCtx->duration * frame_rate / AV_TIME_BASE;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#ifdef WITH_FFMPEG
|
|
static void sound_hdaudio_extract_small_block(
|
|
struct hdaudio * hdaudio,
|
|
short * target_buffer,
|
|
int sample_position /* units of target_rate */,
|
|
int target_rate,
|
|
int target_channels,
|
|
int nb_samples /* in target */)
|
|
{
|
|
AVPacket packet;
|
|
int frame_position;
|
|
int frame_size = (long long) target_rate
|
|
* hdaudio->frame_duration / AV_TIME_BASE;
|
|
int in_frame_size = (long long) hdaudio->sample_rate
|
|
* hdaudio->frame_duration / AV_TIME_BASE;
|
|
int rate_conversion =
|
|
(target_rate != hdaudio->sample_rate)
|
|
|| (target_channels != hdaudio->channels);
|
|
int sample_ofs = target_channels * (sample_position % frame_size);
|
|
|
|
frame_position = sample_position / frame_size;
|
|
|
|
if (hdaudio == 0) return;
|
|
|
|
if (rate_conversion) {
|
|
if (hdaudio->resampler &&
|
|
(hdaudio->target_rate != target_rate
|
|
|| hdaudio->target_channels != target_channels)) {
|
|
audio_resample_close(hdaudio->resampler);
|
|
hdaudio->resampler = 0;
|
|
}
|
|
if (!hdaudio->resampler) {
|
|
hdaudio->resampler = audio_resample_init(
|
|
target_channels, hdaudio->channels,
|
|
target_rate, hdaudio->sample_rate);
|
|
hdaudio->target_rate = target_rate;
|
|
hdaudio->target_channels = target_channels;
|
|
if (hdaudio->resample_cache) {
|
|
MEM_freeN(hdaudio->resample_cache);
|
|
}
|
|
|
|
|
|
hdaudio->resample_cache = (short*) MEM_mallocN(
|
|
(long long)
|
|
hdaudio->target_channels
|
|
* frame_size * 2
|
|
* sizeof(short),
|
|
"hdaudio resample cache");
|
|
|
|
if (frame_position == hdaudio->frame_position) {
|
|
audio_resample(hdaudio->resampler,
|
|
hdaudio->resample_cache,
|
|
hdaudio->decode_cache_zero,
|
|
in_frame_size * 7 / 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (frame_position == hdaudio->frame_position + 1
|
|
&& in_frame_size * hdaudio->channels <= hdaudio->decode_pos) {
|
|
int bl_size = in_frame_size * hdaudio->channels;
|
|
int decode_pos = hdaudio->decode_pos;
|
|
|
|
hdaudio->frame_position = frame_position;
|
|
|
|
memcpy(hdaudio->decode_cache,
|
|
hdaudio->decode_cache + bl_size,
|
|
(decode_pos - bl_size) * sizeof(short));
|
|
|
|
decode_pos -= bl_size;
|
|
|
|
memset(hdaudio->decode_cache + decode_pos, 0,
|
|
(hdaudio->decode_cache_size - decode_pos)
|
|
* sizeof(short));
|
|
|
|
|
|
while(av_read_frame(hdaudio->pFormatCtx, &packet) >= 0) {
|
|
int data_size;
|
|
int len;
|
|
uint8_t *audio_pkt_data;
|
|
int audio_pkt_size;
|
|
|
|
if(packet.stream_index != hdaudio->audioStream) {
|
|
av_free_packet(&packet);
|
|
continue;
|
|
}
|
|
|
|
audio_pkt_data = packet.data;
|
|
audio_pkt_size = packet.size;
|
|
|
|
while (audio_pkt_size > 0) {
|
|
len = avcodec_decode_audio(
|
|
hdaudio->pCodecCtx,
|
|
hdaudio->decode_cache
|
|
+ decode_pos,
|
|
&data_size,
|
|
audio_pkt_data,
|
|
audio_pkt_size);
|
|
if (len <= 0) {
|
|
audio_pkt_size = 0;
|
|
break;
|
|
}
|
|
|
|
audio_pkt_size -= len;
|
|
audio_pkt_data += len;
|
|
|
|
if (data_size <= 0) {
|
|
continue;
|
|
}
|
|
|
|
decode_pos += data_size / sizeof(short);
|
|
if (decode_pos + data_size
|
|
/ sizeof(short)
|
|
> hdaudio->decode_cache_size) {
|
|
break;
|
|
}
|
|
}
|
|
av_free_packet(&packet);
|
|
|
|
if (decode_pos + data_size / sizeof(short)
|
|
> hdaudio->decode_cache_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rate_conversion) {
|
|
audio_resample(hdaudio->resampler,
|
|
hdaudio->resample_cache,
|
|
hdaudio->decode_cache_zero,
|
|
in_frame_size * 7 / 4);
|
|
}
|
|
|
|
hdaudio->decode_pos = decode_pos;
|
|
}
|
|
|
|
if (frame_position != hdaudio->frame_position) {
|
|
long decode_pos = 0;
|
|
long long st_time = hdaudio->pFormatCtx
|
|
->streams[hdaudio->audioStream]->start_time;
|
|
double time_base =
|
|
av_q2d(hdaudio->pFormatCtx
|
|
->streams[hdaudio->audioStream]->time_base);
|
|
long long pos = (long long) frame_position * AV_TIME_BASE
|
|
* hdaudio->frame_duration / AV_TIME_BASE;
|
|
|
|
long long seek_pos;
|
|
|
|
hdaudio->frame_position = frame_position;
|
|
|
|
if (st_time == AV_NOPTS_VALUE) {
|
|
st_time = 0;
|
|
}
|
|
|
|
pos += st_time * AV_TIME_BASE * time_base;
|
|
|
|
/* seek a little bit before the target position,
|
|
(ffmpeg seek algorithm doesn't seem to work always as
|
|
specified...)
|
|
*/
|
|
|
|
seek_pos = pos - (AV_TIME_BASE
|
|
* hdaudio->frame_duration
|
|
/ AV_TIME_BASE / 10);
|
|
if (seek_pos < 0) {
|
|
seek_pos = pos;
|
|
}
|
|
|
|
av_seek_frame(hdaudio->pFormatCtx, -1,
|
|
seek_pos,
|
|
AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD);
|
|
avcodec_flush_buffers(hdaudio->pCodecCtx);
|
|
|
|
memset(hdaudio->decode_cache, 0,
|
|
hdaudio->decode_cache_size * sizeof(short));
|
|
|
|
hdaudio->decode_cache_zero = hdaudio->decode_cache;
|
|
|
|
while(av_read_frame(hdaudio->pFormatCtx, &packet) >= 0) {
|
|
int data_size;
|
|
int len;
|
|
uint8_t *audio_pkt_data;
|
|
int audio_pkt_size;
|
|
|
|
if(packet.stream_index != hdaudio->audioStream) {
|
|
av_free_packet(&packet);
|
|
continue;
|
|
}
|
|
|
|
audio_pkt_data = packet.data;
|
|
audio_pkt_size = packet.size;
|
|
|
|
if (!hdaudio->decode_cache_zero
|
|
&& audio_pkt_size > 0) {
|
|
long long diff;
|
|
|
|
if (packet.pts == AV_NOPTS_VALUE) {
|
|
fprintf(stderr,
|
|
"hdaudio: audio "
|
|
"pts=NULL audio "
|
|
"distortion!\n");
|
|
diff = 0;
|
|
} else {
|
|
long long pts = packet.pts;
|
|
long long spts = (long long) (
|
|
pos / time_base / AV_TIME_BASE
|
|
+ 0.5);
|
|
diff = spts - pts;
|
|
if (diff < 0) {
|
|
fprintf(stderr,
|
|
"hdaudio: "
|
|
"negative seek: "
|
|
"%lld < %lld "
|
|
"(pos=%lld) "
|
|
"audio distortion!!\n",
|
|
spts, pts, pos);
|
|
diff = 0;
|
|
}
|
|
}
|
|
|
|
|
|
diff *= hdaudio->sample_rate * time_base;
|
|
diff *= hdaudio->channels;
|
|
|
|
if (diff > hdaudio->decode_cache_size / 2) {
|
|
fprintf(stderr,
|
|
"hdaudio: audio "
|
|
"diff too large!!\n");
|
|
diff = 0;
|
|
}
|
|
|
|
hdaudio->decode_cache_zero
|
|
= hdaudio->decode_cache + diff;
|
|
}
|
|
|
|
while (audio_pkt_size > 0) {
|
|
len = avcodec_decode_audio(
|
|
hdaudio->pCodecCtx,
|
|
hdaudio->decode_cache
|
|
+ decode_pos,
|
|
&data_size,
|
|
audio_pkt_data,
|
|
audio_pkt_size);
|
|
if (len <= 0) {
|
|
audio_pkt_size = 0;
|
|
break;
|
|
}
|
|
|
|
audio_pkt_size -= len;
|
|
audio_pkt_data += len;
|
|
|
|
if (data_size <= 0) {
|
|
continue;
|
|
}
|
|
|
|
decode_pos += data_size / sizeof(short);
|
|
if (decode_pos + data_size
|
|
/ sizeof(short)
|
|
> hdaudio->decode_cache_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
av_free_packet(&packet);
|
|
|
|
if (decode_pos + data_size / sizeof(short)
|
|
> hdaudio->decode_cache_size) {
|
|
break;
|
|
}
|
|
}
|
|
if (rate_conversion) {
|
|
audio_resample(hdaudio->resampler,
|
|
hdaudio->resample_cache,
|
|
hdaudio->decode_cache_zero,
|
|
in_frame_size * 7 / 4);
|
|
}
|
|
hdaudio->decode_pos = decode_pos;
|
|
}
|
|
|
|
memcpy(target_buffer, (rate_conversion
|
|
? hdaudio->resample_cache
|
|
: hdaudio->decode_cache_zero) + sample_ofs,
|
|
nb_samples * target_channels * sizeof(short));
|
|
}
|
|
#endif
|
|
|
|
|
|
void sound_hdaudio_extract(struct hdaudio * hdaudio,
|
|
short * target_buffer,
|
|
int sample_position /* units of target_rate */,
|
|
int target_rate,
|
|
int target_channels,
|
|
int nb_samples /* in target */)
|
|
{
|
|
#ifdef WITH_FFMPEG
|
|
long long max_samples = (long long) target_rate
|
|
* hdaudio->frame_duration / AV_TIME_BASE / 4;
|
|
|
|
while (nb_samples > max_samples) {
|
|
sound_hdaudio_extract_small_block(hdaudio, target_buffer,
|
|
sample_position,
|
|
target_rate,
|
|
target_channels,
|
|
max_samples);
|
|
target_buffer += max_samples * target_channels;
|
|
sample_position += max_samples;
|
|
nb_samples -= max_samples;
|
|
}
|
|
if (nb_samples > 0) {
|
|
sound_hdaudio_extract_small_block(hdaudio, target_buffer,
|
|
sample_position,
|
|
target_rate,
|
|
target_channels,
|
|
nb_samples);
|
|
}
|
|
#else
|
|
|
|
#endif
|
|
}
|
|
|
|
void sound_close_hdaudio(struct hdaudio * hdaudio)
|
|
{
|
|
#ifdef WITH_FFMPEG
|
|
|
|
if (hdaudio) {
|
|
avcodec_close(hdaudio->pCodecCtx);
|
|
av_close_input_file(hdaudio->pFormatCtx);
|
|
MEM_freeN (hdaudio->decode_cache);
|
|
if (hdaudio->resample_cache) {
|
|
MEM_freeN(hdaudio->resample_cache);
|
|
}
|
|
free(hdaudio->filename);
|
|
MEM_freeN (hdaudio);
|
|
}
|
|
#else
|
|
|
|
#endif
|
|
}
|