Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bitrate will be 0 on some files #102

Open
subframe7536 opened this issue Feb 2, 2024 · 13 comments
Open

bitrate will be 0 on some files #102

subframe7536 opened this issue Feb 2, 2024 · 13 comments

Comments

@subframe7536
Copy link
Contributor

Bitrate can be roughly calculated by (file.fileAbstraction.size - file.tag.sizeOnDisk) * 8 / file.properties.duration. Some files will return 0 when getting its bitrate, and make a fallback value for bitrate is needed IMO.

But that needs a new property on FileAbstraction. Here is my implemention.

export class Stream {
    // ...
    public static getFileSize(path: string): number {
        return fs.statSync(path).size
    }
}

export interface IFileAbstraction {
    // ...
    /**
     * Total size of file
     */
    size: number;
}

export class FileAbstraction {
    // ...
    public get size(): number {
        return Stream.getFileSize(this._name)
    }
}
@subframe7536 subframe7536 changed the title bitrate is 0 on some files bitrate will be 0 on some files Feb 2, 2024
@subframe7536
Copy link
Contributor Author

I'm willing to open a PR for it

@stuartambient
Copy link

stuartambient commented Jun 3, 2024

Anything come of this ? I have a bunch showing 0 yet they are showing up correctly in VLC.

Edit: there are also others with 1.xxxx or 0.xxxx bitrates. The percentage of 'wrong' bitrates was low compared to how many files I tested (all audio) but still would like a way to fix it.

Edit2: Checked some of the file sin ffprobe , bitrate was correct.

@benrr101
Copy link
Owner

@stuartambient I need a lot more info to track this down... What file types are giving trouble? I recently spent a bunch of time working on MP3 audio headers and improved the bitrate/duration calculations.

@subframe7536 Unfortunately your proposal doesn't work in all scenarios. Eg, MPEG containers can contain audio and video streams which have different bitrates. The bitrates are stored in separate ICodec implementations and are available via the Properties object as audioBitrate and videoBitrate respectively. Thus, using the total file size to calculate bitrate can give wildly inaccurate values. taglib# made a decent call to return 0 when the bitrate could not be accurately calculated, and node-taglib-sharp continues along those lines. I think what might be a better solution is to return undefined when the bitrate could not be calculated, leading to 0 being a value that indicates an error/bug. I'm leaning in that direction for a lot of fields, and since I'm "back in the game" so-to-speak, I'm likely going to take that up in the next (major) release .

@stuartambient
Copy link

stuartambient commented Nov 25, 2024

@stuartambient I need a lot more info to track this down... What file types are giving trouble? I recently spent a bunch of time working on MP3 audio headers and improved the bitrate/duration calculations.

@benrr101 Out of about ~150 files with 0 bit rate , 2 are flac, the rest are mp3's. I didn't encode these files and there is a variety of encoders among them. Most of them have a bitrate readout in VLC so probably first step is to try out your new code on the 0 bitrate files. 6.0.0 has the new MP3 stuff ?

@benrr101
Copy link
Owner

@stuartambient Yes, v6.0.0 has the new MP3 stuff which should help with bitrate/duration calculation. FLAC is untouched, tho, so I may have to dig into that one some more to see what might be up. Any chance you could share a sample with me for repro?

@stuartambient
Copy link

stuartambient commented Nov 25, 2024

@benrr101 You want the mp3's or the flac ?
Edit: Actually I need to take a better look at the flac ones (the 2 I referenced are not good files). Here are the mp3's
0-bitrate-samples.zip

I just ran these files though 6.0.0 and bitrate still shows 0. Maybe you see something in the file I'm not that is causing it. All 3 show a bitrate in VLC, 224, 192, and 32. 32 is the one with the title in French.

@stuartambient
Copy link

stuartambient commented Nov 25, 2024

Scrolling through a bunch of files (not 0 bitrate ones) and audioBitrate looks accurate on the majority of files.

Edit: I have a bunch of files that say 32 for bitrate, Same thing in VLC but in Spek it shows 103kbps. Between vlc and spek not sure which is the reliable one.

@benrr101
Copy link
Owner

@stuartambient Thanks for the mp3 samples. I did a quick assessment of these using MpegAudioInfo. These files are likely to always generate odd output for the following reasons:

  • Cowboy track - First frame fails CRC check, changes from layer 1 to layer 3 between frame 1 and 2, and first frame is wrong size. Node-taglib-sharp only uses the first frame to bitrate, so it is likely getting messed up data.
  • French track - First frame states bitrate is 32kbps, no VBR header. However, it turns out file is VBR with an average bitrate of 215. This is only detectable by scanning and averaging the bitrate reported for each frame. Node-taglib-sharp should be reporting the 32kbps based on its implementation, but I'll double checking this.
  • Lake track - has the same issue as the French track, though it reports 64kbps on the first frame, and an average of 224kbps.

So I'll take a look at what's causing it to report 0, since it should at least report 32/64kbps for the files latter two files. I made a note a while back that reading all frames to get the most accurate duration/bitrate info should be considered. But that is an expensive operation, which I guess is why we have different "ReadStyle" values. Either way, I want to understand why these files give back zero.

Since I haven't done anything with FLAC bitrates, I definitely would need a FLAC repro to look into that. Can you provide one of those?

@stuartambient
Copy link

01-tómleiki.zip

This is the only flac and it's a mess, 9KB when it should be ~90KB. My code logs errors when node-taglib-sharp fails to read the file so I included this one because I'm wondering why it did not fail. Metadata for it in VLC refers to M4A.

So far I have logged two errors in some files, 'MPEG audio header not found' and 'Argument out of range, value must be a positive, 32-bit integer'. I'm using the unified tagging so maybe I need to branch out to get more errors detected ?

@benrr101
Copy link
Owner

benrr101 commented Nov 26, 2024

@stuartambient Thanks for these samples! They are going to help me find a bunch of bugs... I don't want to get too ahead of myself, but I'll keep you updated on my progress.

Lake track - Bitrate is being read as zero because header is being read from the wrong position. File starts with an ID3v2.4 tag, but this isn't being detected, so MPEG header search starts at position 0. Reading the aforementioned tag makes progress but fails due to the second comment frame being too small. Second comment frame is too small because the header code claims the frame is only 2 bytes when it is actually 130. The wrong value is being read because the frame header constructor is reading "sync-safe" integers regardless of the header's flags. So that's two bugs:

  • Frame header constructor should check flags before reading "sync-safe" integer
  • If a frame fails, skip it, don't bail on the tag

@stuartambient
Copy link

stuartambient commented Nov 26, 2024

Here is another FLAC, not 0 bitrate but when I noticed it had a 60kbps for a FLAC decided to look closer. MP3Tag flags it with a "FLAC error" yet displays some of the metadata (performers, album, title, duration.....). in the spectrogram it shows just a portion of the file and attempting to play it results in nothing or a small portion will play. NTS read it as well with the same tags as MP3Tag but no error. I did a resave of it in MP3Tag, which removed the error but the file is still hosed.

01 - Twilight Reverie.zip

@benrr101
Copy link
Owner

Update on progress: So the ID3v2.4 spec specifies that all frame sizes must be sync-safe uints, and after digging a bit, I found an old mailing list email that is basically this exact issue. Ie, iTunes at one point (or always?) incorrectly formats ID3v2.4 tags and uses normal uints for frame sizes (https://lists.id3.org/pipermail/id3v2/2005-August/001006.html).

Unfortunately, if the frame size is incorrect, we lose our bearings and can't easily find the start of the next frame. I've considered these options:

  • If a failure occurs while reading the frame, and tag version is ID3v2.4, retry with the header read as ID3v2.3 - discarded this idea b/c only in very special cases (like the one you provided me) will the incorrect frame size lead to the frame failing to load. In most cases it will load but will be truncated, though subsequent frame reads may fail.
  • If the highest bit of the bytes of the frame size is 1, then treat it as a normal uint, otherwise treat it as sync-safe - discarded this as well b/c you could have a normal uint that doesn't set the highest bit of the bytes of the frame size. (eg, ... 00000001 00011111 - is that sync-safe or not?)
  • Some kind of scanning for frame identifiers - basically start where we think the header says the frame ends and if there's a valid frame identifier there, the size is valid. Otherwise, maybe reread the header as normal uint? I discarded this idea, though it could be feasible for a couple reasons:
    • iTunes (once again 🙄) uses some non-standard frame IDs, and there's always the possibility for user-specified IDs. So we may accidentally miss valid frame identifiers
    • Users can always put a valid frame identifier inside the value of a frame. So we may false positive find frame identifiers.
    • Making sure we don't overrun the tag requires a lot of restructuring how the tags are read and imho not worth the effort.

What this boils down to is - if the frame size is wrong, the tag is corrupt. We can't discard the tag, but we should at least allow the tag to be identified and rewritten. So, I'm considering exposing a new corruptReason field on Tag and if any exception is thrown while reading the tag, it will be stored there. This way, the user can still access the fields we successfully read, the user can change fields, and then write them out without corrupting the media portion of the file.

In experiments so far, this has enabled me to read the bitrate of the Lake track though it is still the 64kbps as reported by the first frame.

@stuartambient
Copy link

mp3_diags

I ran a few files through MP3 Diags, this was the printout of "The Black Lake". Row 7 shows what you found. The rest of the analysis looks bad as well. Not sure how those other factors would effect the metadata.

I think what you propose sounds good and was thinking in that direction as well.

This was the output to the file after I used NTS to write an image to the tag -

mp3_diags_post_write

I am kind of curious as to how ffmpeg can read the correct bitrate from the file(s).

Anyway, appreciate your efforts and feedback. The world of media file tags looks very challenging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants