4 Star 1 Fork 7

恺人13 / AVCodecSample

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

AVCodecSample

介绍

AVCodec 部件示例 Sample,基于 API12 构建,提供视频播放和录制的功能。

  • 视频播放的主要流程是将视频文件通过解封装->解码->送显至屏幕。
  • 视频录制的主要流程是相机采集->编码->封装成mp4文件。

播放支持的原子能力规格

媒体格式 封装格式 码流格式
视频 mp4 视频码流:H.264/H.265
视频 mkv 视频码流:H.264/H.265
视频 mpeg-ts 视频码流:H.264

录制支持的原子能力规格

封装格式 视频编解码类型
mp4 H.264/H.265
m4a AVC(H.264)

效果预览

播放 特效 录制 录制
播放.jepg 特效.jpeg 录制.jpeg 录制.jpeg

使用说明

  1. 弹出是否允许“videoCodecSample”访问图片与视频?点击“允许”

  2. 弹出是否允许“videoCodecSample”访问文件?点击“允许”

  3. 弹出是否允许“videoCodecSample”使用相机?点击“允许”

播放

  1. 点击下方“开始录制”,录制一个视频文件或推送视频文件至storage/media/100/local/files/Docs下

  2. 点击播放按钮,选择文件

录制

  1. 点击“录制”

  2. 点击“开始录制”

  3. 点击“停止录制”

目录

仓目录结构如下:

video-codec-sample/entry/src/main/          
├── cpp                                # Native层
│   ├── capbilities                    # 能力接口和实现
│   │   ├── include                    # 能力接口
│   │   ├── demuxer.cpp                # 解封装实现
│   │   ├── muxer.cpp                  # 封装实现
│   │   ├── video_decoder.cpp          # 解码实现
│   │   └── video_encoder.cpp          # 编码实现
│   ├── common                         # 公共模块
│   │   ├── dfx                        # 日志
│   │   ├── sample_callback.cpp        # 编解码回调实现   
│   │   ├── sample_callback.h          # 编解码回调定义
│   │   └── sample_info.h              # 功能实现公共类  
│   ├── render                         # 送显模块接口和实现
│   │   ├── include                    # 送显模块接口
│   │   ├── egl_core.cpp               # 送显参数设置
│   │   ├── plugin_manager.cpp         # 送显模块管理实现
│   │   └── plugin_render.cpp          # 送显逻辑实现
│   ├── sample                         # Native层
│   │   ├── player                     # Native层播放接口和实现
│   │   │   ├── Player.cpp             # Native层播放功能调用逻辑的实现
│   │   │   ├── Player.h               # Native层播放功能调用逻辑的接口
│   │   │   ├── PlayerNative.cpp       # Native层 播放的入口
│   │   │   └── PlayerNative.h         # 
│   │   └── recorder                   # Native层录制接口和实现
│   │       ├── Recorder.cpp           # Native层录制功能调用逻辑的实现
│   │       ├── Recorder.h             # Native层录制功能调用逻辑的接口
│   │       ├── RecorderNative.cpp     # Native层 录制的入口
│   │       └── RecorderNative.h       # 
│   ├── types                          # Native层暴露上来的接口
│   │   ├── libplayer                  # 播放模块暴露给UI层的接口
│   │   └── librecorder                # 录制模块暴露给UI层的接口
│   └── CMakeLists.txt                 # 编译入口       
├── ets                                # UI层
│   ├── common                         # 公共模块
│   │   └──utils                       # 共用的工具类
│   │       ├── DateTimeUtils.ets      # 获取当前时间
│   │       ├── Logger.ts              # 日志工具
│   │       └── SaveAsset.ets          # 选取文件保持位置
│   ├── entryability                   # 应用的入口
│   │   └── EntryAbility.ts            # 申请权限弹窗实现
│   ├── pages                          # EntryAbility 包含的页面
│   │   └── Index.ets                  # 首页/播放页面
│   └── sample                         # sample
│       └── recorder                   # 录制
│           └── Recorder.ets           # 录制页面
├── resources                          # 用于存放应用所用到的资源文件
│   ├── base                           # 该目录下的资源文件会被赋予唯一的ID
│   │   ├── element                    # 用于存放字体和颜色 
│   │   ├── media                      # 用于存放图片
│   │   └── profile                    # 应用入口首页
│   ├── en_US                          # 设备语言是美式英文时,优先匹配此目录下资源
│   └── zh_CN                          # 设备语言是简体中文时,优先匹配此目录下资源
└── module.json5                       # 模块配置信息

具体实现

播放

UI层
  1. 在UI层Index.ets页面,用户点击播放按钮后,触发点击事件,调起selectFile()函数,该函数会调起文件管理的选择文件模块,拿到用户选取文件的路径。
  2. 用户选择文件成功后,调起play()函数,该函数会根据上一步获取到的路径,打开一个文件,并获取到该文件的大小,改变按钮状态为不可用,之后调起js层暴露给应用层的playNative()接口
  3. 根据playNative字段,调起PlayerNative::Play函数,此处会注册播放结束的回调。
  4. 播放结束时,Callback()中napi_call_function()接口调起,通知应用层,恢复按钮状态为可用。
JS层
  1. Init()中调用PluginManager()中的Export()方法,注册OnSurfaceCreatedCB()回调,当屏幕上出现新的Xcomponent时,将其转换并赋给单例类PluginManager中的pluginWindow_;
Native层

参考开发者文档,以下是补充:

  1. 解码器config阶段,OH_VideoDecoder_SetSurface接口的入参OHNativeWindow*,即为PluginManager中的pluginWindow_。
  2. 解码器config阶段,SetCallback接口,输入输出回调需将回调上来的帧buffer和index存入一个用户自定义容器中,方便后续操作。
  3. Start时,起两个专门用于输入和输出的线程。
  4. 具体实现原理:
    • 解码器Start后,解码器每拿到一帧,OnNeedInputBuffer就会被调起一次,avcodec框架会给用户一个OH_AVBuffer。
    • 在输入回调中,用户需手动把帧bufer、index存入输入队列中,并同时输入线程解锁。
    • 在输入线程中,把上一步的帧信息储存为bufferInfo后,pop出队。
    • 在输入线程中,使用上一步的bufferInfo,调用ReadSample接口解封装帧数据。
    • 在输入线程中,使用解封装后的bufferInfo,调用解码的PushInputData接口,此时这片buffer用完,返回框架,实现buffer轮转。
    • PushInputData后,这一帧开始解码,每解码完成一帧,输出回调会被调起一次,用户需手动把帧buffer、index存入输出队列中,并。
    • 在输出线程中,把上一步的帧信息储存为bufferInfo后,pop出队。
    • 在输出线程中,调用FreeOutputData接口后,就会送显并释放buffer。释放的buffer会返回框架,实现buffer轮转。

录制

UI层
  1. 在UI层Index.ets页面,用户点击“开始录制”后,会调起文件管理,用户选择一个输出地址。录制结束后,文件会存放于此。
  2. 选择好文件后,会用刚刚打开的fd,和用户预设的录制参数,掉起JS层的initNative,待初始化结束后,调用OH_NativeWindow_GetSurfaceId接口,得到NativeWindow的surfaceId,并把surfaceId回调回UI层。
  3. UI层拿到编码器给的surfaceId后,调起页面路由,携带该surfaceId,跳转到Recorder.ets
  4. 录制页面构建时,Xcomponent构建时,会调起.onLoad()方法,此方法,首先会拿到Xcomponent的surfaceId,然后调起createDualChannelPreview(),此函数会建立一个相机生产,Xcomponent和编码器的surface消费的生产消费模型。
Native层

参考开发者文档,以下是补充:

  1. 用户点击开始录制后,编码器启动,开始对UI层相机预览流进行编码。
  2. 编码器每编码成功一帧,输出回调OnNewOutputBuffer()就会掉起一次,此时用户会拿到avcodec框架给出的OH_AVBuffer。
  3. 在输出回调中,用户需手动把帧buffer、index存入输出队列中,并通知输出线程解锁。
  4. 在输出线程中,把上一步的帧信息储存为bufferInfo后,pop出队。
  5. 在输出线程中,使用上一步的bufferInfo,调用封装接口WriteSample后,这一帧被封装入MP4中了
  6. 最后调用FreeOutputBuffer接口后,这一帧buffer释放回avcodec框架,实现buffer轮转

相关权限

ohos.permission.READ_MEDIA

ohos.permission.WRITE_MEDIA

ohos.permission.MEDIA_LOCATION

ohos.permission.CAMERA

依赖

不涉及。

约束与限制

1.本示例仅支持标准系统上运行;

2.本示例仅支持 API12 及以上版本SDK,SDK版本号(API Version 12 Release),镜像版本号(5.0 Release);

3.本示例需要使用DevEco Studio 5.0 才可编译运行。

相关仓

空文件

简介

AVCodec简单demo 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/kairen-13/AVCodecSample.git
git@gitee.com:kairen-13/AVCodecSample.git
kairen-13
AVCodecSample
AVCodecSample
master

搜索帮助