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);
        }
       
       
    }

 

原创粉丝点击