Android视频播放数据读取的流程
- 格式:doc
- 大小:71.50 KB
- 文档页数:15
2020.03.27 v1.2.0 beta版:更新说明:一、完美支持android1.五、android1.六、android2.0、android2.0一、android2.1平台;2、完美支持320×480、480×800、480×854等各类分辨率(自适应屏幕分辨率);3、支持在线音视频播放,支持URL input和从浏览器调用SeeJoPlayer播放器播放在线音视频;4、自动转为横屏播放,为用户提供更好的观看体验;5、修改了没有SD卡程序出错的Bug;6、美化了视频播放列表和操作说明的界面。
第一部份:功能介绍SeeJoPlayer的优势要紧在相对还算美观的界面和便利的交互操作上。
先说操作吧,它支持:一、全屏切换: 双击屏幕二、播放/暂停: 长按屏幕3、静音/恢复: 长按音量按钮4、播放列表: 操纵面板最右边的按钮(暂不支持编辑功能)五、音量调剂: 单击音量按钮,在弹出的音量显示区域触摸改变音量这些操作和PC上的播放器较为类似,希望大伙儿能用得适应。
至于界面的话,多说无益,直接上图吧:第二部份:源码解析一、VideoView与视频比例缩放:咱们能够很方便的取得VideoView的源代码,最简单的方式是直接在上找“.java”。
因此重写VideoView的进程其实只是在原先的基础上进行一些修改罢了,并非一个很麻烦的工作。
什么缘故Android自带的VideoView会维持视频的长宽比而不能让咱们很方便的自概念比例呢?我猜想可能Google 做Android也是一个很仓促的工程,许多代码并无考虑得太成熟。
VideoView的源码中有如此一段代码:1 @Override2 3 4 5 6 7 8 9101112131415161718192021222324252627protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec){//Log.i("@@@@", "onMeasure");int width = getDefaultSize(mVideoWidth, widthMeasureSpec);int height = getDefaultSize(mVideoHeight, heightMeasureSpec);if(mVideoWidth >0&& mVideoHeight >0){if( mVideoWidth * height > width * mVideoHeight ){//Log.i("@@@", "image too tall, correcting");height = width * mVideoHeight / mVideoWidth;}elseif( mVideoWidth * height < width * mVideoHeight ){//Log.i("@@@", "image too wide, correcting");width = height * mVideoWidth / mVideoHeight;}else{//Log.i("@@@", "aspect ratio is correct: " +//width+"/"+height+"="+//mVideoWidth+"/"+mVideoHeight);}}//Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height); setMeasuredDimension(width, height);}这确实是什么缘故长宽比不能改变的缘故了。
Android多媒体MediaPlayer使⽤详解现在的⼿机功能越来越丰富了,遥想10年前,MP3,MP4,MP5,还是很流⾏的,博主当时读⾼中时很想拥有⼀台,可以听⾳乐和看电影。
可是条件有限,学校也禁⽌此东西,所以只能偷偷的玩。
⽽现在我们的⼿机也很早以前就⽀持了这些功能,⽽且界⾯和功能也远远超过了MP4。
好吧,说多了,今天本⽂介绍的是Andriod系统⾃带的Mediaplayer,和VideoView实现简单的⾳乐和视频的播放,⾄于想做出如酷狗⾳乐这样的APP的话,只要想做,应该也不难,都是基于此实现了功能的扩展。
Android的MediaPlayer包含了Audio和Video的播放功能,在Android的界⾯上,Music和Video两个应⽤程序都是调⽤MediaPlaer来实现的。
⼀、播放⾳频⽂件⾸先看看MediaPlaer的⽣命周期下⾯是MediaPlayer提供的常⽤⽅法⽅法说明MediaPlayer构造⽅法create创建⼀个要播放的多媒体getCurrentPosition得到当前播放位置getDuration得到⽂件的时间getVideoHeight得到视频的⾼度getVideoWidth得到视频的宽度isLooping是否循环播放isPlaying是否正在播放pause暂停prepare准备(同步)prepareAsync准备(异步)release释放MediaPlayer对象相关的资源reset重置MediaPlayer对象为刚刚创建的状态seekTo指定播放的位置(以毫秒为单位的时间)setAudioStreamType设置流媒体的类型setDataSource设置多媒体数据来源(位置)setDisplay设置⽤SurfaceHolder来显⽰多媒体setLooping设置是否循环播放setOnButteringUpdateListener⽹络流媒体的缓冲监听setOnErrorListener设置错误信息监听setOnVideoSizeChangedListener视频尺⼨监听setScreenOnWhilePlaying设置是否使⽤SurfaceHolder来保持屏幕显⽰setVolume设置⾳量start开始播放stop停⽌播放MediaPlayer的⼯作流程是这样的:1,⾸先创建MediaPlaer对象; *2,然后调⽤setDataSource()⽅法来设置⾳频⽂件的路径;**3,再调⽤prepare()⽅法使MediaPlayer进⼊到准备状态;4,调⽤start⽅法就可以播放⾳频。
Android读写文件权限流程概述在A nd ro id开发中,应用程序需要获取特定的权限以便能够读取和写入设备上的文件。
本文将介绍An dr oi d中读写文件权限的流程,帮助开发者了解如何正确处理文件访问权限相关的问题。
为什么需要文件权限A n dr oi d系统为了保护用户的隐私和数据安全,对应用程序的文件访问进行了限制。
如果应用程序想要读取或写入设备上的文件,必须首先获取相应的权限。
否则,在没有权限的情况下进行文件操作将会引发安全异常。
文件读取权限步骤1:在A n d r o i dM a n i f e s t.x m l中声明权限要读取设备上的文件,首先需要在应用的清单文件(A nd ro id Ma ni fe st.xm l)中声明文件读取权限。
可以使用以下代码将读取权限添加到清单文件中:<u se s-p e rm is si on an dr oid:na me="an dr oi d.p e rm is si on.R EA D_E X TE RN AL_ S T OR AG E"/>这个权限允许应用程序读取外部存储(如S D卡)上的文件。
步骤2:检查权限在代码中进行文件读取操作前,需要先检查应用是否已经获得了文件读取权限。
可以使用以下代码来检查权限:i f(C on te xt Co mp at.c he ck Se lf Pe rm iss i on(c on te xt,M ani f es t.p e r mi ss io n.RE AD_EX T ER NA L_ST OR AG E)!=Pa ck ag eM an ag er.P ER MI SS I O N_G RA NT ED){//没有读取权限,请求权限}e ls e{//已经有读取权限,进行文件读取操作}步骤3:请求权限如果在步骤2中检查到没有读取权限,应该向用户请求获取该权限。
android读写文件权限流程
一、 Android读写文件权限介绍
Android SDK提供了文件 I/O 的功能,它使开发人员可以使用简单
的API操作本地文件系统。
在Android 6.0 及更高版本中,对文件I/O,
用户必须显式地获取权限才能继续进行操作。
Android针对本地文件读写,提供了相应的权限控制,主要有类似的
内容:
1、读取外部存储文件权限:
2、写入外部存储文件权限:
二、Android文件读写权限流程
Android读写文件权限的整个流程如下:
1、应用开发者在AndroidManifest文件中声明
“android.permission.READ_EXTERNAL_STORAGE”和
“android.permission.WRITE_EXTERNAL_STORAGE”权限;
2、当用户启动应用的时候,系统会检测应用是否声明了需要申请的
权限;
3、如果声明了需要申请的权限,系统就会向用户发出权限请求提示;
4、用户选择同意或拒绝;
5、如果用户同意,系统会授予该应用相应的权限;
6、如果用户拒绝,应用就无法使用该权限。
三、Android文件读写权限的注意事项
1、在Android 6.0及更高版本中,开发者需要在AndroidManifest 中声明需要申请的权限;
2、在申请权限的时候,要确保用户理解其申请的权限;
3、如果用户拒绝权限,应用就无法使用该权限;。
移动应用中的视频播放功能实现方法随着智能手机的广泛应用,移动应用程序的开发也日益火热。
在这些应用程序中,视频播放功能成为了用户追求的一个重要特性。
在这篇文章中,我们将探讨移动应用中实现视频播放功能的方法。
一、选择合适的视频播放器库为了实现视频播放功能,开发人员需要选择合适的视频播放器库。
目前市场上有多种视频播放器库可供选择,如ExoPlayer、Vitamio等。
这些库提供了丰富的功能和API,方便开发人员进行视频播放器的自定义和扩展。
开发人员可以根据项目需求选择最适合的视频播放器库。
二、视频格式的支持移动设备支持的视频格式有限,开发人员需要确保所使用的视频播放器库支持所需的视频格式。
通常,移动设备支持的视频格式包括MP4、MOV、AVI等常见的格式。
在开发移动应用时,开发人员应尽量使用这些常见的视频格式,以确保视频可以在多数移动设备上正确播放。
三、视频文件的下载和存储为了实现视频播放功能,开发人员需要考虑视频文件的下载和存储。
在移动应用中,常见的做法是将视频文件存储在应用的本地存储空间中,方便离线播放。
开发人员可以使用Android的下载管理器或iOS的NSURLSession等工具来实现视频文件的下载,然后将文件保存到应用的本地存储空间中。
四、视频流的播放除了播放本地视频文件,移动应用还可以播放网络上的视频流。
为了实现这一功能,开发人员需要使用流媒体服务器来存储和传输视频流,并使用视频播放器库来播放与流媒体服务器交互的视频流。
流媒体服务器可以是开源的,如NGINX、Wowza等,也可以是商业产品。
开发人员需要根据项目需求选择最适合的流媒体服务器。
五、视频播放控件的界面设计在移动应用中,视频播放功能通常需要与用户界面进行交互。
为了提供良好的用户体验,开发人员需要设计合适的视频播放控件界面。
这些控件可以包括播放/暂停按钮、进度条、音量控制等。
开发人员应根据应用的设计风格和用户需求,设计出符合用户期望的视频播放控件界面。
Android视频播放数据读取的流程在Android平台上,视频播放数据的读取流程主要包括以下几个步骤:1. 创建MediaExtractor对象:MediaExtractor是Android中用于从多种媒体文件中读取数据的类。
通过调用其构造函数并传入媒体文件的路径,可以创建一个用于读取视频数据的MediaExtractor对象。
2. 选择数据轨道:对于包含视频、音频或字幕等多个轨道的媒体文件,需要选择需要读取的数据轨道。
可以通过调用MediaExtractor的getTrackCount(方法获取媒体文件的轨道数,然后通过调用selectTrack(方法选择需要读取的轨道。
3. 创建MediaCodec对象:MediaCodec是Android中用于解码媒体数据的类。
可以通过调用createDecoderByType(方法并传入媒体文件中对应轨道的MIME类型,来创建一个用于解码视频数据的MediaCodec对象。
4. 配置MediaCodec对象:配置MediaCodec对象包括设置解码器的输入格式和输出格式。
可以通过调用MediaExtractor的getTrackFormat(方法获取当前数据轨道的格式,并将其设置为MediaCodec的输入格式;同时,还需要设置解码后的数据输出的Surface。
5. 启动MediaCodec对象:配置完成后,需要调用MediaCodec的start(方法启动解码器。
6. 获取输入缓冲区:对于解码器的输入数据,需要先获取一个可用的输入缓冲区。
可以通过调用MediaCodec的getInputBuffers(方法来获取解码器的输入缓冲区数组,然后根据返回的索引获取一个可用的输入缓冲区。
7. 读取数据至输入缓冲区:将从MediaExtractor获取的待解码的数据读取至输入缓冲区中。
可以通过调用MediaExtractor的readSampleData(方法,传入一个ByteBuffer对象,来将媒体数据读取至ByteBuffer中。
Android开发中的视频播放和流媒体处理技术在当今数字化时代,视频播放和流媒体处理技术在移动应用开发中扮演着至关重要的角色。
无论是社交媒体、在线教育还是即时通讯应用,视频都是用户最常用的交流和娱乐形式之一。
为了提供高质量的视频体验,Android开发者需要掌握视频播放和流媒体处理的关键技术。
一、视频播放技术1. 视频解码视频解码是将视频文件中的压缩数据还原为可视的图像的过程。
Android系统支持多种视频解码器,如、和VP9等。
开发者可以根据需求选择合适的解码器,并通过硬件加速来提高解码效率,以提供流畅的视频播放。
2. 播放器框架Android提供了多个播放器框架,其中最常用的是MediaPlayer 和ExoPlayer。
MediaPlayer是Android原生的播放器,简单易用,但功能比较有限。
ExoPlayer是Google推荐的高级播放器框架,支持更多的媒体格式和功能,如自适应流媒体、倍速播放和跳转到指定位置等。
3. 视频控制在视频播放过程中,用户通常需要进行一些控制操作,如播放/暂停、快进/快退和调整音量等。
开发者可以通过控制播放器的API来实现这些功能,并根据用户交互来更新播放器界面。
二、流媒体处理技术1. 流媒体协议在实现视频流媒体处理时,选择合适的流媒体协议对于提供良好的用户体验至关重要。
目前常用的流媒体协议包括HTTP、RTSP和RTMP 等。
HTTP协议广泛应用于各类互联网视频平台,RTSP协议适用于实时流媒体传输,RTMP协议则主要用于低延迟的直播推流。
2. 自适应码率自适应码率是一种动态调整视频码率的技术,可根据网络状况选择合适的码率以保证视频的连续播放和流畅性。
开发者可以通过使用自适应码率算法,并与流媒体服务器进行通信,动态改变视频码率和分辨率。
3. 实时编码实时编码是指将实时音视频数据转换为特定格式的过程,如和AAC等。
在实时通讯和直播应用中,实时编码是必不可少的,要求编码器能够实时处理庞大的音视频数据流。
android android读取资源文件的方法Android开发中,我们经常需要读取应用程序中的资源文件,如图片、文字、xml等。
Android提供了一套API供我们方便地读取这些资源文件。
本文将介绍几种读取资源文件的方法。
一、读取图片资源文件1.通过资源ID读取图片资源在我们的应用程序中,通常会将图片存放在res/drawable文件夹下。
我们可以通过资源ID来获取这些图片。
```ImageView imageView = findViewById(R.id.imageView);imageView.setImageResource(R.drawable.my_image);```其中,R.drawable.my_image是图片资源文件的ID。
通过调用setImageResource方法,我们可以将该资源文件设置给ImageView。
2.通过文件名读取图片资源有时候,我们可能需要根据文件名来读取图片资源。
可以使用以下方法来实现:```String fileName = "my_image.png";Resources resources = getResources();int resId = resources.getIdentifier(fileName, "drawable", getPackageName());imageView.setImageResource(resId);```首先,我们需要获取Resources对象,然后调用getIdentifier方法,传入文件名、资源类型和包名,最后获取到资源ID。
接下来,就可以通过setImageResource方法将资源文件设置给ImageView。
二、读取文字资源文件在应用程序中,我们可能需要读取文字资源文件,如strings.xml。
可以使用以下方法来读取:```String textValue =getResources().getString(R.string.my_text);textView.setText(textValue);```其中,R.string.my_text是文字资源文件的ID。
Android读取本地照⽚和视频相册实例代码前⾔项⽬中经常要选择本地照⽚或者视频的需求,如果去扫描整个SD卡就太耗时间,其实Android系统在启动时就已经把整个设备中的多媒体⽂件信息(⽂件名,类型,⼤⼩等)都存到了数据库,然后提供了ContentPrivider这个API来管理这个数据库,我们可以利⽤ContentPrivider来获取所有的照⽚和视频。
ContentPrivider初识先看下管理的的数据库在哪data/data/⽬录下:有很多这种⽂件夹(⽇历,联系⼈,下载管理,多媒体等)我们需要的照⽚和视频就在media下⾯,进去看看。
进去找到database然后打开external.db,就可以看到多张表(⾳频,⽂件,Log,图像,视频等)照⽚相册那么获取照⽚直接通过 ContentProvider读取Images这个数据库就OK了,这⾥开启⼯作线程读取所有.jpeg和.png的图⽚,附上代码段:/*** 读取⼿机中所有图⽚信息*/private void getAllPhotoInfo() {new Thread(new Runnable() {@Overridepublic void run() {List<MediaBean> mediaBeen = new ArrayList<>();HashMap<String,List<MediaBean>> allPhotosTemp = new HashMap<>();//所有照⽚Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;String[] projImage = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA,MediaStore.Images.Media.SIZE,MediaStore.Images.Media.DISPLAY_NAME};Cursor mCursor = getContentResolver().query(mImageUri,projImage,MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",new String[]{"image/jpeg", "image/png"},MediaStore.Images.Media.DATE_MODIFIED+" desc");if(mCursor!=null){while (mCursor.moveToNext()) {// 获取图⽚的路径String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));int size = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE))/1024;String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));//⽤于展⽰相册初始化界⾯mediaBeen.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));// 获取该图⽚的⽗路径名String dirPath = new File(path).getParentFile().getAbsolutePath();//存储对应关系if (allPhotosTemp.containsKey(dirPath)) {List<MediaBean> data = allPhotosTemp.get(dirPath);data.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));continue;} else {List<MediaBean> data = new ArrayList<>();data.add(new MediaBean(MediaBean.Type.Image,path,size,displayName));allPhotosTemp.put(dirPath,data);}}mCursor.close();}//更新界⾯runOnUiThread(new Runnable() {@Overridepublic void run() {//...}});}}).start();}有四点需要注意:1. MediaBean是⽂件实体类,代码就不贴了2. 照⽚集合不是放在List<MediaBean>这样存储的,⽽是HashMap<String,List<MediaBean>>,这样把图⽚已⽂件夹(也就是⽗⽬录)分类,更节省内存,其次⽀持相册展⽰不同⽂件夹的照⽚3. 貌似没办法获取当前设备的拍照默认路径,有的设备是/DCIM,有的是/100andro还有/camera,那相册就默认展⽰最近所有照⽚吧。
android使用mediarecorder采集实时视频通过rmtp传输到流媒体服务器需要实现的功能:实时采集视频和声音,使用rtmp协议传输到流媒体服务器,流媒体使用的是开源的red5.基本实现思路:1、使用mediarecorder录像,路径设置为localsocket,这样可以在socket中获取实时视频流。
视频格式设置的是H264,硬件是定制的,可以支持H264的硬编码,所以获取到的视频流应该是编码后的流。
2、在网上找到了一个juv-rtmp-client的包,可以将编码后的流通过rtmp协议发布到流媒体。
3、在浏览器使用播放器播放实时视频流。
我找了一个开源的播放器,可以支持rtmp方式播放。
现在遇到的问题:关于实时视频,我还找到了一个IVideoChat的示例项目,但是他是使用的Camera的预览回调获取的实时视频,关键代码如下:public void onPreviewFrame(byte[] arg0, Camera arg1){// TODO Auto-generated method stubif (!active)return;if (!init){blockWidth = 32;blockHeight = 32;timeBetweenFrames = 100; // 1000 / frameRateframeCounter = 0;previous = null;init = true;}final long ctime = System.currentTimeMillis();byte[] current = RemoteUtil.decodeYUV420SP2RGB(arg0, wi dth, height);try{final byte[] packet = RemoteUtil.encode(current, previous,blockWidth, blockHeight, width, height);fireOnVideoData(new MediaDataByteArray(timeBetweenFra mes,new ByteArray(packet)));previous = current;if (++frameCounter % 10 == 0)previous = null;} catch (Exception e){e.printStackTrace();}final int spent = (int) (System.currentTimeMillis() - ctime);try{Log.i(TAG, "锟竭程等达拷" + Math.max(0, timeBetweenFrame s - spent)+ " s");Thread.sleep(Math.max(0, timeBetweenFrames - spent));} catch (InterruptedException e){// TODO Auto-generated catch blocke.printStackTrace();}}其中,fireOnVideoData这个方法,就是将编码后的视频通过rtmp发布到流媒体了。
Android视频播放数据读取的流程/s/blog_645b74b90101aezn.html这里分析Android4.0.1本地视频数据读取的流程,其他过程类似当播放条件准备妥当之后,就要循环进行读取视频的原始数据放到MediaBuffer,将MediaBuffer中的数据输送到解码器中解码,解码后的数据放到MediaBuffer中,在将这MediaBuffer中的数据进行render显示。
本文主要侧重读取原始数据的流程,主要是代码跟踪,不夹杂个人分析,有些mpeg4的算法不懂。
1:onVideoEvent中开始读取数据,具体代码如下:void AwesomePlayer::onVideoEvent() {if (!mVideoBuffer) {MediaSource::ReadOptions options;if (mSeeking != NO_SEEK) {LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);options.setSeekTo(mSeekTimeUs,mSeeking == SEEK_VIDEO_ONLY? MediaSource::ReadOptions::SEEK_NEXT_SYNC:MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);}for (;;) {status_t err = mVideoSource->read(&mVideoBuffer, &options); }}}蓝色为核心代码,如果是正常读取,options为null,否则这个结构体中包含了seek到的时间和seek的模式,用于具体从文件中哪里开始读取,传入的mVideoBuffer引用用来装解码后的数据2:蓝色部分调用的是OMXCodec::read函数,这个函数中核心的代码如下:status_t OMXCodec::read(MediaBuffer **buffer, const ReadOptions *options) {status_t err = OK;*buffer = NULL;bool seeking = false;int64_t seekTimeUs;ReadOptions::SeekMode seekMode;if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {seeking = true;}if (seeking) {CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);CHECK(seekTimeUs >= 0);mSeekTimeUs = seekTimeUs;mSeekMode = seekMode;}drainInputBuffers();size_t index = *mFilledBuffers.begin(); // A list of indices into mPortStatus[kPortIndexOutput] filled with data.mFilledBuffers.erase(mFilledBuffers.begin());BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);info->mStatus = OWNED_BY_CLIENT;info->mMediaBuffer->add_ref();*buffer = info->mMediaBuffer;return OK;}两点:a,drainInputBuffers开始了数据的读取;b,mFilledBuffers从这个队列中读取已经解码后的数据放入到传入的MediaBuffer中,mFilledBuffers队列中的MediaBuffer就是drainInputBuffers中写进去的3:跟到drainInputBuffer中看看bool OMXCodec::drainInputBuffer(BufferInfo *info) {CODEC_LOGV("calling emptyBuffer with codec specific data");status_t err = mOMX->emptyBuffer(mNode, info->mBuffer, 0, size,OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,0);CHECK_EQ(err, (status_t)OK);info->mStatus = OWNED_BY_COMPONENT;status_t err;bool signalEOS = false;int64_t timestampUs = 0;size_t offset = 0;int32_t n = 0;for (;;) {MediaBuffer *srcBuffer;if (mSeekTimeUs >= 0) {MediaSource::ReadOptions options;options.setSeekTo(mSeekTimeUs, mSeekMode);mSeekTimeUs = -1;mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;err = mSource->read(&srcBuffer, &options);if (err == OK) {int64_t targetTimeUs;if (srcBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs)&& targetTimeUs >= 0) {CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs); mTargetTimeUs = targetTimeUs;} else {mTargetTimeUs = -1;}}}}CODEC_LOGV("Calling emptyBuffer on buffer %p (length %d), ""timestamp %lld us (%.2f secs)",info->mBuffer, offset,timestampUs, timestampUs / 1E6);err = mOMX->emptyBuffer(mNode, info->mBuffer, 0, offset,flags, timestampUs);info->mStatus = OWNED_BY_COMPONENT;return true;}两点:a,调用err = mSource->read(&srcBuffer, &options);从原始文件中读取原始数据,b,往srcBuffer中读取数据前后,都调用omx转移已经读取到该info中的数据,目的是解码,解码后的数据就房子了mFilledBuffers这个队列中;4:针对mpeg4类型的视频,上面的read函数调用的是MPEG4Source的read函数,核心代码如下:status_t MPEG4Source::read(MediaBuffer **out, const ReadOptions *options) {*out = NULL;int64_t seekTimeUs;ReadOptions::SeekMode mode;if (options && options->getSeekTo(&seekTimeUs, &mode)) {if分支是用于有seek的流程1:首先找到seektime附近的sampleIndex;2:然后找到sampleIndex附近的关键帧的syncSampleIndex;3:然后syncSampleIndex找到具体的sampleTime,sampleTime就是目前需要播放到的位置;4:mSampleTable->getMetaDataForSample调用这个函数找到sampleTime时间的offset和size;5:有了offset和size之后剩下就是调用mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);读取数据放到buffer中去了;uint32_t findFlags = 0;switch (mode) {case ReadOptions::SEEK_PREVIOUS_SYNC:findFlags = SampleTable::kFlagBefore;break;case ReadOptions::SEEK_NEXT_SYNC:findFlags = SampleTable::kFlagAfter;break;case ReadOptions::SEEK_CLOSEST_SYNC:case ReadOptions::SEEK_CLOSEST:findFlags = SampleTable::kFlagClosest;break;default:CHECK(!"Should not be here.");break;}uint32_t sampleIndex;status_t err = mSampleTable->findSampleAtTime(seekTimeUs * mTimescale / 1000000,&sampleIndex, findFlags);uint32_t syncSampleIndex;if (err == OK) {err = mSampleTable->findSyncSampleNear(sampleIndex, &syncSampleIndex, findFlags);}uint32_t sampleTime;if (err == OK) {err = mSampleTable->getMetaDataForSample(sampleIndex, NULL, NULL, &sampleTime);}if (mode == ReadOptions::SEEK_CLOSEST) {targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale; }mCurrentSampleIndex = syncSampleIndex;}off64_t offset;size_t size;uint32_t cts;bool isSyncSample;bool newBuffer = false;if (mBuffer == NULL) {newBuffer = true;status_t err =mSampleTable->getMetaDataForSample(mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);if (err != OK) {return err;}err = mGroup->acquire_buffer(&mBuffer);if (err != OK) {CHECK(mBuffer == NULL);return err;}}if (!mIsAVC || mWantsNALFragments) {if (newBuffer) {ssize_t num_bytes_read =mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);CHECK(mBuffer != NULL);mBuffer->set_range(0, size);mBuffer->meta_data()->clear();mBuffer->meta_data()->setInt64(kKeyTime, ((int64_t)cts * 1000000) / mTimescale);if (isSyncSample) {mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);}++mCurrentSampleIndex;}if (!mIsAVC) {*out = mBuffer;mBuffer = NULL;return OK;}// Each NAL unit is split up into its constituent fragments and // each one of them returned in its own buffer.CHECK(mBuffer->range_length() >= mNALLengthSize);const uint8_t *src =(const uint8_t *)mBuffer->data() + mBuffer->range_offset();size_t nal_size = parseNALSize(src);MediaBuffer *clone = mBuffer->clone();CHECK(clone != NULL);clone->set_range(mBuffer->range_offset() + mNALLengthSize, nal_size);CHECK(mBuffer != NULL);mBuffer->set_range(mBuffer->range_offset() + mNALLengthSize + nal_size, mBuffer->range_length() - mNALLengthSize - nal_size);if (mBuffer->range_length() == 0) {mBuffer->release();mBuffer = NULL;}*out = clone;return OK;}}蓝色部分为主要的流程5:后续就是开始调用SampleTable.cpp和SampleIterator.cpp这两个类的相关函数解析文件和读取数据,最主要的函数时通过sampleIndex获取offset和size信息了,代码如下:status_t SampleTable::getMetaDataForSample(uint32_t sampleIndex,off64_t *offset,size_t *size,uint32_t *compositionTime,bool *isSyncSample) {Mutex::Autolock autoLock(mLock);status_t err;if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {return err;}if (offset) {*offset = mSampleIterator->getSampleOffset();}if (size) {*size = mSampleIterator->getSampleSize();}if (compositionTime) {*compositionTime = mSampleIterator->getSampleTime();}if (isSyncSample) {*isSyncSample = false;if (mSyncSampleOffset < 0) {// Every sample is a sync sample.*isSyncSample = true;} else {size_t i = (mLastSyncSampleIndex < mNumSyncSamples)&& (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex)? mLastSyncSampleIndex : 0;while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) {++i;}if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) {*isSyncSample = true;}mLastSyncSampleIndex = i;}}return OK;}下面这个函数没有看懂,对具体的mpeg4压缩协议没有进行深入了解status_t SampleIterator::findSampleTime(uint32_t sampleIndex, uint32_t *time) {if (sampleIndex >= mTable->mNumSampleSizes) {return ERROR_OUT_OF_RANGE;}while (sampleIndex >= mTTSSampleIndex + mTTSCount) {if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {return ERROR_OUT_OF_RANGE;}mTTSSampleIndex += mTTSCount;mTTSSampleTime += mTTSCount * mTTSDuration;mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];++mTimeToSampleIndex;}*time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);*time += mTable->getCompositionTimeOffset(sampleIndex);return OK;}。