Red5流化MP3遇到问题解决办法
来源:互联网 发布:unity javascript c# 编辑:程序博客网 时间:2024/06/11 09:47
在使用red5 0.8版本时,流化Mp3遇到问题:
日志记录:
[ERROR] [pool-3-thread-1] org.red5.server.messaging.InMemoryPullPullPipe - exception when pulling message from provider
java.lang.IllegalArgumentException
at java.nio.Buffer.position(Buffer.java:218) [na:1.5.0_12]
at org.apache.mina.core.buffer.AbstractIoBuffer.position(AbstractIoBuffer.java:368) [mina-core-2.0.0-M6.jar:na]
at org.red5.io.mp3.impl.MP3Reader.analyzeKeyFrames(MP3Reader.java:629) [red5.jar:na]
at org.red5.io.mp3.impl.MP3Reader.<init>(MP3Reader.java:255) [red5.jar:na]
at org.red5.io.mp3.impl.MP3.getReader(MP3.java:48) [red5.jar:na]
at org.red5.server.stream.provider.FileProvider.init(FileProvider.java:231) [red5.jar:na]
at org.red5.server.stream.provider.FileProvider.pullMessage(FileProvider.java:125) [red5.jar:na]
at org.red5.server.messaging.InMemoryPullPullPipe.pullMessage(InMemoryPullPullPipe.java:74) [red5.jar:na]
at org.red5.server.stream.PlayEngine.pullAndPush(PlayEngine.java:792) [red5.jar:na]
at org.red5.server.stream.PlayEngine$PullAndPushRunnable.run(PlayEngine.java:1458) [red5.jar:na]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:417) [na:1.5.0_12]
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:280) [na:1.5.0_12]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:135) [na:1.5.0_12]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:65) [na:1.5.0_12]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:142) [na:1.5.0_12]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:166) [na:1.5.0_12]
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650) [na:1.5.0_12]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675) [na:1.5.0_12]
at java.lang.Thread.run(Thread.java:595) [na:1.5.0_12]
即问题定位到org.red5.io.mp3.impl.MP3Reader方法analyzeKeyFrames
public synchronized KeyFrameMeta analyzeKeyFrames() {
if (frameMeta != null) {
return frameMeta;
}
// check for cached frame informations
if (frameCache != null) {
frameMeta = frameCache.loadKeyFrameMeta(file);
if (frameMeta != null && frameMeta.duration > 0) {
// Frame data loaded, create other mappings
duration = frameMeta.duration;
frameMeta.audioOnly = true;
posTimeMap = new HashMap<Integer, Double>();
for (int i = 0; i < frameMeta.positions.length; i++) {
posTimeMap.put((int) frameMeta.positions[i],
(double) frameMeta.timestamps[i]);
}
return frameMeta;
}
}
List<Integer> positionList = new ArrayList<Integer>();
List<Double> timestampList = new ArrayList<Double>();
dataRate = 0;
long rate = 0;
int count = 0;
int origPos = in.position();
double time = 0;
in.position(0);
// processID3v2Header();
searchNextFrame();
while (this.hasMoreTags()) {
MP3Header header = readHeader();
if (header == null) {
// No more tags
break;
}
if (header.frameSize() == 0) {
// TODO find better solution how to deal with broken files...
// See APPSERVER-62 for details
break;
}
int pos = in.position() - 4;
if (pos + header.frameSize() > in.limit()) {
// Last frame is incomplete
break;
}
positionList.add(pos);
timestampList.add(time);
rate += header.getBitRate() / 1000;
time += header.frameDuration();
in.position(pos + header.frameSize());//这里异常了
count++;
}
// restore the pos
in.position(origPos);
duration = (long) time;
dataRate = (int) (rate / count);
posTimeMap = new HashMap<Integer, Double>();
frameMeta = new KeyFrameMeta();
frameMeta.duration = duration;
frameMeta.positions = new long[positionList.size()];
frameMeta.timestamps = new int[timestampList.size()];
frameMeta.audioOnly = true;
for (int i = 0; i < frameMeta.positions.length; i++) {
frameMeta.positions[i] = positionList.get(i);
frameMeta.timestamps[i] = timestampList.get(i).intValue();
posTimeMap.put(positionList.get(i), timestampList.get(i));
}
if (frameCache != null) {
frameCache.saveKeyFrameMeta(file, frameMeta);
}
return frameMeta;
}
增加日志打印后,我们发现head.frameSize()为-1536000,帧大小为负值,肯定错了!!!!!!!
研究下代码吧:
这MP3Reader构造方法 先解析文件头部和尾部,是否有ID2和ID1,然后分析数据帧
public MP3Reader(File file) throws FileNotFoundException {
this.file = file;
// parse the id3 info
try {
MP3File mp3file = (MP3File) AudioFileIO.read(file);//解析文件是否有IDV2 IDV1 并解析
MP3AudioHeader audioHeader = (MP3AudioHeader) mp3file.getAudioHeader();
if (audioHeader != null) {
log.debug("Track length: {}", audioHeader.getTrackLength());
log.debug("Sample rate: {}", audioHeader.getSampleRateAsNumber());
log.debug("Channels: {}", audioHeader.getChannels());
log.debug("Variable bit rate: {}", audioHeader.isVariableBitRate());
log.debug("Track length (2): {}", audioHeader.getTrackLengthAsString());
log.debug("Mpeg version: {}", audioHeader.getMpegVersion());
log.debug("Mpeg layer: {}", audioHeader.getMpegLayer());
log.debug("Original: {}", audioHeader.isOriginal());
log.debug("Copyrighted: {}", audioHeader.isCopyrighted());
log.debug("Private: {}", audioHeader.isPrivate());
log.debug("Protected: {}", audioHeader.isProtected());
log.debug("Bitrate: {}", audioHeader.getBitRate());
log.debug("Encoding type: {}", audioHeader.getEncodingType());
log.debug("Encoder: {}", audioHeader.getEncoder());
}
ID3v24Tag idTag = (ID3v24Tag) mp3file.getID3v2TagAsv24();
if (idTag != null) {
// create meta data holder
metaData = new MetaData();
metaData.setAlbum(idTag.getFirstAlbum());
metaData.setArtist(idTag.getFirstArtist());
metaData.setComment(idTag.getFirstComment());
metaData.setGenre(idTag.getFirstGenre());
metaData.setSongName(idTag.getFirstTitle());
metaData.setTrack(idTag.getFirstTrack());
metaData.setYear(idTag.getFirstYear());
//send album image if included
List<TagField> tagFieldList = mp3file.getTag().get(TagFieldKey.COVER_ART);
//fix for APPSERVER-310
if (tagFieldList == null || tagFieldList.isEmpty()) {
log.debug("No cover art was found");
} else {
TagField imageField = tagFieldList.get(0);
if (imageField instanceof AbstractID3v2Frame) {
FrameBodyAPIC imageFrameBody = (FrameBodyAPIC)((AbstractID3v2Frame)imageField).getBody();
if (!imageFrameBody.isImageUrl()) {
byte[] imageBuffer = (byte[]) imageFrameBody.getObjectValue(DataTypes.OBJ_PICTURE_DATA);
//set the cover image on the metadata
metaData.setCovr(imageBuffer);
// Create tag for onImageData event
IoBuffer buf = IoBuffer.allocate(imageBuffer.length);
buf.setAutoExpand(true);
Output out = new Output(buf);
out.writeString("onImageData");
Map<Object, Object> props = new HashMap<Object, Object>();
props.put("trackid", 1);
props.put("data", imageBuffer);
out.writeMap(props, new Serializer());
buf.flip();
//Ugh i hate flash sometimes!!
//Error #2095: flash.net.NetStream was unable to invoke callback onImageData.
ITag result = new Tag(IoConstants.TYPE_METADATA, 0, buf.limit(), null, 0);
result.setBody(buf);
//add to first frames
firstTags.add(result);
}
}
}
} else {
log.info("File did not contain ID3v2 data: {}", file.getName());
}
mp3file = null;
} catch (TagException e) {
log.error("MP3Reader (tag error) {}", e);
} catch (Exception e) {
log.error("MP3Reader {}", e);
}
fis = new FileInputStream(file);
// Grab file channel and map it to memory-mapped byte buffer in
// read-only mode
channel = fis.getChannel();
try {
mappedFile = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel
.size());
} catch (IOException e) {
log.error("MP3Reader {}", e);
}
// Use Big Endian bytes order
mappedFile.order(ByteOrder.BIG_ENDIAN);
// Wrap mapped byte buffer to MINA buffer
in = IoBuffer.wrap(mappedFile);
// Analyze keyframes data
analyzeKeyFrames();
// Create file metadata object
firstTags.addFirst(createFileMeta());
// MP3 header is length of 32 bits, that is, 4 bytes
// Read further if there's still data
if (in.remaining() > 4) {
// Look to next frame
searchNextFrame();
// Set position
int pos = in.position();
// Read header...
// Data in MP3 file goes header-data-header-data...header-data
MP3Header header = readHeader();
// Set position
in.position(pos);
// Check header
if (header != null) {
checkValidHeader(header);
} else {
throw new RuntimeException("No initial header found.");
}
}
}
过程没查出异常,我们继续看一下analyzeKeyFrames() 方法
public void searchNextFrame() {
while (in.remaining() > 1) {
int ch = in.get() & 0xff;
if (ch != 0xff) {
continue;
}
if ((in.get() & 0xe0) == 0xe0) {
// Found it
in.position(in.position() - 2);
return;
}
}
}
//查找特征数据头0xffe0,数据帧的前11个bit为1,算法也是对的
public boolean hasMoreTags() {
MP3Header header = null;
while (header == null && in.remaining() > 4) {
try {
header = new MP3Header(in.getInt());
} catch (IOException e) {
log.error("MP3Reader :: hasMoreTags ::>\n", e);
break;
} catch (Exception e) {
searchNextFrame();
}
}
if (header == null) {
return false;
}
if (header.frameSize() == 0) {
// TODO find better solution how to deal with broken files...
// See APPSERVER-62 for details
return false;
}
if (in.position() + header.frameSize() - 4 > in.limit()) {
// Last frame is incomplete
in.position(in.limit());
return false;
}
in.position(in.position() - 4);
return true;
}
我们再看一下analyzeKeyFrames() 这个方法,在分析帧前,有个in.position(0);跳转到文件开头,如果有IDV2信息应该略过的。后来对出问题的Mp3文件查看,ID2头部信息较多,符合帧头数据解析就出差了,一般的Mp3文件ID3V2信息较少,不会出现问题,特殊的MP3检测出RED5bug,呵呵!!!!!。
对原来算法进行改进
对于有ID3V2的,需要跳过
/**
*@author gzz
*当格式为ID3时,分析帧数据时跳过文件头
*
*/
/**
*@author gzz
*当格式为ID3时,分析帧数据时跳过文件头
*
*/
private void skipFileHead(IoBuffer in){
int pos = in.position();
byte [] buffer = new byte[10];
in.get(buffer);
//头部为ID3时,计算头部长度直接跳过(长度不包括头结构本身10个字节)
int length = 0;
if(buffer[0] == 0X49 && buffer[1] == 0x44 && buffer[2] == 0x33 && buffer[3] == 0x03){
for(int i = 6;i<=9;i++){
length=(length<<7)+buffer[i];
}
log.warn("需要跳过的长度为:{}",length);
in.skip(length);
}
else{
in.position(pos);
}
}
- Red5流化MP3遇到问题解决办法
- fedora无法播放mp3问题解决办法
- Eclipse遇到的问题解决办法
- idea中遇到问题解决办法
- 工作中遇到的问题解决办法
- MyEclipse遇到的一些问题解决办法
- Unity3D发布安卓时遇到的问题解决办法
- 最近遇到的问题解决办法汇总
- 重装MySQL重装遇到的问题解决办法
- xcode7编译webrtc遇到的问题解决办法
- 安装pygraphviz遇到的问题解决办法。
- CentOS升级python后遇到问题解决办法
- mac安装mysql遇到问题解决办法
- 编译LTIB遇到的问题解决办法
- 初学者学MvcMovie遇到的问题解决办法
- 使用Darwin实现mp3文件的点播流化
- 使用最新的cvs及cvsweb,遇到的问题解决办法。
- 安装Visual Studio 2005 SP1 遇到问题解决办法
- SIP基本消息及对应RFC
- GVIML RC LINK
- Memory Problem
- js表单代码大全
- latch free、buffer latch!
- Red5流化MP3遇到问题解决办法
- 从零开始学Android之XML
- linux 命令行 光标移动技巧
- Permission Denial: starting Intent 解决办法
- Networking problem
- 负载测试、压力测试和性能测试的异同
- ALSA的ioctl - hwdep
- VC++的Unicode编程
- 国密SM3杂凑算法与实现