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