• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 音视频解封装
2
3开发者可以调用本模块的Native API接口,完成音视频解封装,即从比特流数据中取出音频、视频等媒体帧数据。
4
5当前支持的数据输入类型有:远程连接(http协议)和文件描述符(fd)。
6
7支持的解封装格式如下:
8
9| 媒体格式  | 封装格式                      | 码流格式                      |
10| -------- | :----------------------------| :----------------------------|
11| 音视频     | mp4                        |视频码流:AVC(H.264),音频码流:AAC、MPEG(MP3)|
12| 音视频     | mkv                        |视频码流:AVC(H.264),音频码流:AAC、MPEG(MP3)、OPUS|
13| 音视频     | mpeg-ts                    |视频码流:AVC(H.264),音频码流:AAC、MPEG(MP3)|
14| 音频       | m4a                        |音频码流:AAC|
15| 音频       | aac                        |音频码流:AAC|
16| 音频       | mp3                        |音频码流:MPEG(MP3)|
17| 音频       | ogg                        |音频码流:OGG|
18| 音频       | flac                        |音频码流:FLAC|
19| 音频       | wav                        |音频码流:PCM|
20| 音频       | amr                        |音频码流:AMR(amrnb/amrwb)|
21
22**适用场景**:
23
24- 播放
25
26  播放媒体文件时,需要先对音视频流进行解封装,然后使用解封装获取的帧数据进行解码和播放。
27
28- 音视频编辑
29
30  编辑媒体文件时,需要先对音视频流进行解封装,获取到指定帧进行编辑。
31
32- 媒体文件格式转换(转封装)
33
34  媒体文件格式转换时,需要先对音视频流进行解封装,然后按需将音视频流封装至新的格式文件内。
35
36## 开发指导
37
38详细的API说明参考[AVDemuxer](../../reference/apis-avcodec-kit/_a_v_demuxer.md)和[AVSource](../../reference/apis-avcodec-kit/_a_v_source.md)
39
40> **说明**
41>
42> - 调用解封装能力解析网络播放路径,需要[声明权限](../../security/AccessToken/declare-permissions.md):ohos.permission.INTERNET
43> - 调用解封装能力解析本地文件,需要[向用户申请授权](../../security/AccessToken/request-user-authorization.md):ohos.permission.READ_MEDIA
44> - 如果使用ResourceManager.getRawFd打开HAP资源文件描述符,使用方法请参考[ResourceManager API参考](../../reference/apis-localization-kit/js-apis-resource-manager.md#getrawfd9)
45
46### 在 CMake 脚本中链接动态库
47
48``` cmake
49target_link_libraries(sample PUBLIC libnative_media_avdemuxer.so)
50target_link_libraries(sample PUBLIC libnative_media_avsource.so)
51target_link_libraries(sample PUBLIC libnative_media_core.so)
52```
53
54### 开发步骤
55
561. 添加头文件。
57
58   ```c++
59   #include <multimedia/player_framework/native_avdemuxer.h>
60   #include <multimedia/player_framework/native_avsource.h>
61   #include <multimedia/player_framework/native_avcodec_base.h>
62   #include <multimedia/player_framework/native_avformat.h>
63   #include <multimedia/player_framework/native_avbuffer.h>
64   ```
65
662. 创建解封装器实例对象。
67
68   ```c++
69   // 创建文件操作符 fd,打开时对文件句柄必须有读权限(filePath 为待解封装文件路径,需预置文件,保证路径指向的文件存在)
70   std::string filePath = "test.mp4";
71   int fd = open(filePath.c_str(), O_RDONLY);
72   struct stat fileStatus {};
73   size_t fileSize = 0;
74   if (stat(filePath.c_str(), &fileStatus) == 0) {
75      fileSize = static_cast<size_t>(fileStatus.st_size);
76   } else {
77      printf("get stat failed");
78      return;
79   }
80   // 为 fd 资源文件创建 source 资源对象, 传入 offset 不为文件起始位置 或 size 不为文件大小时,可能会因不能获取完整数据导致 source 创建失败、或后续解封装失败等问题
81   OH_AVSource *source = OH_AVSource_CreateWithFD(fd, 0, fileSize);
82   if (source == nullptr) {
83      printf("create source failed");
84      return;
85   }
86   // 为 uri 资源文件创建 source 资源对象(可选)
87   // OH_AVSource *source = OH_AVSource_CreateWithURI(uri);
88   ```
89
90   ```c++
91   // 为资源对象创建对应的解封装器
92   OH_AVDemuxer *demuxer = OH_AVDemuxer_CreateWithSource(source);
93   if (demuxer == nullptr) {
94      printf("create demuxer failed");
95      return;
96   }
97   ```
983. 注册[DRM信息监听函数](../../reference/apis-drm-kit/_drm.md#drm_mediakeysysteminfocallback)(可选,若非DRM码流或已获得[DRM信息](../../reference/apis-drm-kit/_drm.md#drm_mediakeysysteminfo),可跳过此步)。
99
100   加入头文件
101   ```c++
102   #include <multimedia/drm_framework/native_drm_common.h>
103   ```
104   在 CMake 脚本中链接动态库
105
106   ``` cmake
107   target_link_libraries(sample PUBLIC libnative_drm.so)
108   ```
109
110   使用示例
111   ```c++
112   // DRM信息监听回调OnDrmInfoChanged实现
113   static void OnDrmInfoChanged(DRM_MediaKeySystemInfo *drmInfo)
114   {
115      // 解析DRM信息,包括数量、DRM类型及对应pssh
116   }
117
118   // 设置异步回调
119   DRM_MediaKeySystemInfoCallback callback = &OnDrmInfoChanged;
120   int32_t ret = OH_AVDemuxer_SetMediaKeySystemInfoCallback(demuxer, callback);
121
122   // 在监听到DRM信息后,也可主动调用获取DRM信息接口
123   DRM_MediaKeySystemInfo mediaKeySystemInfo;
124   OH_AVDemuxer_GetMediaKeySystemInfo(demuxer, &mediaKeySystemInfo);
125   ```
126
1274. 获取文件轨道数(可选,若用户已知轨道信息,可跳过此步)。
128
129   ```c++
130   // 从文件 source 信息获取文件轨道数
131   OH_AVFormat *sourceFormat = OH_AVSource_GetSourceFormat(source);
132   if (sourceFormat == nullptr) {
133      printf("get source format failed");
134      return;
135   }
136   int32_t trackCount = 0;
137   OH_AVFormat_GetIntValue(sourceFormat, OH_MD_KEY_TRACK_COUNT, &trackCount);
138   OH_AVFormat_Destroy(sourceFormat);
139   ```
140
1415. 获取轨道index及信息(可选,若用户已知轨道信息,可跳过此步)。
142
143   ```c++
144   uint32_t audioTrackIndex = 0;
145   uint32_t videoTrackIndex = 0;
146   int32_t w = 0;
147   int32_t h = 0;
148   int32_t trackType;
149   for (uint32_t index = 0; index < (static_cast<uint32_t>(trackCount)); index++) {
150      // 获取轨道信息
151      OH_AVFormat *trackFormat = OH_AVSource_GetTrackFormat(source, index);
152      if (trackFormat == nullptr) {
153         printf("get track format failed");
154         return;
155      }
156      OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRACK_TYPE, &trackType);
157      static_cast<OH_MediaType>(trackType) == OH_MediaType::MEDIA_TYPE_AUD ? audioTrackIndex = index : videoTrackIndex = index;
158      // 获取视频轨宽高
159      if (trackType == OH_MediaType::MEDIA_TYPE_VID) {
160         OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_WIDTH, &w);
161         OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_HEIGHT, &h);
162      }
163      OH_AVFormat_Destroy(trackFormat);
164   }
165   ```
166
1676. 添加解封装轨道。
168
169   ```c++
170   if(OH_AVDemuxer_SelectTrackByID(demuxer, audioTrackIndex) != AV_ERR_OK){
171      printf("select audio track failed: %d", audioTrackIndex);
172      return;
173   }
174   if(OH_AVDemuxer_SelectTrackByID(demuxer, videoTrackIndex) != AV_ERR_OK){
175      printf("select video track failed: %d", videoTrackIndex);
176      return;
177   }
178   // 取消选择轨道(可选)
179   // OH_AVDemuxer_UnselectTrackByID(demuxer, audioTrackIndex);
180   ```
181
1827. 调整轨道到指定时间点(可选)。
183
184   ```c++
185   // 调整轨道到指定时间点,后续从该时间点进行解封装
186   // 注意:
187   // 1. mpegts格式文件使用OH_AVDemuxer_SeekToTime功能时,跳转到的位置可能为非关键帧。可在跳转后调用OH_AVDemuxer_ReadSampleBuffer,通过获取到的OH_AVCodecBufferAttr判断当前帧是否为关键帧。若非关键帧影响应用侧显示等功能,可在跳转后循环读取,获取到后续第一帧关键帧后,再进行解码等处理。
188   // 2. ogg格式文件使用OH_AVDemuxer_SeekToTime功能时,会跳转到传入时间millisecond所在时间间隔(秒)的起始处,可能会导致一定数量的帧误差。
189   OH_AVDemuxer_SeekToTime(demuxer, 0, OH_AVSeekMode::SEEK_MODE_CLOSEST_SYNC);
190   ```
191
1928. 开始解封装,循环获取帧数据(以含音频、视频两轨的文件为例)。
193
194   ```c++
195   // 创建 buffer,用与保存用户解封装得到的数据
196   OH_AVBuffer *buffer = OH_AVBuffer_Create(w * h * 3 >> 1);
197   if (buffer == nullptr) {
198      printf("build buffer failed");
199      return;
200   }
201   OH_AVCodecBufferAttr info;
202   bool videoIsEnd = false;
203   bool audioIsEnd = false;
204   int32_t ret;
205   while (!audioIsEnd || !videoIsEnd) {
206      // 在调用 OH_AVDemuxer_ReadSampleBuffer 接口获取数据前,需要先调用 OH_AVDemuxer_SelectTrackByID 选中需要获取数据的轨道
207      // 获取音频帧数据
208      if(!audioIsEnd) {
209         ret = OH_AVDemuxer_ReadSampleBuffer(demuxer, audioTrackIndex, buffer);
210         if (ret == AV_ERR_OK) {
211            // 可通过 buffer 获取并处理音频帧数据
212            OH_AVBuffer_GetBufferAttr(buffer, &info);
213            printf("audio info.size: %d\n", info.size);
214            if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
215               audioIsEnd = true;
216            }
217         }
218      }
219      if(!videoIsEnd) {
220         ret = OH_AVDemuxer_ReadSampleBuffer(demuxer, videoTrackIndex, buffer);
221         if (ret == AV_ERR_OK) {
222            // 可通过 buffer 获取并处理视频帧数据
223            OH_AVBuffer_GetBufferAttr(buffer, &info);
224            printf("video info.size: %d\n", info.size);
225            if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
226               videoIsEnd = true;
227            }
228         }
229      }
230   }
231   OH_AVBuffer_Destroy(buffer);
232   ```
233
2349. 销毁解封装实例。
235
236   ```c++
237   // 需要用户调用 OH_AVSource_Destroy 接口成功后,手动将对象置为 NULL,对同一对象重复调用 OH_AVSource_Destroy 会导致程序错误
238   if (OH_AVSource_Destroy(source) != AV_ERR_OK) {
239      printf("destroy source pointer error");
240   }
241   source = NULL;
242   // 需要用户调用 OH_AVDemuxer_Destroy 接口成功后,手动将对象置为 NULL,对同一对象重复调用 OH_AVDemuxer_Destroy 会导致程序错误
243   if (OH_AVDemuxer_Destroy(demuxer) != AV_ERR_OK) {
244      printf("destroy demuxer pointer error");
245   }
246   demuxer = NULL;
247   close(fd);
248   ```
249