开源视频播放器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的多音轨实现。
相关文章推荐
- Exoplayer使用记录1-播放多音轨视频
- Exoplayer使用记录4-调节音轨中某个频道的声音
- 使用log4net组件记录系统日志
- 使用SQL语句获取数据库中随机N个记录
- 表中记录查询排序随笔(sql server中order by使用方式小总结)
- SQL基础:使用SQL从表中取记录
- 使用Delphi开发多媒体播放音轨问题的FAQ(原创)
- 怎么合并bug_id相同的记录(bugzilla使用的mysql)?
- CP How-To:如何使用cPanel查看站点原始访问记录(Raw Access Logs)
- 使用Application_Error事件处理程序把异常记录到系统事件日志
- 使用Perl生成usmarc记录
- 使用dom4j和XMLHTTP轻松解决多条记录操作
- Community Server 1.0 Beta安装使用记录
- 在.Net程序中使用log4net记录日志(示例)
- VS2005 && MasterPage && Form 的一些相关使用记录
- Community Server 1.0 Beta安装使用记录(二)
- 2004.7.15 [C#学习记录]使用C#编写一个自定义控件
- 使用.NET Framework组件中的DataGrid显示ADO中的RecordSet对象的记录
- MSFlexGrid控件使用点滴记录
- 顺便记录一下groupwise使用gaim