//Creates the ByteBuffer for the ogg tag public ByteBuffer Create(Tag tag, int paddingSize) { ByteBuffer ogg = creator.Create(tag); int tagLength = ogg.Capacity + 4; ByteBuffer buf = new ByteBuffer( tagLength + paddingSize ); //CREATION OF CVORBIS COMMENT METADATA BLOCK HEADER //If we have padding, the comment is not the last block (bit[0] = 0) //If there is no padding, the comment is the last block (bit[0] = 1) byte type = (paddingSize > 0) ? (byte)0x04 : (byte) 0x84; buf.Put(type); int commentLength = tagLength - 4; //Comment length buf.Put( new byte[] { (byte)((commentLength & 0xFF0000) >> 16), (byte)((commentLength & 0xFF00) >> 8) , (byte)(commentLength&0xFF) } ); //The actual tag buf.Put(ogg); //PADDING if(paddingSize >=4) { int paddingDataSize = paddingSize - 4; buf.Put((byte)0x81); //Last frame, padding 0x81 buf.Put(new byte[]{ (byte)((paddingDataSize&0xFF0000)>>16),(byte)((paddingDataSize&0xFF00)>>8),(byte)(paddingDataSize&0xFF) }); for(int i = 0; i< paddingDataSize; i++) buf.Put((byte)0); } buf.Rewind(); return buf; }
public ByteBuffer Create(Tag tag, int padding) { IList fields = CreateFields(tag); int tagSize = ComputeTagLength(tag, fields); ByteBuffer buf = new ByteBuffer(tagSize + padding); Create(tag, buf, fields, tagSize, padding); buf.Rewind(); return buf; }
protected override void Create(Tag tag, ByteBuffer buf, IList fields, int tagSize, int paddingSize) { //APETAGEX------------------ buf.Put(Utils.GetBytes("APETAGEX")); //Version 2.0 (aka 2000) buf.Put( new byte[] {(byte)0xD0,0x07,0x00,0x00} ); //Tag size int size = tagSize - 32; buf.Put(Utils.GetNumber(size)); //Number of fields int listLength = fields.Count; buf.Put(Utils.GetNumber(listLength)); //Flags buf.Put( new byte[] {0x00,0x00,0x00,(byte)0xA0} ); //means: We have a header and a footer, this is the header //Reserved 8-bytes 0x00 buf.Put( new byte[] {0,0,0,0,0,0,0,0} ); //Now each field is saved: foreach(byte[] field in fields) { buf.Put(field); } //APETAGEX------------------ buf.Put(Utils.GetBytes("APETAGEX")); //APETAGEX //Version 2.0 (aka 2000) buf.Put( new byte[] {(byte)0xD0,0x07,0x00,0x00} ); //Tag size buf.Put(Utils.GetNumber(size)); //Number of fields buf.Put(Utils.GetNumber(listLength)); //Flags buf.Put( new byte[] {0x00,0x00,0x00,(byte)0x80} ); //means: We have a header and a footer, this is the footer //Reserved 8-bytes 0x00 buf.Put( new byte[] {0,0,0,0,0,0,0,0} ); //---------------------------------------------------------------------------- }
public Id3v2Tag Read(ByteBuffer data, bool[] ID3Flags, byte version) { int tagSize = data.Limit; byte[] b; Id3v2Tag tag = new Id3v2Tag(); //---------------------------------------------------------------------------- //Traitement en cas d'Header Etendu==true (A COMPLETER) if (version == Id3v2Tag.ID3V23 && ID3Flags[1]) ProcessExtendedHeader(data); //---------------------------------------------------------------------------- //Extraction des champs de texte int specSize = (version == Id3v2Tag.ID3V22) ? 3 : 4; for (int a = 0; a < tagSize; a++) { //Nom de la Frame b = new byte[specSize]; if(data.Remaining <= specSize) break; data.Get(b); string field = new string(System.Text.Encoding.ASCII.GetChars(b)); if (b[0] == 0) break; //La longueur du texte contenu dans la Frame int frameSize = ReadInteger(data, version); if ((frameSize > data.Remaining) || frameSize <= 0){ //ignore empty frames break; } b = new byte[ frameSize + ((version == Id3v2Tag.ID3V23) ? 2 : 0) ]; data.Get(b); if( "" != field) { Id3Frame f = CreateId3Frame(field, b, version); if(f != null) tag.Add(f); } } return tag; }
//Creates the ByteBuffer for the ogg tag public ByteBuffer Convert(Tag tag) { ByteBuffer ogg = creator.Create(tag); int tagLength = ogg.Capacity + 8; ByteBuffer buf = new ByteBuffer(tagLength); //[packet type=comment0x03]['vorbis'] buf.Put( new byte[]{(byte) 0x03, (byte) 0x76, (byte) 0x6f, (byte) 0x72, (byte) 0x62, (byte) 0x69, (byte) 0x73} ); //The actual tag buf.Put(ogg); //Framing bit = 1 buf.Put( (byte) 0x01 ); buf.Rewind(); return buf; }
//Creates the ByteBuffer for the ogg tag protected override void Create(Tag tag, ByteBuffer buf, IList fields, int tagSize, int padding) { string vendorstring; if (tag.HasField("vendor")) vendorstring = tag.Get("vendor")[0] as string; else //FIXME: Do something about this vendorstring = "DefaultVendor"; byte[] vendorBytes = Utils.GetBytes(vendorstring, "UTF-8"); buf.Put( new byte[]{(byte)(vendorBytes.Length&0xFF), (byte)((vendorBytes.Length & 0xFF00) >> 8) , (byte)((vendorBytes.Length & 0xFF0000) >> 16), (byte)((vendorBytes.Length & 0xFF000000) >> 24) } ); buf.Put( vendorBytes ); //[user comment list length] buf.Put( Utils.GetNumber(fields.Count) ); foreach(byte[] field in fields) { buf.Put(field); } }
public ByteBuffer synchronize(ByteBuffer b) { ByteBuffer bb = new ByteBuffer(b.Capacity); while(b.Remaining >= 1) { byte cur = b.Get(); bb.Put(cur); if((cur&0xFF) == 0xFF && b.Remaining >=1 && b.Peek() == 0x00) { //First part of synchronization b.Get(); } } //We have finished filling the new bytebuffer, so set the limit, and rewind bb.Limit = bb.Position; bb.Rewind(); return bb; }
public void Write(Tag tag, Stream raf, Stream rafTemp) { //Read firstPage---------------------------------------------------- raf.Seek(26, SeekOrigin.Begin); byte[] b = new byte[4]; int pageSegments = raf.ReadByte()&0xFF; //Unsigned raf.Seek(0, SeekOrigin.Begin); b = new byte[27 + pageSegments]; raf.Read(b, 0, b.Length); OggPageHeader firstPage = new OggPageHeader(b); //------------------------------------------------------------------ raf.Seek(0, SeekOrigin.Begin); //write 1st page (unchanged)---------------------------------------- byte[] pageBytes = new byte[firstPage.PageLength + 27 + pageSegments]; raf.Read(pageBytes, 0, pageBytes.Length); rafTemp.Write(pageBytes, 0, pageBytes.Length); //rafTemp.Seek(firstPage.PageLength + raf.Position, SeekOrigin.Current); //------------------------------------------------------------------ //Read 2nd page----------------------------------------------------- long pos = raf.Position; raf.Seek(raf.Position + 26, SeekOrigin.Begin); pageSegments = raf.ReadByte()&0xFF; //Unsigned raf.Seek(pos, SeekOrigin.Begin); b = new byte[27 + pageSegments]; raf.Read(b, 0, b.Length); OggPageHeader secondPage = new OggPageHeader(b); long secondPageEndPos = raf.Position; //------------------------------------------------------------------ //Compute old comment length---------------------------------------- int oldCommentLength = 7; // [.vorbis] raf.Seek(raf.Position + 7, SeekOrigin.Begin); b = new byte[4]; raf.Read(b, 0, b.Length); int vendorstringLength = Utils.GetNumber(b, 0,3); oldCommentLength += 4 + vendorstringLength; raf.Seek(raf.Position + vendorstringLength, SeekOrigin.Begin); b = new byte[4]; raf.Read(b, 0, b.Length); int userComments = Utils.GetNumber(b, 0,3); oldCommentLength += 4; for (int i = 0; i < userComments; i++) { b = new byte[4]; raf.Read(b, 0, b.Length); int commentLength = Utils.GetNumber(b, 0,3); oldCommentLength += 4 + commentLength; raf.Seek(raf.Position + commentLength, SeekOrigin.Begin); } int isValid = raf.ReadByte(); oldCommentLength += 1; if (isValid != 1) throw new CannotWriteException("Unable to retreive old tag informations"); //------------------------------------------------------------------ //Get the new comment and create the container bytebuffer for 2nd page--- ByteBuffer newComment = tc.Convert(tag); int newCommentLength = newComment.Capacity; int newSecondPageLength = secondPage.PageLength - oldCommentLength + newCommentLength; byte[] segmentTable = CreateSegmentTable(oldCommentLength,newCommentLength,secondPage); int newSecondPageHeaderLength = 27 + segmentTable.Length; ByteBuffer secondPageBuffer = new ByteBuffer(newSecondPageLength + newSecondPageHeaderLength); //------------------------------------------------------------------ //Build the new second page header---------------------------------- //OggS capture secondPageBuffer.Put(Utils.GetBytes("OggS")); //Stream struct revision secondPageBuffer.Put((byte) 0); //header_type_flag secondPageBuffer.Put((byte) 0); //absolute granule position secondPageBuffer.Put( new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 }); //stream serial number secondPageBuffer.Put(Utils.GetNumber(secondPage.SerialNumber)); //page sequence no secondPageBuffer.Put(Utils.GetNumber(secondPage.PageSequence)); //CRC (to be computed later) secondPageBuffer.Put(new byte[] {(byte) 0, (byte) 0, (byte) 0, (byte) 0 }); int crcOffset = 22; if (segmentTable.Length > 255) { throw new CannotWriteException ("In this special case we need to " + "create a new page, since we still hadn't the time for that " + "we won't write because it wouldn't create an ogg file."); } //page_segments nb. secondPageBuffer.Put((byte)segmentTable.Length); //page segment table for (int i = 0; i < segmentTable.Length; i++) secondPageBuffer.Put(segmentTable[i]); //------------------------------------------------------------------ //Add to the new second page the new comment------------------------ secondPageBuffer.Put(newComment); //------------------------------------------------------------------ //Add the remaining old second page (encoding infos, etc)----------- raf.Seek(secondPageEndPos+oldCommentLength, SeekOrigin.Begin); secondPageBuffer.Put(raf); //------------------------------------------------------------------ //Compute CRC over the new second page------------------------------ byte[] crc = OggCRCFactory.ComputeCRC(secondPageBuffer.Data); for (int i = 0; i < crc.Length; i++) { secondPageBuffer.Put(crcOffset + i, crc[i]); } //------------------------------------------------------------------ //Transfer the second page bytebuffer content----------------------- secondPageBuffer.Rewind(); rafTemp.Write(secondPageBuffer.Data, 0, secondPageBuffer.Data.Length); //------------------------------------------------------------------ //Write the rest of the original file------------------------------- byte[] buf = new byte[65536]; int read = raf.Read(buf, 0, buf.Length); while (read != 0) { rafTemp.Write(buf, 0, read); read = raf.Read(buf, 0, buf.Length); } //rafTemp.getChannel().transferFrom(raf.getChannel(), rafTemp.Position, raf.Length() - raf.Position); //------------------------------------------------------------------ }
public void Put(ByteBuffer buf) { Put(buf.Data); }
private int ReadInteger(ByteBuffer bb, int version) { int value = 0; if(version == Id3v2Tag.ID3V23) value += (bb.Get()& 0xFF) << 24; value += (bb.Get()& 0xFF) << 16; value += (bb.Get()& 0xFF) << 8; value += (bb.Get()& 0xFF); return value; }
//Process the Extended Header in the ID3v2 Tag, returns the number of bytes to skip private int ProcessExtendedHeader(ByteBuffer data) { //TODO Verify that we have an syncsfe int byte[] exthead = new byte [4]; data.Get(exthead); int extsize = ReadInteger(data, Id3v2Tag.ID3V23); // The extended header size includes those first four bytes. data.Position = data.Position + extsize; return extsize; }
public Id3v2Tag Read(Stream mp3Stream) { Id3v2Tag tag; byte[] b = new byte[3]; mp3Stream.Read(b, 0, b.Length); mp3Stream.Seek(0, SeekOrigin.Begin); string ID3 = new string(System.Text.Encoding.ASCII.GetChars(b)); if (ID3 != "ID3") { throw new CannotReadException("Not an ID3 tag"); } //Begins tag parsing --------------------------------------------- mp3Stream.Seek(3, SeekOrigin.Begin); //---------------------------------------------------------------------------- //Version du tag ID3v2.xx.xx string versionHigh=mp3Stream.ReadByte() +""; string versionID3 =versionHigh+ "." + mp3Stream.ReadByte(); //------------------------------------------------------------------------- --- //D?tection de certains flags (A COMPLETER) this.ID3Flags = ProcessID3Flags( (byte) mp3Stream.ReadByte() ); //---------------------------------------------------------------------------- // On extrait la taille du tag ID3 int tagSize = ReadSyncsafeInteger(mp3Stream); //System.err.println("TagSize: "+tagSize); // ------------------NEWNEWNWENENEWNENWEWN------------------------------- //Fill a byte buffer, then process according to correct version b = new byte[tagSize+2]; mp3Stream.Read(b, 0, b.Length); ByteBuffer bb = new ByteBuffer(b); if (ID3Flags[0]==true) { //We have unsynchronization, first re-synchronize bb = synchronizer.synchronize(bb); } if(versionHigh == "2") { tag = v23.Read(bb, ID3Flags, Id3v2Tag.ID3V22); } else if(versionHigh == "3") { tag = v23.Read(bb, ID3Flags, Id3v2Tag.ID3V23); } else if(versionHigh == "4") { throw new CannotReadException("ID3v2 tag version "+ versionID3 + " not supported !"); } else { throw new CannotReadException("ID3v2 tag version "+ versionID3 + " not supported !"); } return tag; }
protected abstract void Create(Tag tag, ByteBuffer buf, IList fields, int tagSize, int padding);