2

我的目标是使用 nvenc 对我的 Windows 机器的主帧缓冲区进行编码,并使用 VideoToolbox API 将其内容流式传输到我的 iPad

我用来编码 h264 流的代码基本上是https://github.com/NVIDIA/video-sdk-samples/tree/master/nvEncDXGIOutputDuplicationSample的复制/粘贴,唯一的变化是我没有写入文件,而是发送数据

对于解码,我使用https://github.com/zerdzhong/SwfitH264Demo/blob/master/SwiftH264/ViewController.swift#L71

当我将所有内容写入文件时,编码工作完美,我可以毫无问题地使用 h264->mp4 在线转换器,问题是解码器在函数 decompressionSessionDecodeFrameCallback 中给我错误 kVTVideoDecoderBadDataErr

所以对于我尝试过的:

  • 首先使用 h264 分析器,我发现帧顺序是:7/8/5/5/5/5/1 ...
  • 我发现 nvenc 确实只在一个数据包中编码帧 7/8/5/5/5/5
  • 我确实尝试使用序列(0x00 0x00 0x00 0x01)将此数据包分成多个,它分别给了我7/8/5帧
  • 如您所见,我只得到了一个 5 帧,大约 100KB,H264 分析器说有四个 5 帧(大概是 40KB、20KB、30KB、10KB)
  • 使用十六进制文件查看器,我看到分隔这 5 个帧的序列是 (0x00 0x00 0x01),尝试也将它们分开,但解压缩时出现完全相同的 VideoToolbox 错误

这是我用来分离和发送帧的代码:协议只是 PACKET_SIZE->PACKET_DATA 快速代码能够读取 NALU 类型,所以我相信这不是问题

    unsafe {
        Setup();
        loop {
            CaptureFrame();

            let frame_count = GetDataCount();
            if frame_count == 0 {
                continue;
            }

            for i in 0..frame_count {
                let size = RetrieveDataSize(i as i32);
                let size_slice = &(u32::to_le_bytes(size as u32));

                let data = RetrieveData(i as i32);
                let data_slice = std::slice::from_raw_parts(data, size);

                let mut last_frame = 0;

                for x in 0..size {
                    if data_slice[x] == 0 &&
                        data_slice[x + 1] == 0 &&
                        data_slice[x + 2] == 0 &&
                        data_slice[x + 3] == 1 {
                        let frame_size = x - last_frame;
                        if frame_size > 0 {
                            let frame_data = &data_slice[last_frame..x];
                            stream.write(&(u32::to_le_bytes(frame_size as u32))).unwrap();
                            stream.write(frame_data).unwrap();
                            println!("SEND MULTIPLE {}", frame_size);
                        }

                        last_frame = x;
                        println!("NALU {}", data_slice[x + 4] & 0x1F);
                        //println!("TEST {} {}",i, size);
                        continue;
                    }
                }
                // Packet was a single frame
                let frame_size = size - last_frame;
                let frame_data = &data_slice[last_frame..size];
                stream.write(&(u32::to_le_bytes(frame_size as u32))).unwrap();
                stream.write(frame_data).unwrap();
                println!("SEND SINGLE {} {}", last_frame, size);
            }
        }
    }

可能与纹理格式有关,VideoToolbox 提到了 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,NVENC 代码提到了 YUV420 和 NV12,我不确定两者是否相同

这是我的格式说明:

Optional(<CMVideoFormatDescription 0x2823dd410 [0x1e0921e20]> {
    mediaType:'vide' 
    mediaSubType:'avc1' 
    mediaSpecific: {
        codecType: 'avc1'       dimensions: 3840 x 2160 
    } 
    extensions: {{
    CVFieldCount = 1;
    CVImageBufferChromaLocationBottomField = Left;
    CVImageBufferChromaLocationTopField = Left;
    CVPixelAspectRatio =     {
        HorizontalSpacing = 1;
        VerticalSpacing = 1;
    };
    FullRangeVideo = 0;
    SampleDescriptionExtensionAtoms =     {
        avcC = {length = 41, bytes = 0x01640033 ffe10016 67640033 ac2b401e ... 68ee3cb0 fdf8f800 };
    };
}}
})
4

1 回答 1

0

好吧,虽然听起来很奇怪,但我的代码确实可以在模拟器上运行,但不能在我的 iPad pro 上运行。最后它确实有效,所以我仍然会将其标记为正确答案

于 2020-06-24T15:46:29.777 回答