• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 音视频解封装
2
3开发者可以调用本模块的Native API接口,完成音视频解封装,即从比特流数据中取出音频、视频等媒体帧数据。
4
5当前支持的数据输入类型有:远程连接(http协议)和文件描述符(fd)。
6
7支持的解封装格式如下:
8
9| 媒体格式  | 封装格式                      |
10| -------- | :----------------------------|
11| 视频     | mp4、mpeg-ts                  |
12| 音频      | m4a、aac、mp3、ogg、flac、wav |
13
14
15
16**适用场景**:
17
18- 播放
19
20  播放媒体文件时,需要先对音视频流进行解封装,然后使用解封装获取的帧数据进行解码和播放。
21
22- 音视频编辑
23
24  编辑媒体文件时,需要先对音视频流进行解封装,获取到指定帧进行编辑。
25
26- 媒体文件格式转换(转封装)
27
28  媒体文件格式转换时,需要先对音视频流进行解封装,然后按需将音视频流封装至新的格式文件内。
29
30## 开发指导
31详细的API说明参考[AVDemuxer](../reference/native-apis/_a_v_demuxer.md)和[AVSource](../reference/native-apis/_a_v_source.md)
32
33> **说明**
34>
35> - 调用解封装能力解析网络播放路径,需要[申请相关权限](../security/accesstoken-guidelines.md):ohos.permission.INTERNET
36> - 调用解封装能力解析本地文件,需要[申请相关权限](../security/accesstoken-guidelines.md):ohos.permission.READ_MEDIA
37> - 如果使用ResourceManager.getRawFd打开HAP资源文件描述符,使用方法请参考[ResourceManager API参考](../reference/apis/js-apis-resource-manager.md#getrawfd9)
38
39### 在 CMake 脚本中链接动态库
40``` cmake
41target_link_libraries(sample PUBLIC libnative_media_avdemuxer.so)
42target_link_libraries(sample PUBLIC libnative_media_avsource.so)
43```
44
45### 开发步骤
46
471. 创建解封装器实例对象
48
49   ``` c++
50   // 创建文件操作符 fd,打开时对文件句柄必须有读权限
51   std::string fileName = "test.mp4";
52   int fd = open(fileName.c_str(), O_RDONLY);
53   struct stat fileStatus {};
54   size_t fileSize = 0;
55   if (stat(fileName.c_str(), &fileStatus) == 0) {
56      fileSize = static_cast<size_t>(fileStatus.st_size);
57   } else {
58      printf("get stat failed");
59      return;
60   }
61   // 为 fd 资源文件创建 source 资源对象, 传入 offset 不为文件起始位置 或 size 不为文件大小时,可能会因不能获取完整数据导致 source 创建失败、或后续解封装失败等问题
62   OH_AVSource *source = OH_AVSource_CreateWithFD(fd, 0, fileSize);
63   if (source == nullptr) {
64      printf("create source failed");
65      return;
66   }
67   // 为 uri 资源文件创建 source 资源对象(可选)
68   // OH_AVSource *source = OH_AVSource_CreateWithURI(uri);
69   ```
70   ```c++
71   // 为资源对象创建对应的解封装器
72   OH_AVDemuxer *demuxer = OH_AVDemuxer_CreateWithSource(source);
73   if (demuxer == nullptr) {
74      printf("create demuxer failed");
75      return;
76   }
77   ```
78
79
80
812. 获取文件轨道数(可选,若用户已知轨道信息,可跳过此步)
82
83   ``` c++
84   // 从文件 source 信息获取文件轨道数
85   OH_AVFormat *sourceFormat = OH_AVSource_GetSourceFormat(source);
86   if (sourceFormat == nullptr) {
87      printf("get source format failed");
88      return;
89   }
90   int32_t trackCount = 0;
91   OH_AVFormat_GetIntValue(sourceFormat, OH_MD_KEY_TRACK_COUNT, &trackCount);
92   OH_AVFormat_Destroy(sourceFormat);
93   ```
94
95
96
973. 获取轨道index及信息(可选,若用户已知轨道信息,可跳过此步)
98
99   ``` c++
100   uint32_t audioTrackIndex = 0;
101   uint32_t videoTrackIndex = 0;
102   int32_t w = 0;
103   int32_t h = 0;
104   int32_t trackType;
105   for (uint32_t index = 0; index < (static_cast<uint32_t>(trackCount)); index++) {
106      // 获取轨道信息
107      OH_AVFormat *trackFormat = OH_AVSource_GetTrackFormat(source, index);
108      if (trackFormat == nullptr) {
109         printf("get track format failed");
110         return;
111      }
112      OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_TRACK_TYPE, &trackType);
113      static_cast<OH_MediaType>(trackType) == OH_MediaType::MEDIA_TYPE_AUD ? audioTrackIndex = index : videoTrackIndex = index;
114      // 获取视频轨宽高
115      if (trackType == OH_MediaType::MEDIA_TYPE_VID) {
116         OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_WIDTH, &w);
117         OH_AVFormat_GetIntValue(trackFormat, OH_MD_KEY_HEIGHT, &h);
118      }
119      OH_AVFormat_Destroy(trackFormat);
120   }
121   ```
122
123
124
1254. 添加解封装轨道
126
127   ``` c++
128   if(OH_AVDemuxer_SelectTrackByID(demuxer, audioTrackIndex) != AV_ERR_OK){
129      printf("select audio track failed: %d", audioTrackIndex);
130      return;
131   }
132   if(OH_AVDemuxer_SelectTrackByID(demuxer, videoTrackIndex) != AV_ERR_OK){
133      printf("select video track failed: %d", videoTrackIndex);
134      return;
135   }
136   // 取消选择轨道(可选)
137   // OH_AVDemuxer_UnselectTrackByID(demuxer, audioTrackIndex);
138   ```
139
140
141
1425. 调整轨道到指定时间点(可选)
143
144   ``` c++
145   // 调整轨道到指定时间点,后续从该时间点进行解封装
146   // 注意:
147   // 1. mpegts格式文件使用OH_AVDemuxer_SeekToTime功能时,跳转到的位置可能为非关键帧。可在跳转后调用OH_AVDemuxer_ReadSample,通过获取到的OH_AVCodecBufferAttr判断当前帧是否为关键帧。若非关键帧影响应用侧显示等功能,可在跳转后循环读取,获取到后续第一帧关键帧后,再进行解码等处理。
148   // 2. ogg格式文件使用OH_AVDemuxer_SeekToTime功能时,会跳转到传入时间millisecond所在时间间隔(秒)的起始处,可能会导致一定数量的帧误差。
149   OH_AVDemuxer_SeekToTime(demuxer, 0, OH_AVSeekMode::SEEK_MODE_CLOSEST_SYNC);
150   ```
151
1526. 开始解封装,循环获取帧数据(以含音频、视频两轨的文件为例)
153
154   ``` c++
155   // 创建 buffer,用与保存用户解封装得到的数据
156   OH_AVMemory *buffer = OH_AVMemory_Create(w * h * 3 >> 1);
157   if (buffer == nullptr) {
158      printf("build buffer failed");
159      return;
160   }
161   OH_AVCodecBufferAttr info;
162   bool videoIsEnd = false;
163   bool audioIsEnd = false;
164   int32_t ret;
165   while (!audioIsEnd || !videoIsEnd) {
166      // 在调用 OH_AVDemuxer_ReadSample 接口获取数据前,需要先调用 OH_AVDemuxer_SelectTrackByID 选中需要获取数据的轨道
167      // 获取音频帧数据
168      if(!audioIsEnd) {
169         ret = OH_AVDemuxer_ReadSample(demuxer, audioTrackIndex, buffer, &info);
170         if (ret == AV_ERR_OK) {
171            // 可通过 buffer 获取并处理音频帧数据
172            printf("audio info.size: %d\n", info.size);
173            if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
174               audioIsEnd = true;
175            }
176         }
177      }
178      if(!videoIsEnd) {
179         ret = OH_AVDemuxer_ReadSample(demuxer, videoTrackIndex, buffer, &info);
180         if (ret == AV_ERR_OK) {
181            // 可通过 buffer 获取并处理视频帧数据
182            printf("video info.size: %d\n", info.size);
183            if (info.flags == OH_AVCodecBufferFlags::AVCODEC_BUFFER_FLAGS_EOS) {
184               videoIsEnd = true;
185            }
186         }
187      }
188   }
189   OH_AVMemory_Destroy(buffer);
190   ```
191
192
193
1947. 销毁解封装实例
195
196   ``` c++
197   // 需要用户调用 OH_AVSource_Destroy 接口成功后,手动将对象置为 NULL,对同一对象重复调用 OH_AVSource_Destroy 会导致程序错误
198   if (OH_AVSource_Destroy(source) != AV_ERR_OK) {
199      printf("destroy source pointer error");
200   }
201   source = NULL;
202   // 需要用户调用 OH_AVDemuxer_Destroy 接口成功后,手动将对象置为 NULL,对同一对象重复调用 OH_AVDemuxer_Destroy 会导致程序错误
203   if (OH_AVDemuxer_Destroy(demuxer) != AV_ERR_OK) {
204      printf("destroy demuxer pointer error");
205   }
206   demuxer = NULL;
207   close(fd);
208   ```