Laden...

VBR bei mp3s korrekt auslesen

Erstellt von Nightline vor 19 Jahren Letzter Beitrag vor 19 Jahren 3.355 Views
N
Nightline Themenstarter:in
49 Beiträge seit 2005
vor 19 Jahren
VBR bei mp3s korrekt auslesen

Hallo!

Die MediaPlayer-Serie von MS kommt ja bekanntlich nicht mit VBR zurecht und zeigt falsche Songlängen an. Selbes passiert mir (erwartungsgemäss), wenn ich über Microsoft.DirectX.AudioVideoPlayback die Länge auslese.

Gibt es denn noch eine andere Möglichkeit, VBR richtig zu interpretieren? Hat nicht Winamp zufällig auch eine freie dll zum Benutzen?

C
1.215 Beiträge seit 2004
vor 19 Jahren

wird die bitrate denn korrekt ausgegeben?

wenn ja, dann rechnest du dir die länge einfach selber aus:

bitrate: 128 kb/s = 16 kB/s dateigrösse: 3200 kB

länge: 3200 / 16 = 200 s = 3 min 20 s

grtz
chief

C
1.215 Beiträge seit 2004
vor 19 Jahren

genaugenommen sollte man natürlich noch den MPEG-header u. evtl. die ID-Tags abziehen...
also ist diese rechnung ungenau.

grtz
chief

4.207 Beiträge seit 2003
vor 19 Jahren

_Original von Chief Brodie_bitrate: 128 kb/s = 16 kB/s

Wer lesen kann, ist klar im Vorteil ... da gibt es nicht DIE Bitrate ...

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

C
1.215 Beiträge seit 2004
vor 19 Jahren

Wer lesen kann, ist klar im Vorteil ... da gibt es nicht DIE Bitrate ...

wer jede abkürzung kennt, vielleicht...

...eine dumme und überflüssige bemerkung!
🙄

grtz
chief

S
125 Beiträge seit 2005
vor 19 Jahren

Hatte mir selber mal nen kleinen Player gebastelt.
Ich weiß nicht ob dir das hier weiterhilft:



using DX_AudioVideo;
using System;
using System.IO;
using System.Collections;
using Microsoft.DirectX.AudioVideoPlayback;

class MP3_Header
{
	FileInfo fi = null; 
	FileStream fs;
	
	Track track;
// Daten um Header zu finden und zu speichern
	
	int neue_Zahl;
	int ergebnis;
	int rest;
	string temp = "";
	string output = "";	// binär Ausgabe des Headers als String
	byte[] header = new byte[4];	// 4 Bytes des Headers
	int[] IntBytes = new int[4];
	
	int headerpos;		// Headerheaderposition
	
	// MP3 Header-Daten				Bit
	string SyncWord;			// 0 - 11
	string MPEG_Version;		// 12
	string MPEG_Layer;			// 13 - 14
	string ProtectionBit;		// 15
	string Bitrate;				// 16 - 19
	string SamplingFrequency;	// 20 - 21
	string PaddingBit;			// 22
	string PrivateBit;			// 23
	string Mode;				// 24 - 25
	string ModeExtension;		// 26 - 27
	string Copyright;			// 28
	string OriginalHome;		// 29
	string Emphasis;			// 30 - 31
	
	// MPEG 1 & 2 Layer 1 are the same kbit/s
	string[] Layer1 = new string[]{"-","32","64","96","128","160","192","224","256","288","320","352","384","416","448","-"};

	// MPEG 1 & 2 Layer 2 are the same kbit/s
	string[] Layer2 = new string[]{"-","32","48","56","64","80","96","112","128","160","192","224","256","320","384","-"};
	
	// MPEG 1 Layer 3
	string[] Layer3_1 = new string[]{"-","32","40","48","56","64","80","96","112","128","160","192","224","256","320","-"};

	// MPEG 2 Layer 3
	string[] Layer3_2 = new string[]{"-","8","16","24","32","64","80","56","64","128","160","112","128","256","320","-"};
	
	int FrameCount;
	
	public MP3_Header(Track track)
	{
		//this.GetHeader();
		this.track = track;
		Console.WriteLine("MP3_Header angelegt mit: MP3_Header("+track.GetPath()+")");
		this.GetHeaderPos();
		this.GetHeaderParts(output);
		this.CountFrames();
	}
	
	public void GetHeader(byte[] header)
	{
	
		/*
		 * Jedes Byte wird per "Restverfahren" in seine 8 Bits zerlegt.
		 * Dabei wird die Reihenfolge der Bits umgekehrt.
		 * Zur Kontrolle kann jeder Rechenschritt ausgegeben werden. (Kommentierung entfernen)
		 * */
		foreach( byte b in header )	// für jedes Element in "ByteArray"
		{
			neue_Zahl = b;
			for(int i = 0; i < 8; ++i)	// 8 mal ( 8 bit = 1 byte)
			{
				ergebnis = neue_Zahl/2;	// Ergebnis der Division
				rest = neue_Zahl%2;		// Restwert der Division

				//Console.WriteLine("{0,3} durch 2 = {1,3} Rest: {2}", neue_Zahl, ergebnis, rest);
				
				temp = temp.Insert(0,rest.ToString());	// Rest im temporären String an Position 0 einfügen
														// wegen umgekehrter Reihenfolge der bits
				neue_Zahl = ergebnis;					// zum Weiterrechnen im nächsten Durchlauf
			}
			output += temp;//+" ";		// Einfügen eines Leerzeichens um jeweils 8 Bit voneinander zu trennen
			temp = "";				// temporären String wieder "leeren"
		}
		Console.WriteLine(output);
		fs.Close();
	}
	
	public void GetHeaderPos()
	{
		//fi = new FileInfo(@"C:\mp3\Track 01-.mp3");
		//fi = new FileInfo(@"C:\mp3\01 - Flat On The Floor.mp3");
		//fi = new FileInfo(@"C:\mp3\10 - Another Hole In The Head.mp3");
		fi = new FileInfo(track.GetPath());
		Console.WriteLine("MP3_Header.GetHeaderPos():"+track.GetPath());
		fs = fi.OpenRead();
		
		byte b;
		headerpos = 0;
				
		do
		{
			b = (byte)fs.ReadByte();
			
			//Console.WriteLine("Byte Nr: {0,4}: {1} ",headerpos,(int)header[0]);
			headerpos++;
		}while(b != 0xff);
		
		Console.WriteLine("\nHeader:\n******");
		header[0] = b;
		Console.WriteLine("Byte 0 = {0:x}",(int)b);
		
		for(int i = 1; i <= 3; ++i)
		{
			header[i] = (byte)fs.ReadByte();
			Console.WriteLine("Byte {0} = {1:x}",i+1,(int)header[i]);
		}
		
		Console.WriteLine("\n\nHeader bei: " +(headerpos-1));
		this.GetHeader(header);
		// 11111111 11111011 10110000 01100100
		// 11111111 11111011 10110000 01100100
		fs.Close();
	}
	
	public void GetHeaderParts(string output)
	{
		this.SyncWord 			= output.Substring(0,12);
		this.MPEG_Version 		= output.Substring(12,1);
		this.MPEG_Layer			= output.Substring(13,2);
		this.ProtectionBit		= output.Substring(15,1);
		this.Bitrate			= output.Substring(16,4);
		this.SamplingFrequency 	= output.Substring(20,2);
		this.PaddingBit			= output.Substring(22,1);
		this.PrivateBit			= output.Substring(23,1);
		this.Mode				= output.Substring(24,2);
		this.ModeExtension		= output.Substring(26,2);
		this.Copyright			= output.Substring(28,1);
		this.OriginalHome		= output.Substring(29,1);
		this.Emphasis			= output.Substring(30,2);
		
//		Console.WriteLine("SyncWord: " + SyncWord + 
//		                  "\nMPEG_Version: "+ MPEG_Version +
//		                  "\nMPEG_Layer: "+ MPEG_Layer +
//		                  "\nProtectionBit:"+ ProtectionBit +
//		                  "\nBitrate: "+ Bitrate);
		
//		Console.WriteLine("Version: "+this.GetMPEG_Version());
//		Console.WriteLine(this.GetMPEG_Layer());
//		Console.WriteLine("CRC-Prüfsumme: "+this.GetProtectionBit());
//		Console.WriteLine(this.GetBitrate()+" kbit/s");
//		Console.WriteLine(this.GetSamplingFrequency()+" Hz");
//		Console.WriteLine("Padding: " +this.GetPaddingBit());
//		Console.WriteLine("Private: " +this.GetPrivateBit());
//		Console.WriteLine("Mode: " +this.GetMode());
//		Console.WriteLine("Copyright: " +this.GetCopyright());
//		Console.WriteLine("Original: " + this.GetOriginalHome());
//		Console.WriteLine("Rauschunterdr.: " +this.GetEmphasis());
		                  
	}
	
	// SyncWord (ungenutzt, ausser bei MPEG 2.5)
	public string GetSyncWord()
	{
		return SyncWord;
	}
	
	// MPEG Version
	public string GetMPEG_Version()
	{
		if(MPEG_Version.Equals("0"))
			return "MPEG 2";
		else
			return "MPEG 1";
	}
	
	// Layer
	public string GetMPEG_Layer()
	{
		if(MPEG_Layer.Equals("00"))
			return "undefiniert";
		else if(MPEG_Layer.Equals("01"))
			return "Layer III";
		else if(MPEG_Layer.Equals("10"))
			return "Layer II";
		else
			return "Layer I";
	}
	
	// CRC Prüfsumme (ja: 16 Bit nach dem Header)
	public string GetProtectionBit()
	{
		if(this.ProtectionBit.Equals("1"))
			return "nein";	// 0: CRC = ja ; 1: CRC = nein
		else
			return "ja";
	}
	
	// Bitrate
	public string GetBitrate()
	{
		if(this.MPEG_Version.Equals("1")) // MPEG 1 ???
		{
			return this.MPEG_1_Bitrate();			
		}
		else							  // MPEG 2 ???
		{
			return this.MPEG_2_Bitrate();
		}
	}
	
	// Sample Frequenz
	public string GetSamplingFrequency()
	{
		if(MPEG_Version.Equals("1"))
		{
			switch(this.SamplingFrequency)
			{
				case "00":	return "44100";
				case "01":	return "48000";
				case "10":  return "32000";
				case "11":	return "-";
				default: return "error";
			}
		}
		else
		{
			switch(this.SamplingFrequency)
			{
				case "00":	return "22050";
				case "01":	return "24000";
				case "10":  return "16000";
				case "11":	return "-";
				default: return "error";
			}
		}
	}
	
	// Padding Bit
	public string GetPaddingBit()
	{
		if(this.PaddingBit.Equals("1"))
			return "ja";
		else
			return "nein";
	}
	
	// Private Bit (ungenutzt)
	public string GetPrivateBit()
	{
		if(this.PrivateBit.Equals("1"))
			return "ja";
		else
			return "nein";
	}
	
	// Audio Modus
	public string GetMode()
	{
		switch(this.Mode)
		{
			case "00":	return "Stereo";
			case "01":	return "Joint Stereo";
			case "10":	return "Dual-Channel";
			case "11":	return "Mono";
			default:	return "error";
		}
	}
	
//	Nur interessant bei Joint Stereo
//	
//	public string GetModeExtension(string ModeExtension)
//	{
//		
//	}

	// Copyright ja/nein
	public string GetCopyright()
	{
		if(this.Copyright.Equals("1"))
			return "ja";
		else
			return "nein";
	}
	
	// Original-Datenstrom
	public string GetOriginalHome()
	{
		if(this.OriginalHome.Equals("1"))
			return "ja";
		else
			return "nein";
	}
	
	// Rauschunterdrückung
	public string GetEmphasis()
	{
		switch(this.Emphasis)
		{
			case "00":	return "keine";
			case "01": 	return "50/15ms";
			case "10":	return "reserviert";
			case "11":	return "CCITT j.17";
			default: 	return "error";
		}
	}
	
	private string MPEG_1_Bitrate()
	{
		if(this.MPEG_Layer.Equals("11")) 		// MPEG 1 Layer I  ???
		{
			return this.Layer1[this.Bitrates()];
		}
		else if(this.MPEG_Layer.Equals("10")) 	// MPEG 1 Layer II ???
		{
			return this.Layer2[this.Bitrates()];
		}
		else									// MPEG 1 Layer III ???
		{
			return this.Layer3_1[this.Bitrates()];
		}
	}
	
	private string MPEG_2_Bitrate()
	{
		if(this.MPEG_Layer.Equals("11")) 		// MPEG 2 Layer I  ???
		{
			return this.Layer1[this.Bitrates()];
		}
		else if(this.MPEG_Layer.Equals("10")) 	// MPEG 2 Layer II ???
		{
			return this.Layer2[this.Bitrates()];
		}
		else									// MPEG 2 Layer III ???
		{
			return this.Layer3_2[this.Bitrates()];
		}
	}
	
	private int Bitrates()
	{
		switch(this.Bitrate)
		{
			case "0000": return 0;
			
			case "0001": return 1;
			
			case "0010": return 2;
			
			case "0011": return 3;
			
			case "0100": return 4;
			
			case "0101": return 5;
			
			case "0110": return 6;
			
			case "0111": return 7;
			
			case "1000": return 8;
			
			case "1001": return 9;
			
			case "1010": return 10;
			
			case "1011": return 11;
			
			case "1100": return 12;
			
			case "1101": return 13;
			 
			case "1110": return 14;
			
			case "1111": return 15;
			
			default: return -1;
					
		}
	}
	

	
	void CountFrames()
	{
		double framelength	= 0;
		double bitrate 		= (Convert.ToDouble(this.GetBitrate()))*1000;	// kbps * 1000
		double frequency 	= (Convert.ToDouble(this.GetSamplingFrequency()));
		double padding 		= Convert.ToDouble(this.PaddingBit);
		
		if(this.GetMPEG_Layer().Equals("Layer I"))
		{
			Console.WriteLine(this.GetMPEG_Layer());
			framelength = (12 * bitrate / frequency + padding) * 4;
			Console.WriteLine("(12 * {0} / {1} + {2}) * 4 = {3}", bitrate, frequency, padding, framelength);
		}
		else
		{
			Console.WriteLine(this.GetMPEG_Layer());
			framelength = 144 * bitrate;
			Console.WriteLine("144 * bitrate = " + framelength);
			framelength = framelength / frequency;
			Console.WriteLine("framelength / frequency = " + framelength);
			framelength = framelength + padding;
			Console.WriteLine("framelength + padding = " + framelength);
			Console.WriteLine("144 * {0} / {1} + {2} = {3}", bitrate, frequency, padding, framelength);
		}
		
		FrameCount = (int)framelength;
		Console.WriteLine("Framelength in Bytes: {0}",(int)framelength);
		//return FrameCount;	
	}
	
	public int GetFrameCount()
	{
		return FrameCount;
	}
	
}


Ist mit Sicherheit nicht die elgenteste Lösung, aber ich hatte mit der Tracklänge nie Probleme. 😁

*************************
Ich bin root, ich darf das... 😜
root>_
*************************

P
939 Beiträge seit 2003
vor 19 Jahren

Das ist auch nicht die Lösung. Ganz unten gibt es eine CountFrames-Methode, die funktioniert aber nur für feste Bitraten.

Ich habe (vor zwei Jahren) aus dem C-Code des Frauenhofer Mp3-Decoders eine C#-Version entwickelt. Guter, objektorientierter Code: es gibt nur ein Mp3-Frame-Objekt. Über einen BitStream (und ein BitReservoir) wird der Mp3-Frame immer wieder von neuem aufgefüllt, kein einziges "new" im Code. Performance-kritische Teile sind nativ implementiert (IMDCT, Subband-Synthese).

Das müsste ich mal veröffentlichen. Bisher liegt es nur bei mir rum, komm irgendwie nicht dazu.

Jedenfalls, um die Spieldauer eines VBR-Files zu ermitteln, müssen erstmal alle Frame-Headers gelesen werden. Es sei denn über VBR soll eine feste, aber unübliche Bitrate eingestellt werden (z.B. 100 kb). Ich glaub da gab es Bits, anhand derer man das untescheiden konnte. Auf jeden Fall sind zwei VBR-Verfahren beschrieben: feste Bitrate und qualitätsgesteuerte Bitrate.

Gruss
Pulpapex