您的位置:首页 > 其它

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

2016-11-01 16:31 323 查看

前言:

在视频播放中,我们经常会遇到多音轨的资源文件,比如某个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 DUR
4000
ATION 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的多音轨实现。

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: