【问题标题】:how to read/assign the elements of a pointer that points to an array of structures in C++如何读取/分配指向 C++ 中结构数组的指针的元素
【发布时间】:2012-09-16 17:01:20
【问题描述】:

在 iOS 核心音频中有一个 API AudioFileWritePackets 有一个 inPacketDescriptions 参数定义为 '指向音频数据的数据包描述数组的指针。

在方法签名中看起来像这样:
const AudioStreamPacketDescription *inPacketDescriptions,

现在 struct AudioStreamPacketDescription 定义如下:

struct  AudioStreamPacketDescription
{
    SInt64  mStartOffset;
    UInt32  mVariableFramesInPacket;
    UInt32  mDataByteSize;
};
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;

我想知道如何创建和填充这样的“指向结构数组的指针”,甚至在给定变量后如何读取它。使用来自苹果的speakHere 示例,我在接收变量的位置放置了一个断点,并尝试将其所有内容转储到日志中。这是一个尝试示例:

void AQRecorder::printPacketDescriptionContents(const AudioStreamPacketDescription * inPacketDescriptions, UInt32 inNumberPackets)
{
    for (int i = 0; i < inNumberPackets; ++i)
    {
        NSLog(@"\n----------------\n");
        NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld", i, (*inPacketDescriptions).mStartOffset);
        NSLog(@"this is packetDescriptionArray[%d].mVariableFramesInPacket: %lu", i, (*inPacketDescriptions).mVariableFramesInPacket);
        NSLog(@"this is packetDescriptionArray[%d].mDataByteSize: %lu", i, (*inPacketDescriptions).mDataByteSize);
        NSLog(@"\n----------------\n");   

    }        
}

有什么想法吗?

更新: 这是我试图弄乱它的示例日志。也许它可以帮助回答(注意在底部它一直显示为空。它没有有意义的是,整个事情只是一堆零,因为它是一个由回调返回的变量,应该被正确填充,还请注意它告诉我我返回的数据包数量..) .. 另外,如果我使用 ((const AudioStreamPacketDescription *)(inPacketDescriptions +i))-&gt;mDataByteSize) 运行代码,我会收到 EXC_BAD_ACCESS 错误

(lldb) po **(inPacketDescriptions)
error: indirection requires pointer operand ('const AudioStreamPacketDescription' invalid)
error: 1 errors parsing expression
(lldb) po *(inPacketDescriptions)
(AudioStreamPacketDescription) $1 = [no Objective-C description available]
(lldb) po *(inPacketDescriptions).mStartOffset
error: member reference type 'const AudioStreamPacketDescription *' is a pointer; maybe you meant to use '->'?
error: indirection requires pointer operand ('SInt64' (aka 'long long') invalid)
error: 2 errors parsing expression
(lldb) po (*inPacketDescriptions).mStartOffset
(SInt64) $2 = 0 <nil>

(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)
(const class AudioStreamPacketDescription *) $3 = 0x00000010 [no Objective-C description available]
(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)->mStartOffset
error: Execution was interrupted, reason: Attempted to dereference an invalid pointer..
The process has been returned to the state before execution.
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mStartOffset
(SInt64) $5 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mDataByteSize
(UInt32) $6 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +100))->mDataByteSize
(UInt32) $7 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +500))->mDataByteSize
(UInt32) $8 = 0 <nil>

(lldb) po inPacketDescriptions[0].mStartOffset
error: parent failed to evaluate: parent is NULL
(lldb) 

这也是它在 XCode 检查器中的样子:

【问题讨论】:

    标签: c++ arrays pointers struct core-audio


    【解决方案1】:

    声明一个AudioStreamPacketDescription的数组:

    AudioStreamPacketDescription descriptions[10];
    
    /* Populate 'descriptions' */
    descriptions[0].mVariableFramesInPacket = 4; /* For example. */
    

    然后将其传递给函数:

    /* 'a' is an instance of AQRecorder. */
    a.printPacketDescriptionContents(descriptions, 10);
    

    当一个数组被传递给一个函数时,它会衰减为一个指针(指向数组的第一个元素)。

    示例printPacketDescriptionContents() 仅访问数组的第一个元素inNumberPackets 次:您需要访问每个元素:

    NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld",
          i,
          inPacketDescriptions[i].mStartOffset);
    

    【讨论】:

    • 我之前完全尝试过(即 inPacketDescriptions[i].mStartOffset).. 错误是:parent failed to evaluate: parent is NULL
    • @www.fossfactory.org,不确定是什么问题。查看这个小演示ideone.com/UGRhi
    • 您链接到的代码只是您创建的自定义数据结构(我相信)。不幸的是,它不一定符合我正在处理的 Core Audio 中的那个,也不是行为方式相同..
    • @www.fossfactory.org,它包含对数据类型的猜测,是的,但给出了一个声明、填充和访问 struct 数组的工作程序示例。
    【解决方案2】:

    我不记得您(客户)曾经填充过这个特定结构的实例。您需要为这些结构创建 storage,然后将其传递给多个调用,以便成功处理读取和写入音频数据。几种非 PCM 格式将需要此信息,具体取决于音频数据的存储方式。

    我想知道如何创建和填充这样的“指向结构数组的指针”,甚至一旦给定变量,如何读取它。

    嗯,AudioFile I/O 和 AudioConvertor 接口中有一些 API 使用这种结构。基本上,您不会自己填充此类型。基本流程是这样的:

    // this is not for PCM audio data
    //
    // we'll read up to 8 packets at a time:
    const size_t MaxPacketsToRead(8);
    
    // allocate MaxPacketsToRead ASPDs on the stack:
    AudioStreamPacketDescription aspds[MaxPacketsToRead];
    
    // audio file read function:
    AudioFileID inAudioFile = ...;
    Boolean inUseCache = ...;
    UInt32 outNumBytes = ...;
    AudioStreamPacketDescription* outPacketDescriptions(aspds);
    SInt64 inStartingPacket = ...;
    UInt32 ioNumPackets = MaxPacketsToRead; // << you may not get all the packets
                                            // you request, but this sets the
                                            // upper limit.
    void* outBuffer = ...;
    
    
    OSStatus result(AudioFileReadPackets(inAudioFile,
                                         inUseCache,
                                         &outNumBytes,
                                         outPacketDescriptions,
                                         inStartingPacket,
                                         &ioNumPackets,
                                         outBuffer
    ));
    
    if (noErr != result) {
      ...uh-oh...
    }
    
    // *now* we know that we have ioNumPackets worth of valid ASPDs,
    // populated by the reader. and we have the associated audio data
    // in other parameters.
    // we can now safely pass all this information off to a function which 
    // reads the ASPDs, such as AudioFileWritePackets.
    

    (更详细地了解 OP 的问题) 在许多情况下,您可以避免所有这些复杂性,只需创建一个 ExtAudioFile 表示,并为您的目标样本格式指定 kExtAudioFileProperty_ClientDataFormat -- 然后 ExtAudioFile API 将代表您创建一个内部转换器,它将任意类型的音频文件的输入转换为一些指定的 PCM 表示,您可以将其用于播放样本数据。如果您想支持多种文件格式,那么在这个级别实现所有这些实际上是相当复杂的。 ExtAudioFile 使转换样本数据变得非常容易——如果这是一个选项并且它与您的流媒体场景很好地配合的话。

    就日志记录而言,从外观上看,您正在尝试打印 NULL 结构的字段。

    【讨论】:

    • 你对空结构部分是对的。事实证明我正在读取的文件是 PCM(未压缩)。它自然没有数据包描述。我是我自己玩数据包描述的内容是,我正在从音频文件中读取这些数据包描述(使用 CMSampleBufferGetAudioStreamPacketDescriptionsPtr)并将这些信息打包在我创建的定制数据包中,以便通过蓝牙发送数据。一旦我在客户端..我必须从我发送的数据包中提取数据并将其打包在适当的..
    • @www.fossfactory.org 我玩数据包描述内容的原因…… 啊,酷。在这种情况下这是有道理的,如果您无法简单地将您流式传输的音频文件作为原始数据传输并从您的阅读器中读取它(由于数据的分布,使用某些格式实现它也可能很复杂 - 那也就是说,在某些情况下,您可能需要较长的时间才能获得足够的信息才能开始播放)。
    • .. AudioStreamPacketDescription 格式,我将依次将其提供给 AudioFileWritePackets.. 这就是计划。在相关的说明中.. 我正在进行一些其他音频实验,我注意到在一种情况下,我通过 AudioFileReadPackets 读取音频数据并通过网络发送它没有 audioPacketDescriptions .. 即使它是一个 VBR 文件(即 .mp3).. 在接收端我获取数据包并通过 AudioFileStreamParseBytes 解析它.. 这反过来又重新创建了 packetDescriptions(使用它的 AudioFileStream_PacketsProc 回调).. 知道为什么发生这种情况?
    • @www.fossfactory.org AudioFileStreamParseBytes 旨在处理存储在磁盘上的文件。我很惊讶它似乎在解码过程之后起作用。 (耸耸肩) 这个想法是,您可以在数据可用时将文件输入流式传输到其中 - 然后它会在有足够的数据来理解文件时通知您(说明显而易见)。
    • @justin 我有一个类似的问题,const size_t MaxPacketsToRead(8); 不过这对我不起作用,我正在尝试AudioStreamPacketDescription tempDesc[packetsFilledDesc]; 其中packetsFilledDesc 是在运行时在for 循环之后计算的。然而,由于它是一个结构数组,它返回 null。如果我这样做 AudioStreamPacketDescription tempDesc[24]; 它可以工作,但我不能使用常量。我该如何解决这个问题?
    猜你喜欢
    • 2021-12-14
    • 2017-01-04
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 2023-04-03
    相关资源
    最近更新 更多