summaryrefslogtreecommitdiffstats
path: root/libhb/declpcm.c
diff options
context:
space:
mode:
authorvan <[email protected]>2008-04-02 17:55:48 +0000
committervan <[email protected]>2008-04-02 17:55:48 +0000
commit7d87a5afd0a6764e9ef041053ba56234480aef81 (patch)
tree250e7f1e90a43b1fd2173f133281d81a2d34f7e2 /libhb/declpcm.c
parentf960fc7344f13893175b062bffc0877634aacceb (diff)
scan and pcm audio fixes.
- lpcm audio fixed to handle 24 bit & interpret header correctly. - get aspect ratio from libmpeg2 rather than doing it ourselves. - announce when aspect ratio changes during preview scan. - if aspect ratio isn't either 4:3 or 16:9 complain & map to either 4:3 or 16:9 (whichever is closest). - start stream previews from file position 0 rather than 1/11 in case there's only on mpeg sequence header in the file. - don't give up on a file just because we can't get a preview due to a missing sequence header - only give up if we can't get any previews. - get audio bitstream characteristics during preview in a uniform way (we were treating PCM & MPEG audio specially which resulted in not getting their sample rate which caused a divide by zero in sync). git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1370 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb/declpcm.c')
-rw-r--r--libhb/declpcm.c233
1 files changed, 189 insertions, 44 deletions
diff --git a/libhb/declpcm.c b/libhb/declpcm.c
index 155331315..884680b94 100644
--- a/libhb/declpcm.c
+++ b/libhb/declpcm.c
@@ -6,6 +6,27 @@
#include "hb.h"
+struct hb_work_private_s
+{
+ hb_job_t *job;
+ uint32_t size; /* frame size in bytes */
+ uint32_t count; /* frame size in samples */
+ uint32_t pos; /* buffer offset for next input data */
+
+ int64_t next_pts; /* pts for next output frame */
+ int64_t sequence;
+
+ /* the following is frame info for the frame we're currently accumulating */
+ uint64_t duration; /* frame duratin (in 90KHz ticks) */
+ uint32_t offset; /* where in buf frame starts */
+ uint32_t samplerate; /* sample rate in bits/sec */
+ uint8_t nchannels;
+ uint8_t sample_size; /* bits per sample */
+
+ uint8_t frame[HB_DVD_READ_BUFFER_SIZE*2];
+};
+
+static hb_buffer_t * Decode( hb_work_object_t * w );
int declpcmInit( hb_work_object_t *, hb_job_t * );
int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
void declpcmClose( hb_work_object_t * );
@@ -19,72 +40,196 @@ hb_work_object_t hb_declpcm =
declpcmClose
};
+static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 };
+static const int hdr2samplesize[] = { 16, 20, 24, 16 };
+
+static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in )
+{
+ hb_work_private_t * pv = w->private_data;
+
+ /*
+ * LPCM packets have a 7 byte header (the substream id is stripped off
+ * before we get here so it's numbered -1 below)::
+ * byte -1 Substream id
+ * byte 0 Number of frames that begin in this packet
+ * (last frame may finish in next packet)
+ * byte 1,2 offset to first frame that begins in this packet (not including hdr)
+ * byte 3:
+ * bits 0-4 continuity counter (increments modulo 20)
+ * bit 5 reserved
+ * bit 6 audio mute on/off
+ * bit 7 audio emphasis on/off
+ * byte 4:
+ * bits 0-2 #channels - 1 (e.g., stereo = 1)
+ * bit 3 reserved
+ * bits 4-5 sample rate (0=48K,1=96K,2=44.1K,3=32K)
+ * bits 6-7 bits per sample (0=16 bit, 1=20 bit, 2=24 bit)
+ * byte 5 Dynamic range control (0x80 = off)
+ *
+ * The audio is viewed as "frames" of 150 90KHz ticks each (80 samples @ 48KHz).
+ * The frames are laid down continuously without regard to MPEG packet
+ * boundaries. E.g., for 48KHz stereo, the first packet will contain 6
+ * frames plus the start of the 7th, the second packet will contain the
+ * end of the 7th, 8-13 & the start of 14, etc. The frame structure is
+ * important because the PTS on the packet gives the time of the first
+ * frame that starts in the packet *NOT* the time of the first sample
+ * in the packet. Also samples get split across packet boundaries
+ * so we can't assume that we can consume all the data in one packet
+ * on every call to the work routine.
+ */
+ pv->offset = ( ( in->data[1] << 8 ) | in->data[2] ) + 2;
+ if ( pv->offset >= HB_DVD_READ_BUFFER_SIZE )
+ {
+ hb_log( "declpcm: illegal frame offset %d", pv->offset );
+ pv->offset = 2; /*XXX*/
+ }
+ pv->samplerate = hdr2samplerate[ ( in->data[4] >> 4 ) & 0x3 ];
+ pv->nchannels = ( in->data[4] & 7 ) + 1;
+ pv->sample_size = hdr2samplesize[in->data[4] >> 6];
+
+ /*
+ * PCM frames have a constant duration (150 90KHz ticks).
+ * We need to convert that to the amount of data expected. It's the
+ * duration divided by the sample rate (to get #samples) times the number
+ * of channels times the bits per sample divided by 8 to get bytes.
+ * (we have to compute in bits because 20 bit samples are not an integral
+ * number of bytes). We do all the multiplies first then the divides to
+ * avoid truncation errors.
+ */
+ pv->duration = in->data[0] * 150;
+ pv->count = ( pv->duration * pv->nchannels * pv->samplerate ) / 90000;
+ pv->size = ( pv->count * pv->sample_size ) / 8;
+
+ pv->next_pts = in->start;
+}
+
int declpcmInit( hb_work_object_t * w, hb_job_t * job )
{
+ hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
+ w->private_data = pv;
+ pv->job = job;
return 0;
}
+/*
+ * Convert DVD encapsulated LPCM to floating point PCM audio buffers.
+ * The amount of audio in a PCM frame is always <= the amount that will fit
+ * in a DVD block (2048 bytes) but the standard doesn't require that the audio
+ * frames line up with the DVD frames. Since audio frame boundaries are unrelated
+ * to DVD PES boundaries, this routine has to reconstruct then extract the audio
+ * frames. Because of the arbitrary alignment, it can output zero, one or two buf's.
+ */
int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
- hb_buffer_t * in = *buf_in, * out;
- int samplerate = 0;
- int count;
- uint8_t * samples_u8;
- float * samples_fl32;
- int i;
- uint64_t duration;
-
- *buf_out = NULL;
+ hb_work_private_t * pv = w->private_data;
+ hb_buffer_t *in;
+ hb_buffer_t *buf = NULL;
- if( in->data[5] != 0x80 )
+ /* need an input buffer to do anything */
+ if( ! buf_in || ! ( in = *buf_in ) )
{
- hb_log( "no LPCM frame sync (%02x)", in->data[5] );
+ *buf_out = buf;
return HB_WORK_OK;
}
- switch( ( in->data[4] >> 4 ) & 0x3 )
+ pv->sequence = in->sequence;
+
+ /* if we have a frame to finish, add enough data from this buf to finish it */
+ if ( pv->size )
{
- case 0:
- samplerate = 48000;
- break;
- case 1:
- samplerate = 96000;//32000; /* FIXME vlc says it is 96000 */
- break;
- case 2:
- samplerate = 44100;
- break;
- case 3:
- samplerate = 32000;
- break;
+ memcpy( pv->frame + pv->pos, in->data + 6, pv->size - pv->pos );
+ buf = Decode( w );
}
+ *buf_out = buf;
- count = ( in->size - 6 ) / 2;
- out = hb_buffer_init( count * sizeof( float ) );
- duration = count * 90000 / samplerate / 2;
- out->start = in->start;
- out->stop = out->start + duration;
-
- samples_u8 = in->data + 6;
- samples_fl32 = (float *) out->data;
-
- /* Big endian int16 -> float conversion */
- for( i = 0; i < count; i++ )
+ /* save the (rest of) data from this buf in our frame buffer */
+ lpcmInfo( w, in );
+ int off = pv->offset;
+ int amt = in->size - off;
+ pv->pos = amt;
+ memcpy( pv->frame, in->data + off, amt );
+ if ( amt >= pv->size )
{
-#ifdef WORDS_BIGENDIAN
- samples_fl32[0] = *( (int16_t *) samples_u8 );
-#else
- samples_fl32[0] = (int16_t) ( ( samples_u8[0] << 8 ) | samples_u8[1] );
-#endif
- samples_u8 += 2;
- samples_fl32 += 1;
+ if ( buf )
+ {
+ buf->next = Decode( w );
+ }
+ else
+ {
+ *buf_out = Decode( w );
+ }
+ pv->size = 0;
}
+ return HB_WORK_OK;
+}
- *buf_out = out;
+static hb_buffer_t *Decode( hb_work_object_t *w )
+{
+ hb_work_private_t *pv = w->private_data;
+ hb_buffer_t *out = hb_buffer_init( pv->count * sizeof( float ) );
+
+ out->start = pv->next_pts;
+ pv->next_pts += pv->duration;
+ out->stop = pv->next_pts;
- return HB_WORK_OK;
+ uint8_t *frm = pv->frame;
+ float *odat = (float *)out->data;
+ int count = pv->count;
+
+ switch( pv->sample_size )
+ {
+ case 16: // 2 byte, big endian, signed (the right shift sign extends)
+ while ( --count >= 0 )
+ {
+ *odat++ = ( (int)( frm[0] << 24 ) >> 16 ) | frm[1];
+ frm += 2;
+ }
+ break;
+ case 20:
+ // 20 bit big endian signed (5 bytes for 2 samples = 2.5 bytes/sample
+ // so we do two samples per iteration).
+ count /= 2;
+ while ( --count >= 0 )
+ {
+ *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 12 ) |
+ ( frm[1] << 4 ) | ( frm[2] >> 4 ) ) / 16.;
+ *odat++ = (float)( ( (int)( frm[2] << 28 ) >> 16 ) |
+ ( frm[3] << 8 ) | frm[4] ) / 16.;
+ frm += 5;
+ }
+ break;
+ case 24:
+ // This format is bizarre. It's 24 bit samples but some confused
+ // individual apparently thought they would be easier to interpret
+ // as 16 bits if they were scrambled in the following way:
+ // Things are stored in 4 sample (12 byte) chunks. Each chunk has
+ // 4 samples containing the two top bytes of the actual samples in
+ // 16 bit big-endian order followed by the four least significant bytes
+ // of each sample.
+ count /= 4; // the loop has to work in 4 sample chunks
+ while ( --count >= 0 )
+ {
+ *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 8 ) |
+ ( frm[1] << 8 ) | frm[8] ) / 256.;
+ *odat++ = (float)( ( (int)( frm[2] << 24 ) >> 8 ) |
+ ( frm[3] << 8 ) | frm[9] ) / 256.;
+ *odat++ = (float)( ( (int)( frm[4] << 24 ) >> 8 ) |
+ ( frm[5] << 8 ) | frm[10] ) / 256.;
+ *odat++ = (float)( ( (int)( frm[6] << 24 ) >> 8 ) |
+ ( frm[7] << 8 ) | frm[11] ) / 256.;
+ frm += 12;
+ }
+ break;
+ }
+ return out;
}
void declpcmClose( hb_work_object_t * w )
{
+ if ( w->private_data )
+ {
+ free( w->private_data );
+ w->private_data = 0;
+ }
}