no commit message
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
package org.mangui.hls.demux {
|
||||
//import by.blooddy.crypto.Base64;
|
||||
import com.hurlant.util.Base64;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
CONFIG::LOGGING {
|
||||
import org.mangui.hls.utils.Log;
|
||||
}
|
||||
|
||||
public class ID3 {
|
||||
public var len : int;
|
||||
public var hasTimestamp : Boolean = false;
|
||||
public var timestamp : Number;
|
||||
public var tags : Vector.<ID3Tag> = new Vector.<ID3Tag>();
|
||||
|
||||
/* create ID3 object by parsing ByteArray, looking for ID3 tag length, and timestamp */
|
||||
public function ID3(data : ByteArray) {
|
||||
var tagSize : uint = 0;
|
||||
try {
|
||||
var pos : uint = data.position;
|
||||
var header : String;
|
||||
do {
|
||||
header = data.readUTFBytes(3);
|
||||
if (header == 'ID3') {
|
||||
// skip 24 bits
|
||||
data.position += 3;
|
||||
// retrieve tag(s) length
|
||||
var byte1 : uint = data.readUnsignedByte() & 0x7f;
|
||||
var byte2 : uint = data.readUnsignedByte() & 0x7f;
|
||||
var byte3 : uint = data.readUnsignedByte() & 0x7f;
|
||||
var byte4 : uint = data.readUnsignedByte() & 0x7f;
|
||||
tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4;
|
||||
var endPos : uint = data.position + tagSize;
|
||||
CONFIG::LOGGING {
|
||||
Log.debug2("ID3 tag found, size/end pos:" + tagSize + "/" + endPos);
|
||||
}
|
||||
// read ID3 tags
|
||||
_parseID3Frames(data, endPos);
|
||||
data.position = endPos;
|
||||
} else if (header == '3DI') {
|
||||
// http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer
|
||||
data.position += 7;
|
||||
CONFIG::LOGGING {
|
||||
Log.debug2("3DI footer found, end pos:" + data.position);
|
||||
}
|
||||
} else {
|
||||
data.position -= 3;
|
||||
len = data.position - pos;
|
||||
CONFIG::LOGGING {
|
||||
if (len) {
|
||||
Log.debug2("ID3 len:" + len);
|
||||
if (!hasTimestamp) {
|
||||
Log.warn("ID3 tag found, but no timestamp");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (true);
|
||||
} catch(e : Error) {
|
||||
}
|
||||
len = 0;
|
||||
return;
|
||||
};
|
||||
|
||||
/* Each Elementary Audio Stream segment MUST signal the timestamp of
|
||||
its first sample with an ID3 PRIV tag [ID3] at the beginning of
|
||||
the segment. The ID3 PRIV owner identifier MUST be
|
||||
"com.apple.streaming.transportStreamTimestamp". The ID3 payload
|
||||
MUST be a 33-bit MPEG-2 Program Elementary Stream timestamp
|
||||
expressed as a big-endian eight-octet number, with the upper 31
|
||||
bits set to zero.
|
||||
*/
|
||||
private function _parseID3Frames(data : ByteArray, endPos : uint) : void {
|
||||
while(data.position + 8 <= endPos) {
|
||||
var tag_id : String = data.readUTFBytes(4);
|
||||
var tag_len : int = data.readUnsignedInt();
|
||||
var tag_flags : int = data.readUnsignedShort();
|
||||
|
||||
CONFIG::LOGGING {
|
||||
Log.debug("ID3 tag id:" + tag_id);
|
||||
}
|
||||
switch(tag_id) {
|
||||
case "PRIV":
|
||||
// owner should be "com.apple.streaming.transportStreamTimestamp"
|
||||
if (data.readUTFBytes(44) == 'com.apple.streaming.transportStreamTimestamp') {
|
||||
// smelling even better ! we found the right descriptor
|
||||
// skip null character (string end) + 3 first bytes
|
||||
data.position += 4;
|
||||
// timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero.
|
||||
var pts_33_bit : int = data.readUnsignedByte() & 0x1;
|
||||
hasTimestamp = true;
|
||||
timestamp = (data.readUnsignedInt() / 90);
|
||||
if (pts_33_bit) {
|
||||
timestamp += 47721858.84; // 2^32 / 90
|
||||
}
|
||||
timestamp = Math.round(timestamp);
|
||||
CONFIG::LOGGING {
|
||||
Log.debug("ID3 timestamp found:" + timestamp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
var tag_data : * = null;
|
||||
var base64 : Boolean;
|
||||
var firstChar : String = tag_id.charAt(0);
|
||||
if(firstChar == 'T' || firstChar == 'W') {
|
||||
base64 = false;
|
||||
var cur_tag_pos : int;
|
||||
var end_tag_pos : int = data.position + tag_len;
|
||||
// skip character encoding byte
|
||||
data.position++;
|
||||
|
||||
while(data.position < end_tag_pos - 1) {
|
||||
cur_tag_pos = data.position;
|
||||
var text : String = data.readUTFBytes(end_tag_pos-cur_tag_pos);
|
||||
if(tag_data) {
|
||||
tag_data += ',' + text;
|
||||
} else {
|
||||
tag_data = text;
|
||||
}
|
||||
data.position = cur_tag_pos + text.length + 1;
|
||||
}
|
||||
data.position = end_tag_pos;
|
||||
CONFIG::LOGGING {
|
||||
Log.debug2("id/data:" + tag_id + "/" + tag_data);
|
||||
}
|
||||
} else {
|
||||
base64 = true;
|
||||
tag_data = new ByteArray();
|
||||
data.readBytes(tag_data, 0, tag_len);
|
||||
tag_data=Base64.encode(tag_data);
|
||||
}
|
||||
var id3tag : ID3Tag = new ID3Tag(tag_id,tag_flags,base64,tag_data);
|
||||
tags.push(id3tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user