ref: fa036e6261e0eeabbe9ef410abc5acf75cba2ff5
parent: d79790588cb4a863b20d8303bf4df10bfb02d11f
author: menno <menno>
date: Mon Dec 15 12:47:56 EST 2003
foo_mp4 updates
--- a/plugins/foo_mp4/input_aac.cpp
+++ b/plugins/foo_mp4/input_aac.cpp
@@ -22,11 +22,11 @@
** Commercial non-GPL licensing of this software is possible.
** For more info contact Ahead Software through Mpeg4AAClicense@nero.com.
**
-** $Id: input_aac.cpp,v 1.3 2003/12/14 16:47:32 menno Exp $
+** $Id: input_aac.cpp,v 1.4 2003/12/15 17:47:56 menno Exp $
**/
-#include "../SDK/foobar2000.h"
-#include "../foo_input_std/id3v2_hacks.h"
+#include "foobar2000/SDK/foobar2000.h"
+#include "foobar2000/foo_input_std/id3v2_hacks.h"
#include <faad.h>
//#define DBG_OUT(A) OutputDebugString(A)
--- a/plugins/foo_mp4/mp4_parser.cpp
+++ b/plugins/foo_mp4/mp4_parser.cpp
@@ -1,4 +1,4 @@
-#include "../SDK/foobar2000.h"
+#include "foobar2000/SDK/foobar2000.h"
#define USE_TAGGING
#include <mp4ff.h>
@@ -97,21 +97,35 @@
packet_decoder * p_decoder;
- unsigned char *buffer;
- unsigned int buffer_size;
-
/* for gapless decoding */
mp4ff_callback_t mp4cb;
double m_length;
- uint32_t m_timescale;
- int m_skip_samples;
- uint32_t m_skip_frames;
-
+ unsigned m_timescale;
+ unsigned m_skip_samples;
+ unsigned m_skip_frames;
+ double m_timescale_div;
unsigned m_expected_sample_rate,m_expected_channels;
+
+ double m_vbr_last_duration;
+ unsigned m_vbr_update_frames,m_vbr_update_bytes;
+ double m_vbr_update_time;
+ bool m_vbr_update_enabled;
+ unsigned m_vbr_update_interval;
+
+ int64_t duration_to_samples(int64_t val)
+ {
+ return (int64_t) ( (double)val * (double) m_expected_sample_rate * m_timescale_div + 0.5);
+ }
+
+ int64_t samples_to_duration(int64_t val)
+ {
+ return (int64_t) ( (double)val * (double)m_timescale / (double)m_expected_sample_rate + 0.5);
+ }
+
audio_chunk_i m_tempchunk;
static uint32_t callback_read(void *udata, void *buffer, uint32_t length)
@@ -134,6 +148,14 @@
return reinterpret_cast<input_mp4*>(udata)->mp4file->set_eof() ? 1 : 0;
}
+ void vbr_update_reset()
+ {
+ m_vbr_update_frames = 0; m_vbr_update_bytes = 0;
+ m_vbr_update_time = 0;
+ m_vbr_update_enabled = true;//make this configurable ?
+ m_vbr_update_interval = 16;
+ }
+
void cleanup()
{
if (infile) {mp4ff_close(infile);infile=0;}
@@ -142,6 +164,7 @@
p_decoder->service_release();
p_decoder = 0;
}
+ vbr_update_reset();
}
input_mp4()
@@ -157,6 +180,9 @@
infile = 0;
m_offset = 0;
m_skip_samples = 0;
+
+ vbr_update_reset();
+
}
~input_mp4()
@@ -169,6 +195,57 @@
return (!stricmp(ext,"MP4") || !stricmp(ext,"M4A"));
}
+ bool read_mp4_track_info(file_info * info)
+ {
+ m_expected_sample_rate = mp4ff_get_sample_rate(infile,track);
+ m_expected_channels = mp4ff_get_channel_count(infile,track);
+
+ if (m_expected_sample_rate == 0)
+ {
+ console::error("Invalid MP4 sample rate.");
+ return false;
+ }
+
+ if (m_expected_channels == 0)
+ {
+ console::error("Invalid MP4 channel count.");
+ return false;
+ }
+ m_timescale = mp4ff_time_scale(infile,track);
+
+ if (m_timescale == 0)
+ {
+ console::error("Invalid MP4 time scale.");
+ return false;
+ }
+/* if (m_timescale != m_expected_sample_rate)
+ {
+ console::error("Different sample rate / time scales not supported.");
+ return false;
+ }*/
+
+ m_timescale_div = 1.0 / (double) m_timescale;
+
+ {
+ int64_t duration = mp4ff_get_track_duration_use_offsets(infile,track);
+ if (duration == -1)
+ m_length = -1.0;
+ else
+ {
+ m_length = (double)duration * m_timescale_div;
+ }
+ }
+
+ info->set_length(m_length);
+
+ info->info_set_int("bitrate",(mp4ff_get_avg_bitrate(infile,track) + 500) / 1000);
+ info->info_set_int("channels",m_expected_channels);
+ info->info_set_int("samplerate",m_expected_sample_rate);
+// info->info_set_int("mp4_timescale",m_timescale);
+
+ return true;
+ }
+
virtual bool open(reader *r, file_info *info, unsigned flags)
{
if (!r->can_seek())
@@ -189,7 +266,7 @@
{
cleanup();
console::error("Error parsing MP4 file.");
- return 0;
+ return false;
}
if ((track = find_track_to_decode(infile,codecname)) < 0)
@@ -196,18 +273,27 @@
{
cleanup();
console::error("Unable to find correct sound track in the MP4 file.");
- return 0;
+ return false;
}
+ info->info_set("codec",codecname);
+
+ if (!read_mp4_track_info(info))
+ {
+ cleanup();
+ return false;
+ }
+
p_decoder = packet_decoder::create(codecname);
if (p_decoder == 0)
{
cleanup();
console::error("Unable to find correct packet decoder object.");
- return 0;
+ return false;
}
- info->info_set("codec",codecname);
+ unsigned char *buffer;
+ unsigned int buffer_size;
buffer = NULL;
buffer_size = 0;
@@ -218,51 +304,31 @@
if (buffer) free(buffer);
cleanup();
console::error("Error initializing decoder.");
- return 0;
+ return false;
}
- m_timescale = mp4ff_time_scale(infile,track);
+ m_expected_channels = (unsigned)info->info_get_int("channels");
+ m_expected_sample_rate = (unsigned)info->info_get_int("samplerate");
+ if (m_expected_channels==0 || m_expected_sample_rate==0)
+ {
+ cleanup();
+ console::error("Decoder returned invalid info.");
+ return false;
+ }
+
if (buffer)
{
free(buffer);
}
- {
- m_expected_sample_rate = mp4ff_get_sample_rate(infile,track);
- m_expected_channels = mp4ff_get_channel_count(infile,track);
- if (m_timescale != m_expected_sample_rate)
- {
- cleanup();
- console::error("Different sample rate / time scales not supported.");
- return 0;
- }
+ {
char *tag = NULL, *item = NULL;
int k, j;
-
- if (m_timescale<=0)
- m_length = -1.0;
- else
- {
- int64_t duration = mp4ff_get_track_duration_use_offsets(infile,track);
- if (duration == -1)
- m_length = -1.0;
- else
- {
- m_length = (double)duration / (double)m_timescale;
- }
- }
-
- info->set_length(m_length);
-
-
if (flags & OPEN_FLAG_GET_INFO)
{
- info->info_set_int("bitrate",(mp4ff_get_avg_bitrate(infile,track) + 500) / 1000);
- info->info_set_int("channels",m_expected_channels);
- info->info_set_int("samplerate",m_expected_sample_rate);
j = mp4ff_meta_get_num_items(infile);
for (k = 0; k < j; k++)
@@ -286,6 +352,8 @@
m_offset = 0;
+ vbr_update_reset();
+
sampleId = 0;
return 1;
@@ -305,6 +373,9 @@
do
{
+ unsigned char *buffer;
+ unsigned int buffer_size;
+
/* get acces unit from MP4 file */
buffer = NULL;
buffer_size = 0;
@@ -329,8 +400,8 @@
if (m_skip_frames>0) m_skip_frames--;
else
{
- unsigned offset = mp4ff_get_sample_offset(infile,track,sampleId);
- unsigned duration = mp4ff_get_sample_duration(infile, track, sampleId);
+ unsigned offset = (unsigned)duration_to_samples(mp4ff_get_sample_offset(infile,track,sampleId));
+ unsigned duration = (unsigned)duration_to_samples(mp4ff_get_sample_duration(infile, track, sampleId));
// console::info(uStringPrintf("duration: %u, offset: %u",duration,offset));
if (m_tempchunk.is_empty())
@@ -345,6 +416,10 @@
}
else
{
+ m_vbr_update_frames++;
+ m_vbr_update_bytes += buffer_size;
+ m_vbr_update_time += (m_vbr_last_duration = m_tempchunk.get_duration());
+
if (m_tempchunk.get_srate() != m_expected_sample_rate)
{
cleanup();
@@ -351,12 +426,12 @@
console::error(uStringPrintf("Expected sample rate: %u, got: %u.",m_expected_sample_rate,m_tempchunk.get_srate()));
return -1;
}
- if (m_tempchunk.get_channels() != m_expected_channels)
+/* if (m_tempchunk.get_channels() != m_expected_channels)
{
cleanup();
console::error(uStringPrintf("Expected channels: %u, got: %u.",m_expected_channels,m_tempchunk.get_channels()));
return -1;
- }
+ }*/
}
unsigned samplerate,channels,decoded_sample_count;
@@ -499,11 +574,13 @@
sampleId = dest_sample;
- m_skip_samples = skip_samples;
+ m_skip_samples = (unsigned)duration_to_samples(skip_samples);
m_offset = 0;
p_decoder->reset_after_seek();
+
+ vbr_update_reset();
return true;
@@ -513,6 +590,30 @@
{
return !stricmp(type, "audio/mp4") || !stricmp(type, "audio/x-mp4");
}
+
+ virtual bool get_dynamic_info(file_info * out,double * timestamp_delta,bool * b_track_change)
+ {
+ bool ret = false;
+ if (m_vbr_update_enabled)
+ {
+ if (m_vbr_update_time > 0 && m_vbr_update_frames >= m_vbr_update_interval)
+ {
+ double delay = - (m_vbr_update_time - m_vbr_last_duration);
+ int val = (int) ( ((double)m_vbr_update_bytes * 8.0 / m_vbr_update_time + 500.0) / 1000.0 );
+ if (val != out->info_get_bitrate_vbr())
+ {
+ *timestamp_delta = delay;
+ out->info_set_bitrate_vbr(val);
+ ret = true;
+ }
+ m_vbr_update_frames = 0; m_vbr_update_bytes = 0;
+ m_vbr_update_time = 0;
+ }
+ }
+
+ return ret;
+ }
+
};
static service_factory_t<input, input_mp4> foo_mp4;
\ No newline at end of file
--- a/plugins/foo_mp4/packet_decoder_aac.cpp
+++ b/plugins/foo_mp4/packet_decoder_aac.cpp
@@ -1,4 +1,4 @@
-#include "../SDK/foobar2000.h"
+#include "foobar2000/SDK/foobar2000.h"
#include <faad.h>
@@ -61,6 +61,9 @@
return false;
}
+ info->info_set_int("samplerate",t_samplerate);
+ info->info_set_int("channels",t_channels);
+
{
mp4AudioSpecificConfig mp4ASC;
if (AudioSpecificConfig((unsigned char*)data, bytes, &mp4ASC) >= 0)
@@ -87,7 +90,7 @@
virtual bool decode(const void * buffer,unsigned bytes,audio_chunk * out)
{
- const audio_sample * sample_buffer = (const audio_sample*)faacDecDecode(hDecoder, &frameInfo, (unsigned char*)buffer, bytes);
+ audio_sample * sample_buffer = (audio_sample*)faacDecDecode(hDecoder, &frameInfo, (unsigned char*)buffer, bytes);
if (frameInfo.error > 0)
{
@@ -105,6 +108,27 @@
if (frameInfo.samples)
{
+ if (frameInfo.channels == 6 && frameInfo.num_lfe_channels)
+ {
+ //channel order for 5.1: L/R/C/LF/BL/BR
+ audio_sample r1, r2, r3, r4, r5, r6;
+ for (unsigned int i = 0; i < frameInfo.samples; i += frameInfo.channels)
+ {
+ r1 = sample_buffer[i];
+ r2 = sample_buffer[i+1];
+ r3 = sample_buffer[i+2];
+ r4 = sample_buffer[i+3];
+ r5 = sample_buffer[i+4];
+ r6 = sample_buffer[i+5];
+ sample_buffer[i] = r2;
+ sample_buffer[i+1] = r3;
+ sample_buffer[i+2] = r1;
+ sample_buffer[i+3] = r6;
+ sample_buffer[i+4] = r4;
+ sample_buffer[i+5] = r5;
+ }
+ }
+
return out->set_data(sample_buffer,frameInfo.samples / frameInfo.channels,frameInfo.channels,frameInfo.samplerate);
}
else