mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
a52: play files that do not start with a frame
A52 files cut from a longer stream can start partway through a frame. The metadata parser required a sync word at offset 0 and rejected them, although the codec can resync. Scan the first 64 KB for a frame header that is followed by a second valid header, record it as first_frame_offset, and use that offset when seeking and resuming. Tested in the simulator (perfsim) and on a Sansa Clip+: a file starting with 675 bytes of a partial frame now plays. In the simulator it decodes all 3161 frames with the same output as the file without those bytes, and seeks land on frame starts. Fixes FS#9056 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Change-Id: I5d72b1cd1d8437491ec00a38459e391fb158b9a2
This commit is contained in:
parent
b34fcd602a
commit
8194d51c49
2 changed files with 93 additions and 25 deletions
|
|
@ -158,14 +158,15 @@ enum codec_status codec_run(void)
|
|||
|
||||
samplesdone = 0;
|
||||
|
||||
if (ci->id3->offset) {
|
||||
sample_loc = (ci->id3->offset / ci->id3->bytesperframe) *
|
||||
A52_SAMPLESPERFRAME;
|
||||
if (ci->id3->offset > ci->id3->first_frame_offset) {
|
||||
sample_loc = ((ci->id3->offset - ci->id3->first_frame_offset) /
|
||||
ci->id3->bytesperframe) * A52_SAMPLESPERFRAME;
|
||||
param = ci->id3->offset;
|
||||
}
|
||||
else if (ci->id3->elapsed) {
|
||||
sample_loc = ci->id3->elapsed/1000 * ci->id3->frequency;
|
||||
param = sample_loc/A52_SAMPLESPERFRAME*ci->id3->bytesperframe;
|
||||
param = sample_loc/A52_SAMPLESPERFRAME*ci->id3->bytesperframe +
|
||||
ci->id3->first_frame_offset;
|
||||
}
|
||||
else {
|
||||
sample_loc = 0;
|
||||
|
|
@ -190,7 +191,8 @@ enum codec_status codec_run(void)
|
|||
sample_loc = param/1000 * ci->id3->frequency;
|
||||
|
||||
if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*
|
||||
ci->id3->bytesperframe)) {
|
||||
ci->id3->bytesperframe +
|
||||
ci->id3->first_frame_offset)) {
|
||||
samplesdone = sample_loc;
|
||||
ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "metadata.h"
|
||||
#include "logf.h"
|
||||
#include "metadata_parsers.h"
|
||||
|
|
@ -42,37 +43,107 @@ static const unsigned short a52_441framesizes[] =
|
|||
1254 * 2, 1393 * 2, 1394 * 2
|
||||
};
|
||||
|
||||
/* How far into the file to look for the first frame */
|
||||
#define A52_MAX_SCAN (64 * 1024)
|
||||
#define A52_HEADER_SIZE 6
|
||||
|
||||
/* Return the size in bytes of the frame starting with hdr, or 0 if hdr is
|
||||
not a valid frame header. */
|
||||
static int a52_frame_size(const unsigned char *hdr)
|
||||
{
|
||||
int frmsizecod = hdr[4] & 0x3f;
|
||||
int bitrate;
|
||||
|
||||
if ((hdr[0] != 0x0b) || (hdr[1] != 0x77) || (frmsizecod > 37)
|
||||
|| (hdr[5] >= 0x60)) /* bsid >= 12 is not A52 */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bitrate = a52_bitrates[frmsizecod >> 1];
|
||||
|
||||
switch (hdr[4] & 0xc0)
|
||||
{
|
||||
case 0x00:
|
||||
return bitrate * 2 * 2;
|
||||
case 0x40:
|
||||
return a52_441framesizes[frmsizecod];
|
||||
case 0x80:
|
||||
return bitrate * 3 * 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the first frame in the file, which is not at the start if the file
|
||||
was cut from a longer stream. A match must be followed by a second valid
|
||||
header so that sync words inside the audio data are skipped. Returns the
|
||||
offset of the frame and leaves its header in hdr, or -1 if none found. */
|
||||
static off_t a52_find_first_frame(int fd, unsigned char *buf, size_t bufsize,
|
||||
unsigned char *hdr)
|
||||
{
|
||||
unsigned char next[A52_HEADER_SIZE];
|
||||
off_t pos = 0;
|
||||
ssize_t n;
|
||||
int i, size;
|
||||
|
||||
while (pos < A52_MAX_SCAN)
|
||||
{
|
||||
if ((lseek(fd, pos, SEEK_SET) < 0)
|
||||
|| ((n = read(fd, buf, bufsize)) < A52_HEADER_SIZE))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i <= n - A52_HEADER_SIZE; i++)
|
||||
{
|
||||
size = a52_frame_size(&buf[i]);
|
||||
if (!size)
|
||||
continue;
|
||||
|
||||
/* A frame at the very start is accepted as before, even if the
|
||||
file holds only one frame */
|
||||
if ((pos + i == 0)
|
||||
|| ((lseek(fd, pos + i + size, SEEK_SET) >= 0)
|
||||
&& (read(fd, next, sizeof(next)) == sizeof(next))
|
||||
&& a52_frame_size(next)))
|
||||
{
|
||||
memcpy(hdr, &buf[i], A52_HEADER_SIZE);
|
||||
return pos + i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlap so a header split across reads is still found */
|
||||
pos += n - (A52_HEADER_SIZE - 1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool get_a52_metadata(int fd, struct mp3entry *id3)
|
||||
{
|
||||
/* Use the trackname part of the id3 structure as a temporary buffer */
|
||||
unsigned char* buf = (unsigned char *)id3->path;
|
||||
unsigned char hdr[A52_HEADER_SIZE];
|
||||
unsigned long totalsamples;
|
||||
off_t offset;
|
||||
int i;
|
||||
|
||||
if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 5) < 5))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((buf[0] != 0x0b) || (buf[1] != 0x77))
|
||||
offset = a52_find_first_frame(fd, buf, sizeof(id3->path), hdr);
|
||||
if (offset < 0)
|
||||
{
|
||||
logf("not an A52/AC3 file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
i = buf[4] & 0x3e;
|
||||
|
||||
if (i > 36)
|
||||
{
|
||||
logf("A52: Invalid frmsizecod: %d\n",i);
|
||||
return false;
|
||||
}
|
||||
i = hdr[4] & 0x3e;
|
||||
|
||||
id3->bitrate = a52_bitrates[i >> 1];
|
||||
id3->vbr = false;
|
||||
id3->filesize = ffilesize(fd);
|
||||
id3->first_frame_offset = offset;
|
||||
|
||||
switch (buf[4] & 0xc0)
|
||||
switch (hdr[4] & 0xc0)
|
||||
{
|
||||
case 0x00:
|
||||
id3->frequency = 48000;
|
||||
|
|
@ -88,15 +159,10 @@ bool get_a52_metadata(int fd, struct mp3entry *id3)
|
|||
id3->frequency = 32000;
|
||||
id3->bytesperframe = id3->bitrate * 3 * 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
logf("A52: Invalid samplerate code: 0x%02x\n", buf[4] & 0xc0);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* One A52 frame contains 6 blocks, each containing 256 samples */
|
||||
totalsamples = id3->filesize / id3->bytesperframe * 6 * 256;
|
||||
totalsamples = (id3->filesize - offset) / id3->bytesperframe * 6 * 256;
|
||||
id3->length = totalsamples / id3->frequency * 1000;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue