HarmonyOS 鸿蒙Next Native侧如何将ArkTS侧Uint8Array的H264实时流传给VideoDecoder解码

发布于 1周前 作者 eggper 来自 鸿蒙OS

HarmonyOS 鸿蒙Next Native侧如何将ArkTS侧Uint8Array的H264实时流传给VideoDecoder解码

目前demo:

1. 本实例基于AVCodec能力,提供基于视频编解码的视频播放和录制的功能。(解析本地视频文件)

2. Native侧如何获取ArkTS侧的Uint8Array实例
目前通过demo2将h264实时流数据Uint8Array传到Native侧,通过demo1可解析本地视频文件,要实现实时流数据的解析,中间应该怎么进行转换,传给VideoDecoder解码?
 

// demo1 Player.cpp
void Player::DecInputThread() {
…省略
CodecBufferInfo bufferInfo = signal->inputBufferInfoQueue_.front();
signal->inputBufferInfoQueue_.pop();
signal->inputFrameCount_++;
lock.unlock();
// 按理解,应该改这里,将Uint8Array数据写入bufferInfo,不过不懂怎么转换
demuxer_->ReadSample(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer), bufferInfo.attr);

    int32_t ret = videoDecoder_-&gt;PushInputData(bufferInfo);
    <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (ret != AV_ERR_OK) {
        OH_LOG_ERROR(LOG_APP, <span class="hljs-string"><span class="hljs-string">"Push data failed, thread out"</span></span>);
        <span class="hljs-keyword"><span class="hljs-keyword">break</span></span>;
    }
 ...省略

}<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>



关于HarmonyOS 鸿蒙Next Native侧如何将ArkTS侧Uint8Array的H264实时流传给VideoDecoder解码的问题,您也可以访问:https://www.itying.com/category-93-b0.html 联系官网客服。

10 回复

找到大概突破口:

  1. 接收h264码流片段保存到本地test.h264。
  2. 转为mp4:ffmpeg -i input.h264 -c:v copy -c:a copy output.mp4
  3. 将output.mp4下载到手机上,用demo列表1的工程播放。
  4. 断点查看整体逻辑、编码参数。。。(正在进行。。。)

首先感谢网友的分享和帮助!!!
问题已解决,参见issues:https://gitee.com/kairen-13/AVCodecSample/issues
直接读裸码流官方demo: https://gitee.com/openharmony/multimedia_av_codec/tree/master/test/unittest/video_test/video_test/sample/decoder
实现思路:
0. 通过demo1导入工程直接可运行(https://gitee.com/harmonyos_samples/AVCodecVideo

1. ArkTS层传Uint8Array到Native,参考(2. Native侧如何获取ArkTS侧的Uint8Array实例

2. 屏蔽demo读文件的代码,修改为读码流,大概修改如下:
 

/ Player.cpp
void Player::DecInputThread() {
    //...省略
        CodecBufferInfo bufferInfo = signal->inputBufferInfoQueue_.front();
        signal->inputBufferInfoQueue_.pop();
        signal->inputFrameCount_++;
        lock.unlock();
        // 按理解,应该改这里,将Uint8Array数据写入bufferInfo
        // - demuxer_->ReadSample(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer), bufferInfo.attr);
        // =========Native不大懂,尝试修改======
        uint8_t * buf = ...; // ArkTS传的Uint8Array H264码流数据
        int bufSize = ...; // 码流长度
        OH_AVBuffer *buffer = reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer);
        std::memcpy(OH_AVBuffer_GetAddr(buffer), reinterpret_cast<char *>(buf), bufSize);
    <span class="hljs-comment"><span class="hljs-comment">// 赋值attr</span></span>
    <span class="hljs-comment"><span class="hljs-comment">// 解析buf是否包含”00 00 00 01 65“为I帧 flags=AVCODEC_BUFFER_FLAGS_SYNC_FRAME</span></span>
    <span class="hljs-comment"><span class="hljs-comment">// 解析buf是否包含”00 00 00 01 67/68“为SPS/PPS flags=AVCODEC_BUFFER_FLAGS_CODEC_DATA</span></span>
    <span class="hljs-comment"><span class="hljs-comment">// 其他为=AVCODEC_BUFFER_FLAGS_NONE</span></span>
    uint32_t flags = ...;
    bufferInfo.attr.size = bufSize;
    bufferInfo.attr.offset = <span class="hljs-number"><span class="hljs-number">0</span></span>;
    int64_t microseconds = <span class="hljs-number"><span class="hljs-number">1000000</span></span> / <span class="hljs-number"><span class="hljs-number">30</span></span> * (videoDecContext_-&gt;inputFrameCount - <span class="hljs-number"><span class="hljs-number">1</span></span>);
    bufferInfo.attr.pts = microseconds; <span class="hljs-comment"><span class="hljs-comment">// 此缓冲区的显示时间戳(以微秒为单位)</span></span>
    bufferInfo.attr.flags = flags;
    <span class="hljs-comment"><span class="hljs-comment">// =============</span></span>
    <span class="hljs-comment"><span class="hljs-comment">//--!!!非常重要--改码流后,需要再调下OH_AVBuffer_SetBufferAttr--</span></span>
    <span class="hljs-comment"><span class="hljs-comment">//---之前漏了这句,写入成功,导致无法解码出来</span></span>
    int32_t ret1 = OH_AVBuffer_SetBufferAttr(<span class="hljs-keyword"><span class="hljs-keyword">reinterpret_cast</span></span>&lt;OH_AVBuffer *&gt;(info.buffer), &amp;info.attr);
    CHECK_AND_RETURN_RET_LOG(ret1 == AV_ERR_OK, AVCODEC_SAMPLE_ERR_ERROR, <span class="hljs-string"><span class="hljs-string">"Set avbuffer attr failed"</span></span>);

//------------------- int32_t ret = videoDecoder_->PushInputData(bufferInfo); if (ret != AV_ERR_OK) { OH_LOG_ERROR(LOG_APP, “Push data failed, thread out”); break; } //…省略 }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

  1. 当前仅支持传入 annexB 格式帧 。
  2. 可以看下flags值、buffer size。

以下引用来自: (https://gitee.com/kairen-13/AVCodecSample/issues/IAJX6A)

问:支持输入H264裸码流解码吗? 答:支持的,传裸码流的话 ①当前仅支持传入 annexB 格式帧,不支持 AVCC 格式帧 ②确保 buffer size 正确传入 ③首帧需传 XPS 信息 仅关键帧(I帧): AVCODEC_BUFFER_FLAGS_SYNC_FRAME 仅配置帧(pps、sps): AVCODEC_BUFFER_FLAGS_CODEC_DATA 是配置帧又是关键帧(pps、sps + I帧) : AVCODEC_BUFFER_FLAGS_CODEC_DATA|AVCODEC_BUFFER_FLAGS_SYNC_FRAME 普通帧(P帧): AVCODEC_BUFFER_FLAGS_NONE

我这一段 std::memcpy(OH_AVBuffer_GetAddr(buffer), reinterpret_cast<char *>(buf), bufSize); 报错闪退,楼主有遇到过吗?

① 可以判空、打印长度。 ② 可以排查buf数据内容。 确定这行报错的话,这行前打印下buffer、buf是否为空,以及bufSize。 if (buffer == nullptr) { OH_LOG_ERROR(LOG_APP, “buffer 为空”); } if (buf == nullptr) { OH_LOG_ERROR(LOG_APP, “buf 为空”); } OH_LOG_ERROR(LOG_APP, “bufSize=%{public}d”, bufSize); // 排查收到数据是否正常 // 打印前5位:buf[4]为flags if (bufSize >= 5) { OH_LOG_ERROR(LOG_APP, “前5位16进制 0x%{public}02X 0x%{public}02X 0x%{public}02X 0x%{public}02X 0x%{public}02X\n”, buf[0], buf[1], buf[2], buf[3], buf[4]); OH_LOG_ERROR(LOG_APP, “前5位10进制 %{public}d %{public}d %{public}d %{public}d %{public}d\n”, buf[0], buf[1], buf[2], buf[3], buf[4]); } //[或者] 写入到文件查看:可加if判断只写首次数据,方便看 std::ofstream file("/data/storage/el2/base/haps/entry/files/test.h264"); if (file.is_open()) { file.write(reinterpret_cast<char*>(buf), bufSize); file.close(); }

你是要做类似直播一样的项目吗?

嗯,上层通过socket tcp连接编码服务器,接收实时H264码流,然后传给Native侧VideoCoder解码,显示到surface上。

开发者您好,该问题已反馈研发人员进一步分析,请耐心等待!
回到顶部