FFmpeg 播放 mp3 和 LRC 时音乐歌词不同步?

2021年9月19日 · 709 字 · 2 分钟

NO. 歌曲 歌词
1 🎵 see you again 📝
2 🎵 那些年 📝

上面两首歌和其对应的歌词,在用 ffmpeg/ffplay 播放的时候,发现第一首两者同步,第二首音乐和歌词不同步。播放命令如下

ffplay -vf subtitles=filename=xxxx.lrc xxxx.mp3

第二首歌和歌词都是从网易云音乐下载的,按理说,两者是匹配的。我用网易云音乐听,两者非常同步。文件没有问题,难道是 ffmpeg/ffplay 的问题?

后来分析了 FFmpeg 中有关 lrc 歌词解码/解析的部分代码。发现问题所在:

从代码(libavformat/lrcdec.c)看,ffmpeg 只能正确处理 [mm:ss.SS] 格式的时间点。

static int64_t read_ts(const char *p, int64_t *start)
{
    int64_t offset = 0;
    uint64_t mm, ss, cs;

    while(p[offset] == ' ' || p[offset] == '\t') {
        offset++;
    }
    if(p[offset] != '[') {
        return 0;
    }
    if(sscanf(p, "[-%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) {
        /* Just in case negative pts, players may drop it but we won't. */
        *start = -(int64_t) (mm*60000 + ss*1000 + cs*10);
    } else if(sscanf(p, "[%"SCNu64":%"SCNu64".%"SCNu64"]", &mm, &ss, &cs) == 3) {
        *start = mm*60000 + ss*1000 + cs*10;
    } else {
        return 0;
    }
    do {
        offset++;
    } while(p[offset] && p[offset-1] != ']');
    return offset;
}

歌词文件 1 中,时间点格式为[mm:ss.SS]

[00:10.61]It's been a long day without you, my friend
[00:17.61]And I'll tell you all about it when I see you again
......

歌词文件 2 中,时间点格式为[mm:ss.SSS]

[00:17.904]又回到最初的起点
[00:20.912]记忆中你青涩的脸
......

我又查了下lrc格式的wiki介绍:LRC (file format),文中有如下描述:

The Line Time Tags are in the format [mm:ss.xx] where mm is minutes, ss is seconds and xx is hundredths of a second.

这么看,应该是歌词文件没有遵守lrc的格式。

当然,如果你要做个播放器,最好还是兼容一下。上述代码可以稍作修改

static int64_t read_ts(const char *p, int64_t *start) {
    int64_t offset = 0;
    uint64_t mm;
    float ss;

    while (p[offset] == ' ' || p[offset] == '\t') {
        offset++;
    }
    if (p[offset] != '[') {
        return 0;
    }
    if (sscanf(p, "[-%" SCNu64":%f]", &mm, &ss) == 2) {
        /* Just in case negative pts, players may drop it but we won't. */
        *start = -(int64_t) (mm * 60000 + (uint64_t) (ss * 1000));
    } else if (sscanf(p, "[%" SCNu64":%f]", &mm, &ss) == 2) {
        *start = mm * 60000 + (uint64_t) (ss * 1000);
    } else {
        return 0;
    }
    do {
        offset++;
    } while (p[offset] && p[offset - 1] != ']');
    return offset;
}

  1. 网易云音乐歌词下载地址:https://music.163.com/api/song/media?id=<123456>