android下视频文件从解码到播放需要哪几步,请简述

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android下视频文件从解码到播放需要哪几步,请简述相关的知识,希望对你有一定的参考价值。

android通过软解码播放视频
1, 一般情况下Android的平台都是硬解码视频的,尤其是在Arm平台这种成熟的硬件平台上面(硬解码代码由芯片厂商提供)。但是Android移植到
2, MIPS平台时间还不长,还不成熟,还需要自己实现硬件解码的工作。为了早日让Android在MIPS平台运行起来,我选择了先用软解码播放视频。
3,Android代码是从Android on MIPS社区获得的代码。发现软解码视频播放过程中会发生崩溃。经过分析好像是内存分配的问题。

4, 经过研究OpenCore库(Android框架是通过OpenCore来播放视频的,网上有很多关于OpenCore的介绍,这里就不多说了),并参考Android平台——Surfaceflinger机制。发现问题出在源文件:
frameworks/base/libs/surfaceflinger/LayerBuffer.cpp的LayerBuffer::BufferSource::postBuffer方法中:
............
buffer = new LayerBuffer::Buffer(buffers, offset);
............类LayerBuffer::Buffer的构造函数代码如下:
LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset)
: mBufferHeap(buffers)

NativeBuffer& src(mNativeBuffer);
g.handle = 0;
gralloc_module_t const * module = LayerBuffer::getGrallocModule();
if (module && module->perform)
int err = module->perform(module,
GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,
buffers.heap->heapID(), buffers.heap->getSize(),
offset, buffers.heap->base(),
& g.handle);
if (err == NO_ERROR)
op.l = 0;
op.t = 0;
op.r = buffers.w;
op.b = buffers.h;
g.w = buffers.hor_stride ?: buffers.w;
g.h = r_stride ?: buffers.h;
rmat = rmat;
se = (void*)(intptr_t(buffers.heap->base()) + offset);


LayerBuffer::getGrallocModule方法的调用到的Gralloc为:
hardware/libhardware/modules/gralloc/gralloc.cpp因为的没有实现在自己的硬件只能用通用的Gralloc,经过分析发现通用的Gralloc没有实现
5, module->perform函数指针,module->perform为NULL,所以不会对Buffer进行必要的初始化(我觉得应该是一个疏忽,只是不知道是谷歌的疏忽,还是MIPS移植人员的疏忽,最起码应该能够让通用硬件能跑起来)。参考其他的硬件实现一个perform函数指针到通用Gralloc中。
在源文件:
hardware/libhardware/modules/gralloc/mapper.cpp增加如下的函数定义:
int gralloc_perform(struct gralloc_module_t const* module,
int operation, ... )

int res = -EINVAL;
va_list args;
va_start(args, operation);
switch (operation)
case GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER:
int fd = va_arg(args, int);
size_t size = va_arg(args, size_t);
size_t offset = va_arg(args, size_t);
void* base = va_arg(args, void*);
native_handle_t** handle = va_arg(args, native_handle_t**);
private_handle_t* hnd = (private_handle_t*)native_handle_create(
private_handle_t::sNumFds, private_handle_t::sNumInts);
hnd->magic = private_handle_t::sMagic;
hnd->fd = fd;
hnd->flags = private_handle_t::PRIV_FLAGS_USES_PMEM;
hnd->size = size;
hnd->offset = offset;
hnd->base = intptr_t(base) + offset;
hnd->lockState = private_handle_t::LOCK_STATE_MAPPED;
*handle = (native_handle_t *)hnd;
res = 0;
break;


va_end(args);
return res;
然后在gralloc.cpp中增加,gralloc_perform的声明:
extern int gralloc_perform(struct gralloc_module_t const* module,
int operation, ... );并修改HAL_MODULE_INFO_SYM的定义,增加perform字段的定义:
struct private_module_t HAL_MODULE_INFO_SYM =
base:
.......
perform: gralloc_perform,
,
......
; 重新编译gralloc模块,再次用Gallary应用程序通过软解码播放视频,就可以流畅的播放了,软解码的效率挺高的,没有卡的感觉!
参考技术A 但是Android移植到MIPS平台时间还不长,还不成熟,还需要自己实现硬件解码的工作。为了早日让Android在MIPS平台运行起来,我选择了先用软解码播放视频。我的Android代码是从AndroidonMIPS社区获得的代码。发现软解码视频播放过程中会发生崩溃。经过分析好像是内存分配的问题。经过研究OpenCore库(Android框架是通过OpenCore来播放视频的,网上有很多关于OpenCore的介绍,这里就不多说了),并参考Android平台Surfaceflinger机制。发现问题出在源文件:frameworks/base/libs/surfaceflinger/LayerBuffer.cpp的LayerBuffer::BufferSource::postBuffer方法中:............buffer=newLayerBuffer::Buffer(buffers,offset);............类LayerBuffer::Buffer的构造函数代码如下:LayerBuffer::Buffer::Buffer(constISurface::BufferHeap&buffers,ssize_toffset):mBufferHeap(buffers)NativeBuffer&src(mNativeBuffer);src.img.handle=0;gralloc_module_tconst*module=LayerBuffer::getGrallocModule();if(module&&module-perform)interr=module-perform(module,GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,buffers.heap-heapID(),buffers.heap-getSize(),offset,buffers.heap-base(),&src.img.handle);if(err==NO_ERROR)src.crop.l=0;src.crop.t=0;src.crop.r=buffers.w;src.crop.b=buffers.h;src.img.w=buffers.hor_stride?:buffers.w;src.img.h=buffers.ver_stride?:buffers.h;src.img.format=buffers.format;src.img.base=(void*)(intptr_t(buffers.heap-base())+offset);LayerBuffer::getGrallocModule方法的调用到的Gralloc为:hardware/libhardware/modules/gralloc/gralloc.cpp因为的没有实现在自己的硬件只能用通用的Gralloc,经过分析发现通用的Gralloc没有实现module-perform函数指针,module-perform为NULL,所以不会对Buffer进行必要的初始化(我觉得应该是一个疏忽,只是不知道是谷歌的疏忽,还是MIPS移植人员的疏忽,最起码应该能够让通用硬件能跑起来)。参考其他的硬件实现一个perform函数指针到通用Gralloc中。在源文件:hardware/libhardware/modules/gralloc/mapper.cpp增加如下的函数定义:intgralloc_perform(structgralloc_module_tconst*module,intoperation,...)intres=-EINVAL;va_listargs;va_start(args,operation);switch(operation)caseGRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER:intfd=va_arg(args,int);size_tsize=va_arg(args,size_t);size_toffset=va_arg(args,size_t);void*base=va_arg(args,void*);native_handle_t**handle=va_arg(args,native_handle_t**);private_handle_t*hnd=(private_handle_t*)native_handle_create(private_handle_t::sNumFds,private_handle_t::sNumInts);hnd-magic=private_handle_t::sMagic;hnd-fd=fd;hnd-flags=private_handle_t::PRIV_FLAGS_USES_PMEM;hnd-size=size;hnd-offset=offset;hnd-base=intptr_t(base)+offset;hnd-lockState=private_handle_t::LOCK_STATE_MAPPED;*handle=(native_handle_t*)hnd;res=0;break;va_end(args);returnres;然后在gralloc.cpp中增加,gralloc_perform的声明:externintgralloc_perform(structgralloc_module_tconst*module,intoperation,...);并修改HAL_MODULE_INFO_SYM的定义,增加perform字段的定义:structprivate_module_tHAL_MODULE_INFO_SYM=base:.......perform:gralloc_perform,,......;重新编译gralloc模块,再次用Gallary应用程序通过软解码播放视频,就可以流畅的播放了,软解码的效率挺高的,没有卡的感觉!本回答被提问者采纳 参考技术B   建议安装优秀的播放器来解决,解码能力强,而且操作简单、方便。比如QQ音影、百度视频等等。
  1、手机安装最新版本的QQ音影。
  2、打开软件后,自动提示搜索本机的视频,搜索到打开就OK。
  3、还可以看软件推荐的视频,基本上流行的电视如直播、点播等等。

ffmpeg播放时刻与视频文件时间戳对齐(同步)

问题描述

当解码较快的时候,并不是解码后立马显示视频帧,这样看着是倍速的效果。如何还原实际的播放速率?

解决方案

为了解决在解码后视频播放还原原来每帧播放的时刻点。我们需要在解码较快的情况下对帧显示加一定的时间延时,这个延时策略就是计算出

延时调整时间 =(当前帧时间戳  -  上一帧时间戳)- (当前机器准显示时间  -  上一帧显示机器时间)

延时调整时间 有可能为负值则丢弃。如果为正值,可根据时长做一定调整,毕竟送帧显示也是耗时操作。

demo示例:

void dispThread(void *arg)

    Input *input = (Input *)arg;
    static const double interval = 1000000.0 / input->fps;
    double q2d = av_q2d(input->tb) * 1000000.0;
    int64_t pre_pts = 0;
    int64_t frame_pre_pts = AV_NOPTS_VALUE;
    AVFrame *pending_frm = NULL;

    while (!input->is_req_exit()) 
        int64_t cur_pts;
        int remaining_time = 10000;
        double duration = interval;
        AVFrame *frm;

        if (pending_frm) 
            frm = pending_frm;
         else 
            frm = input->PopFrame();
            if (!frm) 
                msleep(10);
                continue;
            
            // printf("pop frame pts: %ld\\n", frm->pts);
        
        static auto delete_func = [](AVFrame * f) 
            av_frame_free(&f);
        ;

        cur_pts = av_gettime_relative();
        if (frame_pre_pts != AV_NOPTS_VALUE)
            duration = (frm->pts - frame_pre_pts) * q2d;

        int countdown = (pre_pts == 0) ? 0 : (int)(duration - (cur_pts - pre_pts));
        remaining_time = std::min<int>(remaining_time, countdown);

        // printf("countdown: %d, remaining_time: %d us\\n",
        //         countdown, remaining_time);
        if (input->realtime) 
            countdown = 0;
            remaining_time = 0;
        
        if (countdown <= 0) 
            frame_pre_pts = frm->pts;
            pre_pts = cur_pts;
            if (frm == pending_frm)
                pending_frm = NULL;
            push_frame(input->join, input->slice_idx,
                       std::shared_ptr<AVFrame>(frm, delete_func));
         else 
            pending_frm = frm;
        

        if (remaining_time > 0)
        
            printf("countdown: %d, remaining_time: %d us\\n",
                 countdown, remaining_time);
            std::this_thread::sleep_for(std::chrono::microseconds(remaining_time));
        
    
    if (pending_frm)
        av_frame_free(&pending_frm);

知识点1:av_q2d(AVRational a)函数

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

av_q2d(AVRational);该函数负责把AVRational结构转换成double,通过这个函数可以计算出某一帧在视频中的时间位置

timestamp(秒) = pts * av_q2d(st->time_base);

计算视频长度的方法:

time(秒) = st->duration * av_q2d(st->time_base);

知识点2:av_gettime_relative();

该函数是拿到当前的机器时间(系统时间)。

如果是直播的情况下我们不做延时。

大致的流程如下:

取得解码视频帧

记录当前机器时间

计算当前准显示时间与上次显示时间差值d1

计算当前帧时间戳与上次显示时间戳差值d2

计算延时时间 d2 - d1

延时时间大于0则进行sleep延时

并保存当前帧在下一次循环送显

以上步骤为解决方案。请参考。

以上是关于android下视频文件从解码到播放需要哪几步,请简述的主要内容,如果未能解决你的问题,请参考以下文章

Android音视频硬解码播放H264

Android FFmpeg音视频解码播放

Android媒体解码MediaCodec MediaExtractor学习

怎样实现自己写的Android视屏播放器播放rmvb格式的视频

ubuntu 下如何手动从网上下载依赖和播放音视频需要的解码器? (我在ubuntu下无法上网)

平台支持vp8硬件解码,播放器就可以支持吗