开源视频播放器IjkPlayer使用记录之(四)--多音轨的探路之旅

前言:

在视频播放中,我们经常会遇到多音轨的资源文件,比如某个mkv文件同时支持英语/国语,那么最好是能够进行音轨的切换。在IjkPlayer中并没有支持多音轨的代码,所以在移植的过程中,需要自己编写代码,获取多音轨的相关数据。
先感谢一下https://github.com/Bilibili/ijkplayer/issues/72,本文中的代码基本都是copy的Peterede commented on 7 May 2015的回答。

Ijkplayer的代码结构简介及多音轨的实现:

IjkPlayer是基于ffmpeg实现的,如果希望修改ijkplayer获取ffmpeg的内容,那么必须了解Ijkplayer获取ffmpeg数据的大概流程。
下面分层介绍Ijkplayer的主要代码结构

Ijkplayer的java层:IjkMediaPlayer.java

代码主要就是Ijkplayer java的所有接口实现,通过接口访问ndk  c的内容。在本例中,需要添加查询音轨数目,设置当前音轨,查询当前音轨内容这三个接口。这块只是接口封装,没有具体内容,就不详细介绍了。

Ijkplayer的jni层:Ijkplayer_jni.c

好了,从这里开始所有的代码都是c代码了。
static JNINativeMethod g_methods[] = {
        {
                "_setDataSource",
                                       "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
                                                                                                 (void *) IjkMediaPlayer_setDataSourceAndHeaders
        },
在 g_methods中添加对应的方法,如添加获取音轨数目的接口:
 {       "_getAudioTrack",      "()I",                                                    (void *) IjkMediaPlayer_getAudioTrack},

这一层的实现代码如下:

static int IjkMediaPlayer_getAudioTrack(JNIEnv *env, jobject thiz) {
    MPTRACE("%s\n", __func__);
    jint ret = 0;
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
    JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: start: null mp", LABEL_RETURN);
    ret = ijkmp_get_audio_track(mp);

    LABEL_RETURN:
    ijkmp_dec_ref_p(&mp);
    return ret;
}
获取当前的mediaplayer对象,然后调用Ijkplayer实现层的接口去获取内容。


Ijkplayer的实现层:ijkplayer.c

这里需要补充说明一下,ijkplayer是基于ffmpeg的,播放器是基于ffplay的,所以在ijkplayer初始化的时候会获取1个ffplay的实例。具体代码如下:
  IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
    if (!mp)
        goto fail;

    mp->ffplayer = ffp_create();
其实我们获取音轨之类的内容都是在这个实例上获取的。这一层主要就是把相关的信息透传给ff_player来处理。
这里获取音轨数目的代码如下:
int ijkmp_get_audio_track(IjkMediaPlayer *mp)
{
    assert(mp);
    pthread_mutex_lock(&mp->mutex);
    int ret=ffp_get_audio_track_info_l(mp->ffplayer);
    pthread_mutex_unlock(&mp->mutex);
    return ret;
}

ffplay的实现:ffplay.c

在这一层来实现具体的代码。
代码请参见https://github.com/Bilibili/ijkplayer/issues/72。
这样,我们已经可以获取到这个视频有几个音轨,并可以对其进行设置了。
但是,这样有个问题了,音轨知道有几个,怎么区分呢?

多音轨信息的获取

基于mpeg,mpeg提供ffprobe读取视频的资源内容:
具体信息可以见下:
ffprobe -i test.mkv
ffprobe version N-82166-g894e7ef Copyright (c) 2007-2016 the FFmpeg developers
  built with Apple LLVM version 8.0.0 (clang-800.0.38)
  configuration: --disable-yasm
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 65.100 / 57. 65.100
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
Input #0, matroska,webm, from 'test.mkv':
  Metadata:
    encoder         : libebml v1.3.1 + libmatroska v1.4.2
    creation_time   : 2016-09-14T13:42:18.000000Z
  Duration: 01:50:20.39, start: 0.000000, bitrate: 3223 kb/s
    Stream #0:0: Video: h264 (High), yuv420p(tv, bt709, progressive), 1920x816, SAR 1:1 DAR 40:17, 24.04 fps, 24.04 tbr, 1k tbn, 48 tbc (default)
    Metadata:
      BPS             : 2965476
      BPS-eng         : 2965476
      DURATION        : 01:50:20.054000000
      DURATION-eng    : 01:50:20.054000000
      NUMBER_OF_FRAMES: 158242
      NUMBER_OF_FRAMES-eng: 158242
      NUMBER_OF_BYTES : 2453951583
      NUMBER_OF_BYTES-eng: 2453951583
      _STATISTICS_WRITING_APP: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_APP-eng: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_DATE_UTC: 2016-09-14 13:42:18
      _STATISTICS_WRITING_DATE_UTC-eng: 2016-09-14 13:42:18
      _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
      _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
    Stream #0:1(chi): Audio: aac (LC), 44100 Hz, stereo, fltp (default)
    Metadata:
      title           : 粤语
      BPS             : 127484
      BPS-eng         : 127484
      DURATION        : 01:50:20.268000000
      DURATION-eng    : 01:50:20.268000000
      NUMBER_OF_FRAMES: 283964
      NUMBER_OF_FRAMES-eng: 283964
      NUMBER_OF_BYTES : 105498100
      NUMBER_OF_BYTES-eng: 105498100
      _STATISTICS_WRITING_APP: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_APP-eng: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_DATE_UTC: 2016-09-14 13:42:18
      _STATISTICS_WRITING_DATE_UTC-eng: 2016-09-14 13:42:18
      _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
      _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
    Stream #0:2(chi): Audio: aac (LC), 44100 Hz, stereo, fltp
    Metadata:
      title           : 国语
      BPS             : 127482
      BPS-eng         : 127482
      DURATION        : 01:50:20.390000000
      DURATION-eng    : 01:50:20.390000000
      NUMBER_OF_FRAMES: 283964
      NUMBER_OF_FRAMES-eng: 283964
      NUMBER_OF_BYTES : 105498100
      NUMBER_OF_BYTES-eng: 105498100
      _STATISTICS_WRITING_APP: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_APP-eng: mkvmerge v8.3.0 ('Over the Horizon') 64bit
      _STATISTICS_WRITING_DATE_UTC: 2016-09-14 13:42:18
      _STATISTICS_WRITING_DATE_UTC-eng: 2016-09-14 13:42:18
      _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
      _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES


ffmpeg的下载和编译这里就不说了,可以自行baidu。可以看到title可以清楚的区分出audio stream.
如果对前面代码有研究过,那么应该知道每个stream对应的是一个AVStream,AVStream中有 AVDictionary *metadata;
   这个字段对应的就是上面的MetaData。获取内容的代码参考了IjkPlayer获取MetaData的代码结构。
   构建了一个新的IjkStreamMediaMeta,结构源自:IjkMediaMeta。
   附一下我的代码中主要函数:
IjkStreamMediaMeta *ffp_get_audio_meta_l(FFPlayer *ffp,int index)
{
    if (!ffp)
        return NULL;
    assert(ffp);
    VideoState *is = ffp->is;
    int total = 0;
    if (!is)
        return EIJK_NULL_IS_PTR;

    AVFormatContext *ic = is->ic;
    int start_index, stream_index;
    AVStream *st;
    int codec_type = AVMEDIA_TYPE_AUDIO;

    if (codec_type == AVMEDIA_TYPE_VIDEO)
        start_index = is->video_stream;
    else if (codec_type == AVMEDIA_TYPE_AUDIO)
        start_index = is->audio_stream;
/*else
    start_index = is->subtitle_stream;
if (start_index < (codec_type == AVMEDIA_TYPE_SUBTITLE ? -1 : 0))
    return;*/

    for (stream_index = 0; stream_index < is->ic->nb_streams; stream_index++)
    {
        st = ic->streams[stream_index];
        if (st->codec->codec_type == codec_type) {
            /* check that parameters are OK */
            switch(codec_type) {
                case AVMEDIA_TYPE_AUDIO:
                    if (st->codec->sample_rate != 0 &&
                        st->codec->channels != 0)
                        total++;
                    if (total == index)
                        goto the_end;
                    break;

            }
        }
    }
    return NULL;
    the_end:
        return ijkstreammeta_get_streammeta_l(st->metadata);
}

IjkStreamMediaMeta *ijkstreammeta_get_streammeta_l(AVDictionary *meta)
{
    if (!meta)
        return NULL;
    IjkStreamMediaMeta *stream_meta = NULL;
    stream_meta = ijkstreammeta_create();
    if (!stream_meta) {
        ijkstreammeta_destroy_p(&stream_meta);
        return NULL;
    }
    AVDictionaryEntry *entry=av_dict_get(meta,"title",NULL,0);
    char *title="";
    if (!entry  || !entry->value)
    {
        title="";
    }
    else
    {
        title=entry->value;
    }
    ijkstreammeta_set_string_l(stream_meta, IJKSTREAMM_KEY_FORMAT, title);
    return stream_meta;
}

以上就是基于Ijkplayer的多音轨实现。
   

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值