You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JLayer中获取播放器当前帧并实现pause()方法

实现JLayer AdvancedPlayer的暂停/续播功能

我正在用Java编写一款MP3播放器,希望实现pause()方法存储AdvancedPlayer停止时的播放帧,resume()方法从该帧继续播放。但现有的JLayer库(umjammer/jlayer分支)中,Player类只有getFrame()方法,没有支持从指定帧开始播放的play()方法,无法执行play(fromThisFrame, Integer.MAX_VALUE)这类操作。尝试过切换JavaFX但编译失败,阅读库源码、重构代码都没解决问题,这是课程作业的测试分支,仅需实现基础功能。

现有代码

AudioHandler类

public class AudioHandler {
    private AdvancedPlayer player;
    private Thread playThread;
    Bitstream stream; 
    private boolean playing = false;
    private boolean paused;
    private int pausedOnFrame;
    private Track currTrack;

    /*
     * Handling the playing of an mp3 instance
     * @param track Track instance that carries the path that the player can play
     */
    public void play(Track track){
        String path = track.getPath();
        currTrack = track; 

        if(!playing){
            playing = true;
            paused = false;
            pausedOnFrame = 0; 
// Thread handling is needed cause I will implement a SwiftGUI 
            playThread = new Thread(() -> {
                try(FileInputStream fis = new FileInputStream(path)) {
                    stream = new Bitstream(fis);
                    player = new AdvancedPlayer(fis);
                    player.play(pausedOnFrame, Integer.MAX_VALUE); 
                }
                catch (JavaLayerException | IOException e) { e.printStackTrace(); } 
                finally{ 
                    playing = false;
                    pausedOnFrame = 0;
                }
            });
            playThread.start();
        }
        else System.out.println("Already playing.");
    } 

    /*
     * Stops the thread and the player
     * Sets the playing to false
     */
    public void stop() {
    // ...
    } 

    /*
     * Sets the paused to true
     * Closes the player == pauses the player at the frame
     */
    public void pause() throws BitstreamException {
        if (playing && !paused) {
            System.out.println(stream.readFrame());
            player.close(); 
            paused = true;
        }
    }

    /*
     * Resumes playback from where it was paused
     */
    public void resume() {
        if (paused) {
            paused = false; 
            play(currTrack, pausedOnFrame); // Start playing again from the saved path
        }
    }
}

Main类

import java.io.*;

public class Main {
    public static void main(String[] args) {
        try{
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            // 修正语法错误:假设Track有接收路径的构造方法
            Track asd = new Track("/path/to/song.mp3");
            AudioHandler plr = new AudioHandler(); 
           
            String input;
            while(true){
                input = reader.readLine();
                if(input.equals("exit")) System.exit(0);
                if(input.equals("play")) plr.play(asd);
                if(input.equals("stop")) plr.stop();
                if(input.equals("pause")) plr.pause();
                if(input.equals("resume")) plr.resume();
            }
        } 
        catch(Exception e){}
    }
}

解决方案

针对umjammer/jlayer库,我们可以通过自定义AdvancedPlayer子类统计已播放帧数,并在续播时跳过对应帧数的方式实现暂停/续播功能,具体修改如下:

1. 修改AudioHandler成员变量

添加记录已播放帧数的变量:

public class AudioHandler {
    private AdvancedPlayer player;
    private Thread playThread;
    Bitstream stream; 
    private boolean playing = false;
    private boolean paused;
    private int playedFrames = 0; // 新增:记录已播放帧数
    private Track currTrack;
    // ... 其他代码
}

2. 自定义CountingAdvancedPlayer子类

在AudioHandler内部定义继承自AdvancedPlayer的子类,用于统计已播放帧数:

private class CountingAdvancedPlayer extends AdvancedPlayer {
    private volatile int framesPlayed = 0;

    public CountingAdvancedPlayer(InputStream in) throws JavaLayerException {
        super(in);
    }

    @Override
    protected boolean decodeFrame() throws JavaLayerException {
        boolean decoded = super.decodeFrame();
        if (decoded) {
            framesPlayed++;
        }
        return decoded;
    }

    public int getFramesPlayed() {
        return framesPlayed;
    }
}

3. 重构play方法,支持起始帧偏移

添加重载的play方法,实现从指定帧开始播放的逻辑:

// 原有play方法,默认从第0帧开始
public void play(Track track){
    play(track, 0);
}

// 新增重载方法,支持从指定帧开始播放
public void play(Track track, int startFrame){
    String path = track.getPath();
    currTrack = track; 

    if(!playing){
        playing = true;
        paused = false;
        playedFrames = startFrame;

        playThread = new Thread(() -> {
            try(FileInputStream fis = new FileInputStream(path)) {
                // 使用自定义的计数播放器
                player = new CountingAdvancedPlayer(fis);
                // 跳过起始帧之前的所有帧
                skipFrames(startFrame);
                // 播放剩余所有帧
                player.play(Integer.MAX_VALUE); 
            }
            catch (JavaLayerException | IOException e) { e.printStackTrace(); } 
            finally{ 
                playing = false;
                // 非暂停状态下重置计数
                if(!paused){
                    playedFrames = 0;
                }
            }
        });
        playThread.start();
    }
    else System.out.println("Already playing.");
}

// 新增跳过指定帧数的方法
private void skipFrames(int frames) throws JavaLayerException {
    if(frames <= 0) return;
    Bitstream bitstream = ((CountingAdvancedPlayer) player).getBitstream();
    for(int i=0; i<frames; i++){
        Header header = bitstream.readFrame();
        if(header == null){
            break; // 到达文件末尾,停止跳过
        }
        bitstream.closeFrame();
    }
}

4. 修改pause方法,记录暂停时的帧数

在暂停时停止播放器并记录当前已播放帧数:

public void pause() throws JavaLayerException {
    if (playing && !paused) {
        player.stop(); // 停止播放
        // 记录当前已播放帧数
        playedFrames = ((CountingAdvancedPlayer) player).getFramesPlayed();
        player.close();
        paused = true;
        playing = false;
    }
}

5. 修改resume方法,从记录的帧数续播

直接调用重载的play方法,传入记录的起始帧:

public void resume() {
    if (paused) {
        paused = false; 
        play(currTrack, playedFrames);
    }
}

内容的提问来源于stack exchange,提问作者AutisticAllosaurus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:38:13