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 ```