Convert C array of pointers to Python array of str

2019-08-08 02:11发布

问题:

I am writing a Python app that makes use of PulseAudio API. The implementation is heavily using callbacks written in Python and invoked by PulseAudio's C code.

The most information is passed into the callback by a specific structure, for instance pa_sink_info, which is defined in C as follows:

typedef struct pa_sink_info {
  const char *name;                  
  uint32_t index;                    
  const char *description;           
  pa_sample_spec sample_spec;        
  pa_channel_map channel_map;        
  uint32_t owner_module;             
  pa_cvolume volume;                 
  int mute;                          
  uint32_t monitor_source;           
  const char *monitor_source_name;   
  pa_usec_t latency;                 
  const char *driver;                
  pa_sink_flags_t flags;             
  pa_proplist *proplist;             
  pa_usec_t configured_latency;      
  pa_volume_t base_volume;           
  pa_sink_state_t state;             
  uint32_t n_volume_steps;           
  uint32_t card;                     
  uint32_t n_ports;                  
  pa_sink_port_info** ports;         
  pa_sink_port_info* active_port;    
  uint8_t n_formats;                 
  pa_format_info **formats;          
} pa_sink_info;

From this structure it's very easy to get scalar values, eg.:

self.some_proc(
  struct.contents.index,
  struct.contents.name,
  struct.contents.description)

But I have a difficulty dealing with ports and active_port, which in Python are described as:

('n_ports', uint32_t),
('ports', POINTER(POINTER(pa_sink_port_info))),
('active_port', POINTER(pa_sink_port_info)),

Here n_ports specifies number of elements in ports, which is a pointer to array of pointers to structures of type pa_sink_port_info. Actually, I don't even know how I can convert these to Python types at all.

What is the most efficient way of converting ports into Python dictionary containing pa_sink_port_info's?

回答1:

Solving this problem required careful reading of Python's ctypes reference. Once the mechanism of ctypes type translation implementation was clear, it's not so difficult to get to the desired values.

The main idea about pointers is that you use their contents attribute to get to the data the pointer points to. Another useful thing to know is that pointers can be indexed like arrays (it's not validated by the interpreter, so it's your own responsibility to make sure it is indeed an array).

For this particular PulseAudio example, we can process the ports structure member (which is a pointer to array of pointers) as follows:

port_list = []
if struct.contents.ports:
  i = 0
  while True:
    port_ptr = struct.contents.ports[i]
    # NULL pointer terminates the array
    if port_ptr:
      port_struct = port_ptr.contents
      port_list.append(port_struct.name)
      i += 1
    else:
      break