mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	pulse-server: handle format_info
Parse the format_info in CREATE_STREAM and use this to negotiate the stream. Implement channel_map parsing. Delay setting up the buffer attributes until we have negotiated a format. Makes GStreamer pulsesink work (rhythmbox,... )
This commit is contained in:
		
							parent
							
								
									aea7ace124
								
							
						
					
					
						commit
						5b32125d44
					
				
					 2 changed files with 384 additions and 142 deletions
				
			
		| 
						 | 
					@ -22,6 +22,9 @@
 | 
				
			||||||
 * DEALINGS IN THE SOFTWARE.
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RATE_MAX	(48000u*8u)
 | 
				
			||||||
 | 
					#define CHANNELS_MAX	(64u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum sample_format {
 | 
					enum sample_format {
 | 
				
			||||||
	SAMPLE_U8,
 | 
						SAMPLE_U8,
 | 
				
			||||||
	SAMPLE_ALAW,
 | 
						SAMPLE_ALAW,
 | 
				
			||||||
| 
						 | 
					@ -69,6 +72,16 @@ static inline uint32_t format_pa2id(enum sample_format format)
 | 
				
			||||||
	return audio_formats[format].format;
 | 
						return audio_formats[format].format;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline enum sample_format format_name2pa(const char *name, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) {
 | 
				
			||||||
 | 
							if (strncmp(name, audio_formats[i].name, size) == 0)
 | 
				
			||||||
 | 
								return i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return SAMPLE_INVALID;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline enum sample_format format_id2pa(uint32_t id)
 | 
					static inline enum sample_format format_id2pa(uint32_t id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t i;
 | 
						size_t i;
 | 
				
			||||||
| 
						 | 
					@ -79,7 +92,6 @@ static inline enum sample_format format_id2pa(uint32_t id)
 | 
				
			||||||
	return SAMPLE_INVALID;
 | 
						return SAMPLE_INVALID;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
struct sample_spec {
 | 
					struct sample_spec {
 | 
				
			||||||
	enum sample_format format;
 | 
						enum sample_format format;
 | 
				
			||||||
	uint32_t rate;
 | 
						uint32_t rate;
 | 
				
			||||||
| 
						 | 
					@ -93,13 +105,184 @@ static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
 | 
				
			||||||
	return audio_formats[ss->format].size * ss->channels;
 | 
						return audio_formats[ss->format].size * ss->channels;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CHANNELS_MAX	64
 | 
					static inline bool sample_spec_valid(const struct sample_spec *ss)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (ss->format < SAMPLE_MAX &&
 | 
				
			||||||
 | 
						    ss->rate > 0 && ss->rate <= RATE_MAX &&
 | 
				
			||||||
 | 
						    ss->channels > 0 && ss->channels <= CHANNELS_MAX);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum channel_position {
 | 
				
			||||||
 | 
						CHANNEL_POSITION_INVALID = -1,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_MONO = 0,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_FRONT_LEFT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_FRONT_RIGHT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_FRONT_CENTER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_REAR_CENTER,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_REAR_LEFT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_REAR_RIGHT,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_LFE,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_SIDE_LEFT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_SIDE_RIGHT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX0,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX1,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX2,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX3,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX4,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX5,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX6,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX7,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX8,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX9,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX10,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX11,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX12,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX13,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX14,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX15,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX16,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX17,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX18,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX19,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX20,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX21,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX22,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX23,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX24,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX25,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX26,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX27,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX28,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX29,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX30,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_AUX31,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_CENTER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_FRONT_LEFT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_FRONT_RIGHT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_FRONT_CENTER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_REAR_LEFT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_REAR_RIGHT,
 | 
				
			||||||
 | 
						CHANNEL_POSITION_TOP_REAR_CENTER,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CHANNEL_POSITION_MAX
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct channel {
 | 
				
			||||||
 | 
						uint32_t channel;
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct channel audio_channels[] = {
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_MONO] = { SPA_AUDIO_CHANNEL_MONO, "mono", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_FL, "front-left", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_FR, "front-right", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_FC, "front-center", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_REAR_CENTER] = { SPA_AUDIO_CHANNEL_RC, "rear-center", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_REAR_LEFT] = { SPA_AUDIO_CHANNEL_RL, "rear-left", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_RR, "rear-right", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_LFE] = { SPA_AUDIO_CHANNEL_LFE, "lfe", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FLC, "front-left-of-center", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = { SPA_AUDIO_CHANNEL_FRC, "front-right-of-center", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_SIDE_LEFT] = { SPA_AUDIO_CHANNEL_SL, "side-left", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_SIDE_RIGHT] = { SPA_AUDIO_CHANNEL_SR, "side-right", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX0] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 1, "aux0", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX1] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 2, "aux1", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX2] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 3, "aux2", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX3] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 4, "aux3", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX4] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 5, "aux4", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX5] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 6, "aux5", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX6] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 7, "aux6", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX7] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 8, "aux7", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX8] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 9, "aux8", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX9] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 10, "aux9", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX10] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 11, "aux10", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX11] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 12, "aux11", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX12] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 13, "aux12", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX13] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 14, "aux13", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX14] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 15, "aux14", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX15] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 16, "aux15", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX16] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 17, "aux16", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX17] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 18, "aux17", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX18] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 19, "aux18", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX19] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 20, "aux19", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX20] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 21, "aux20", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX21] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 22, "aux21", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX22] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 23, "aux22", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX23] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 24, "aux23", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX24] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 25, "aux24", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX25] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 26, "aux25", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX26] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 27, "aux26", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX27] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 28, "aux27", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX28] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 29, "aux28", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX29] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 30, "aux29", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX30] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 31, "aux30", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_AUX31] = { SPA_AUDIO_CHANNEL_CUSTOM_START + 32, "aux31", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_CENTER] = { SPA_AUDIO_CHANNEL_TC, "top-center", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_FRONT_LEFT] = { SPA_AUDIO_CHANNEL_TFL, "top-front-left", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_FRONT_RIGHT] = { SPA_AUDIO_CHANNEL_TFR, "top-front-right", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_FRONT_CENTER] = { SPA_AUDIO_CHANNEL_TFC, "top-front-center", },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_REAR_LEFT] = { SPA_AUDIO_CHANNEL_TRL, "top-rear-left", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_REAR_RIGHT] = { SPA_AUDIO_CHANNEL_TRR, "top-rear-right", },
 | 
				
			||||||
 | 
						[CHANNEL_POSITION_TOP_REAR_CENTER] = { SPA_AUDIO_CHANNEL_TRC, "top-rear-center", },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct channel_map {
 | 
					struct channel_map {
 | 
				
			||||||
	uint8_t channels;
 | 
						uint8_t channels;
 | 
				
			||||||
	uint32_t map[CHANNELS_MAX];
 | 
						enum channel_position map[CHANNELS_MAX];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline uint32_t channel_pa2id(enum channel_position channel)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
 | 
				
			||||||
 | 
					                return SPA_AUDIO_CHANNEL_UNKNOWN;
 | 
				
			||||||
 | 
					        return audio_channels[channel].channel;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline enum channel_position channel_id2pa(uint32_t id, uint32_t *aux)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) {
 | 
				
			||||||
 | 
							if (id == audio_channels[i].channel)
 | 
				
			||||||
 | 
								return i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return CHANNEL_POSITION_AUX0 + (*aux)++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline enum channel_position channel_name2pa(const char *name, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						for (i = 0; i < SPA_N_ELEMENTS(audio_channels); i++) {
 | 
				
			||||||
 | 
							if (strncmp(name, audio_channels[i].name, size) == 0)
 | 
				
			||||||
 | 
								return i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return CHANNEL_POSITION_INVALID;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void channel_map_to_positions(const struct channel_map *map, uint32_t *pos)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 0; i < map->channels; i++)
 | 
				
			||||||
 | 
							pos[i] = channel_pa2id(map->map[i]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cvolume {
 | 
					struct cvolume {
 | 
				
			||||||
	uint8_t channels;
 | 
						uint8_t channels;
 | 
				
			||||||
	float values[CHANNELS_MAX];
 | 
						float values[CHANNELS_MAX];
 | 
				
			||||||
| 
						 | 
					@ -127,7 +310,7 @@ struct format_info {
 | 
				
			||||||
static int format_parse_param(const struct spa_pod *param, struct sample_spec *ss, struct channel_map *map)
 | 
					static int format_parse_param(const struct spa_pod *param, struct sample_spec *ss, struct channel_map *map)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct spa_audio_info info = { 0 };
 | 
						struct spa_audio_info info = { 0 };
 | 
				
			||||||
//	uint32_t i;
 | 
						uint32_t i, aux = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        spa_format_parse(param, &info.media_type, &info.media_subtype);
 | 
					        spa_format_parse(param, &info.media_type, &info.media_subtype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,8 +329,64 @@ static int format_parse_param(const struct spa_pod *param, struct sample_spec *s
 | 
				
			||||||
        ss->channels = info.info.raw.channels;
 | 
					        ss->channels = info.info.raw.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	map->channels = info.info.raw.channels;
 | 
						map->channels = info.info.raw.channels;
 | 
				
			||||||
//	for (i = 0; i < map->channels; i++)
 | 
						for (i = 0; i < map->channels; i++)
 | 
				
			||||||
//		map->map[i] = info.info.raw.position[i];
 | 
							map->map[i] = channel_id2pa(info.info.raw.position[i], &aux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct spa_pod *format_build_param(struct spa_pod_builder *b,
 | 
				
			||||||
 | 
							uint32_t id, struct sample_spec *spec, struct channel_map *map)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct spa_audio_info_raw info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info = SPA_AUDIO_INFO_RAW_INIT(
 | 
				
			||||||
 | 
								.format = format_pa2id(spec->format),
 | 
				
			||||||
 | 
								.channels = spec->channels,
 | 
				
			||||||
 | 
								.rate = spec->rate);
 | 
				
			||||||
 | 
						if (map)
 | 
				
			||||||
 | 
							channel_map_to_positions(map, info.position);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return spa_format_audio_raw_build(b, id, &info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct spa_pod *format_info_build_param(struct spa_pod_builder *b,
 | 
				
			||||||
 | 
							uint32_t id, struct format_info *info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *str;
 | 
				
			||||||
 | 
						struct sample_spec ss;
 | 
				
			||||||
 | 
						struct channel_map map, *pmap = NULL;
 | 
				
			||||||
 | 
						size_t size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(ss);
 | 
				
			||||||
 | 
						spa_zero(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str = pw_properties_get(info->props, "format.sample_format")) == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						if (str[0] != '\"')
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						str++;
 | 
				
			||||||
 | 
						size = strcspn(str, "\"");
 | 
				
			||||||
 | 
						ss.format = format_name2pa(str, size);
 | 
				
			||||||
 | 
						if (ss.format == SAMPLE_INVALID)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str = pw_properties_get(info->props, "format.rate")) == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						ss.rate = atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str = pw_properties_get(info->props, "format.channels")) == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						ss.channels = atoi(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((str = pw_properties_get(info->props, "format.channel_map")) != NULL) {
 | 
				
			||||||
 | 
							while ((*str == '\"' || *str == ',') &&
 | 
				
			||||||
 | 
							    (size = strcspn(++str, "\",")) > 0) {
 | 
				
			||||||
 | 
								map.map[map.channels++] = channel_name2pa(str, size);
 | 
				
			||||||
 | 
								str += size;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (map.channels == ss.channels)
 | 
				
			||||||
 | 
								pmap = ↦
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return format_build_param(b, id, &ss, pmap);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -623,6 +623,64 @@ static int send_command_request(struct stream *stream)
 | 
				
			||||||
	return send_message(client, msg);
 | 
						return send_message(client, msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t usec_to_bytes_round_up(uint64_t usec, const struct sample_spec *ss)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint64_t u;
 | 
				
			||||||
 | 
						u = (uint64_t) usec * (uint64_t) ss->rate;
 | 
				
			||||||
 | 
						u = (u + 1000000UL - 1) / 1000000UL;
 | 
				
			||||||
 | 
						u *= sample_spec_frame_size(ss);
 | 
				
			||||||
 | 
						return (uint32_t) u;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t frame_size, max_prebuf, minreq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame_size = s->frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH)
 | 
				
			||||||
 | 
							attr->maxlength = MAXLENGTH;
 | 
				
			||||||
 | 
						attr->maxlength -= attr->maxlength % frame_size;
 | 
				
			||||||
 | 
						attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->tlength == (uint32_t) -1)
 | 
				
			||||||
 | 
							attr->tlength = usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*1000, &s->ss);
 | 
				
			||||||
 | 
						if (attr->tlength > attr->maxlength)
 | 
				
			||||||
 | 
							attr->tlength = attr->maxlength;
 | 
				
			||||||
 | 
						attr->tlength -= attr->tlength % frame_size;
 | 
				
			||||||
 | 
						attr->tlength = SPA_MAX(attr->tlength, frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->minreq == (uint32_t) -1) {
 | 
				
			||||||
 | 
							uint32_t process = usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*1000, &s->ss);
 | 
				
			||||||
 | 
							/* With low-latency, tlength/4 gives a decent default in all of traditional,
 | 
				
			||||||
 | 
							 * adjust latency and early request modes. */
 | 
				
			||||||
 | 
							uint32_t m = attr->tlength / 4;
 | 
				
			||||||
 | 
							m -= m % frame_size;
 | 
				
			||||||
 | 
							attr->minreq = SPA_MIN(process, m);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						minreq = usec_to_bytes_round_up(MIN_USEC, &s->ss);
 | 
				
			||||||
 | 
						attr->minreq = SPA_MAX(attr->minreq, minreq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->tlength < attr->minreq+frame_size)
 | 
				
			||||||
 | 
							attr->tlength = attr->minreq + frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						attr->minreq -= attr->minreq % frame_size;
 | 
				
			||||||
 | 
						if (attr->minreq <= 0) {
 | 
				
			||||||
 | 
							attr->minreq = frame_size;
 | 
				
			||||||
 | 
							attr->tlength += frame_size*2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (attr->tlength <= attr->minreq)
 | 
				
			||||||
 | 
							attr->tlength = attr->minreq*2 + frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_prebuf = attr->tlength + frame_size - attr->minreq;
 | 
				
			||||||
 | 
						if (attr->prebuf == (uint32_t) -1 || attr->prebuf > max_prebuf)
 | 
				
			||||||
 | 
							attr->prebuf = max_prebuf;
 | 
				
			||||||
 | 
						attr->prebuf -= attr->prebuf % frame_size;
 | 
				
			||||||
 | 
						attr->prebuf = SPA_MAX(attr->prebuf, frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_info(NAME" %p: maxlength:%u tlength:%u minreq:%u prebuf:%u", s,
 | 
				
			||||||
 | 
								attr->maxlength, attr->tlength, attr->minreq, attr->prebuf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int reply_create_playback_stream(struct stream *stream)
 | 
					static int reply_create_playback_stream(struct stream *stream)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -630,6 +688,18 @@ static int reply_create_playback_stream(struct stream *stream)
 | 
				
			||||||
	struct impl *impl = client->impl;
 | 
						struct impl *impl = client->impl;
 | 
				
			||||||
	struct message *reply;
 | 
						struct message *reply;
 | 
				
			||||||
	uint32_t size;
 | 
						uint32_t size;
 | 
				
			||||||
 | 
						struct spa_dict_item items[1];
 | 
				
			||||||
 | 
						char latency[32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fix_playback_buffer_attr(stream, &stream->attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						snprintf(latency, sizeof(latency)-1, "%u/%u",
 | 
				
			||||||
 | 
								stream->attr.minreq * 2 / stream->frame_size,
 | 
				
			||||||
 | 
								stream->ss.rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency);
 | 
				
			||||||
 | 
						pw_stream_update_properties(stream->stream,
 | 
				
			||||||
 | 
								&SPA_DICT_INIT(items, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size = writable_size(stream, 0);
 | 
						size = writable_size(stream, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -678,11 +748,49 @@ static int reply_create_playback_stream(struct stream *stream)
 | 
				
			||||||
	return send_message(client, reply);
 | 
						return send_message(client, reply);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t frame_size, minfrag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						frame_size = s->frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH)
 | 
				
			||||||
 | 
							attr->maxlength = MAXLENGTH;
 | 
				
			||||||
 | 
						attr->maxlength -= attr->maxlength % frame_size;
 | 
				
			||||||
 | 
						attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						minfrag = usec_to_bytes_round_up(MIN_USEC, &s->ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0)
 | 
				
			||||||
 | 
							attr->fragsize = usec_to_bytes_round_up(DEFAULT_FRAGSIZE_MSEC*1000, &s->ss);
 | 
				
			||||||
 | 
						attr->fragsize -= attr->fragsize % frame_size;
 | 
				
			||||||
 | 
						attr->fragsize = SPA_MAX(attr->fragsize, minfrag);
 | 
				
			||||||
 | 
						attr->fragsize = SPA_MAX(attr->fragsize, frame_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (attr->fragsize > attr->maxlength)
 | 
				
			||||||
 | 
							attr->fragsize = attr->maxlength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_info(NAME" %p: maxlength:%u fragsize:%u minfrag:%u", s,
 | 
				
			||||||
 | 
								attr->maxlength, attr->fragsize, minfrag);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int reply_create_record_stream(struct stream *stream)
 | 
					static int reply_create_record_stream(struct stream *stream)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct client *client = stream->client;
 | 
						struct client *client = stream->client;
 | 
				
			||||||
	struct impl *impl = client->impl;
 | 
						struct impl *impl = client->impl;
 | 
				
			||||||
	struct message *reply;
 | 
						struct message *reply;
 | 
				
			||||||
 | 
						struct spa_dict_item items[1];
 | 
				
			||||||
 | 
						char latency[32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fix_record_buffer_attr(stream, &stream->attr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						snprintf(latency, sizeof(latency)-1, "%u/%u",
 | 
				
			||||||
 | 
								stream->attr.fragsize / stream->frame_size,
 | 
				
			||||||
 | 
								stream->ss.rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items[0] = SPA_DICT_ITEM_INIT(PW_KEY_NODE_LATENCY, latency);
 | 
				
			||||||
 | 
						pw_stream_update_properties(stream->stream,
 | 
				
			||||||
 | 
								&SPA_DICT_INIT(items, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reply = reply_new(client, stream->create_tag);
 | 
						reply = reply_new(client, stream->create_tag);
 | 
				
			||||||
	message_put(reply,
 | 
						message_put(reply,
 | 
				
			||||||
| 
						 | 
					@ -801,6 +909,10 @@ static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *
 | 
				
			||||||
	pw_log_info(NAME" %p: got rate:%u channels:%u", stream, stream->ss.rate, stream->ss.channels);
 | 
						pw_log_info(NAME" %p: got rate:%u channels:%u", stream, stream->ss.rate, stream->ss.channels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stream->frame_size = sample_spec_frame_size(&stream->ss);
 | 
						stream->frame_size = sample_spec_frame_size(&stream->ss);
 | 
				
			||||||
 | 
						if (stream->frame_size == 0) {
 | 
				
			||||||
 | 
							pw_stream_set_error(stream->stream, res, "format not supported");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (stream->create_tag != SPA_ID_INVALID) {
 | 
						if (stream->create_tag != SPA_ID_INVALID) {
 | 
				
			||||||
		if (stream->volume_set) {
 | 
							if (stream->volume_set) {
 | 
				
			||||||
| 
						 | 
					@ -956,68 +1068,6 @@ static const struct pw_stream_events stream_events =
 | 
				
			||||||
	.drained = stream_drained,
 | 
						.drained = stream_drained,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint32_t usec_to_bytes_round_up(uint64_t usec, const struct sample_spec *ss)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	uint64_t u;
 | 
					 | 
				
			||||||
	u = (uint64_t) usec * (uint64_t) ss->rate;
 | 
					 | 
				
			||||||
	u = (u + 1000000UL - 1) / 1000000UL;
 | 
					 | 
				
			||||||
	u *= sample_spec_frame_size(ss);
 | 
					 | 
				
			||||||
	return (uint32_t) u;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	uint32_t frame_size, max_prebuf, minreq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	frame_size = s->frame_size;
 | 
					 | 
				
			||||||
	if (frame_size == 0)
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH)
 | 
					 | 
				
			||||||
		attr->maxlength = MAXLENGTH;
 | 
					 | 
				
			||||||
	attr->maxlength -= attr->maxlength % frame_size;
 | 
					 | 
				
			||||||
	attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->tlength == (uint32_t) -1)
 | 
					 | 
				
			||||||
		attr->tlength = usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*1000, &s->ss);
 | 
					 | 
				
			||||||
	if (attr->tlength > attr->maxlength)
 | 
					 | 
				
			||||||
		attr->tlength = attr->maxlength;
 | 
					 | 
				
			||||||
	attr->tlength -= attr->tlength % frame_size;
 | 
					 | 
				
			||||||
	attr->tlength = SPA_MAX(attr->tlength, frame_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->minreq == (uint32_t) -1) {
 | 
					 | 
				
			||||||
		uint32_t process = usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*1000, &s->ss);
 | 
					 | 
				
			||||||
		/* With low-latency, tlength/4 gives a decent default in all of traditional,
 | 
					 | 
				
			||||||
		 * adjust latency and early request modes. */
 | 
					 | 
				
			||||||
		uint32_t m = attr->tlength / 4;
 | 
					 | 
				
			||||||
		m -= m % frame_size;
 | 
					 | 
				
			||||||
		attr->minreq = SPA_MIN(process, m);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	minreq = usec_to_bytes_round_up(MIN_USEC, &s->ss);
 | 
					 | 
				
			||||||
	attr->minreq = SPA_MAX(attr->minreq, minreq);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->tlength < attr->minreq+frame_size)
 | 
					 | 
				
			||||||
		attr->tlength = attr->minreq + frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	attr->minreq -= attr->minreq % frame_size;
 | 
					 | 
				
			||||||
	if (attr->minreq <= 0) {
 | 
					 | 
				
			||||||
		attr->minreq = frame_size;
 | 
					 | 
				
			||||||
		attr->tlength += frame_size*2;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (attr->tlength <= attr->minreq)
 | 
					 | 
				
			||||||
		attr->tlength = attr->minreq*2 + frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	max_prebuf = attr->tlength + frame_size - attr->minreq;
 | 
					 | 
				
			||||||
	if (attr->prebuf == (uint32_t) -1 || attr->prebuf > max_prebuf)
 | 
					 | 
				
			||||||
		attr->prebuf = max_prebuf;
 | 
					 | 
				
			||||||
	attr->prebuf -= attr->prebuf % frame_size;
 | 
					 | 
				
			||||||
	attr->prebuf = SPA_MAX(attr->prebuf, frame_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_log_info(NAME" %p: maxlength:%u tlength:%u minreq:%u prebuf:%u", s,
 | 
					 | 
				
			||||||
			attr->maxlength, attr->tlength, attr->minreq, attr->prebuf);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int do_create_playback_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m)
 | 
					static int do_create_playback_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = client->impl;
 | 
						struct impl *impl = client->impl;
 | 
				
			||||||
| 
						 | 
					@ -1048,11 +1098,9 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
 | 
				
			||||||
	struct cvolume volume;
 | 
						struct cvolume volume;
 | 
				
			||||||
	struct pw_properties *props = NULL;
 | 
						struct pw_properties *props = NULL;
 | 
				
			||||||
	uint8_t n_formats = 0;
 | 
						uint8_t n_formats = 0;
 | 
				
			||||||
	struct format_info *formats = NULL;
 | 
					 | 
				
			||||||
	struct stream *stream = NULL;
 | 
						struct stream *stream = NULL;
 | 
				
			||||||
	struct spa_audio_info_raw info;
 | 
						uint32_t n_params = 0, flags;
 | 
				
			||||||
	uint32_t n_params, flags;
 | 
						const struct spa_pod *params[32];
 | 
				
			||||||
	const struct spa_pod *params[1];
 | 
					 | 
				
			||||||
	uint8_t buffer[4096];
 | 
						uint8_t buffer[4096];
 | 
				
			||||||
	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
 | 
						struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
| 
						 | 
					@ -1134,6 +1182,12 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
 | 
				
			||||||
				TAG_INVALID)) < 0)
 | 
									TAG_INVALID)) < 0)
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sample_spec_valid(&ss)) {
 | 
				
			||||||
 | 
							if ((params[n_params] = format_build_param(&b,
 | 
				
			||||||
 | 
									SPA_PARAM_EnumFormat, &ss, &map)) != NULL)
 | 
				
			||||||
 | 
								n_params++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (client->version >= 21) {
 | 
						if (client->version >= 21) {
 | 
				
			||||||
		if ((res = message_get(m,
 | 
							if ((res = message_get(m,
 | 
				
			||||||
				TAG_U8, &n_formats,
 | 
									TAG_U8, &n_formats,
 | 
				
			||||||
| 
						 | 
					@ -1142,12 +1196,16 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (n_formats) {
 | 
							if (n_formats) {
 | 
				
			||||||
			uint8_t i;
 | 
								uint8_t i;
 | 
				
			||||||
			formats = calloc(n_formats, sizeof(struct format_info));
 | 
					 | 
				
			||||||
			for (i = 0; i < n_formats; i++) {
 | 
								for (i = 0; i < n_formats; i++) {
 | 
				
			||||||
 | 
									struct format_info format;
 | 
				
			||||||
				if ((res = message_get(m,
 | 
									if ((res = message_get(m,
 | 
				
			||||||
						TAG_FORMAT_INFO, &formats[i],
 | 
											TAG_FORMAT_INFO, &format,
 | 
				
			||||||
						TAG_INVALID)) < 0)
 | 
											TAG_INVALID)) < 0)
 | 
				
			||||||
					goto error;
 | 
										goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if ((params[n_params] = format_info_build_param(&b,
 | 
				
			||||||
 | 
											SPA_PARAM_EnumFormat, &format)) != NULL)
 | 
				
			||||||
 | 
										n_params++;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1182,17 +1240,8 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
 | 
				
			||||||
	stream->volume_set = volume_set;
 | 
						stream->volume_set = volume_set;
 | 
				
			||||||
	stream->muted = muted;
 | 
						stream->muted = muted;
 | 
				
			||||||
	stream->muted_set = muted_set;
 | 
						stream->muted_set = muted_set;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream->frame_size = sample_spec_frame_size(&stream->ss);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((res = fix_playback_buffer_attr(stream, &attr)) < 0)
 | 
					 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream->attr = attr;
 | 
						stream->attr = attr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u",
 | 
					 | 
				
			||||||
			stream->attr.minreq * 2 / stream->frame_size, ss.rate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((str = pw_properties_get(props, PW_KEY_MEDIA_ROLE)) != NULL) {
 | 
						if ((str = pw_properties_get(props, PW_KEY_MEDIA_ROLE)) != NULL) {
 | 
				
			||||||
		if (strcmp(str, "video") == 0)
 | 
							if (strcmp(str, "video") == 0)
 | 
				
			||||||
			str = "Movie";
 | 
								str = "Movie";
 | 
				
			||||||
| 
						 | 
					@ -1232,13 +1281,6 @@ static int do_create_playback_stream(struct client *client, uint32_t command, ui
 | 
				
			||||||
			&stream->stream_listener,
 | 
								&stream->stream_listener,
 | 
				
			||||||
			&stream_events, stream);
 | 
								&stream_events, stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        info = SPA_AUDIO_INFO_RAW_INIT(
 | 
					 | 
				
			||||||
			.format = format_pa2id(ss.format),
 | 
					 | 
				
			||||||
			.channels = ss.channels,
 | 
					 | 
				
			||||||
			.rate = ss.rate);
 | 
					 | 
				
			||||||
	n_params = 0;
 | 
					 | 
				
			||||||
	params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_stream_connect(stream->stream,
 | 
						pw_stream_connect(stream->stream,
 | 
				
			||||||
			PW_DIRECTION_OUTPUT,
 | 
								PW_DIRECTION_OUTPUT,
 | 
				
			||||||
			SPA_ID_INVALID,
 | 
								SPA_ID_INVALID,
 | 
				
			||||||
| 
						 | 
					@ -1258,35 +1300,6 @@ error:
 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	uint32_t frame_size, minfrag;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	frame_size = s->frame_size;
 | 
					 | 
				
			||||||
	if (frame_size == 0)
 | 
					 | 
				
			||||||
		return -EIO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH)
 | 
					 | 
				
			||||||
		attr->maxlength = MAXLENGTH;
 | 
					 | 
				
			||||||
	attr->maxlength -= attr->maxlength % frame_size;
 | 
					 | 
				
			||||||
	attr->maxlength = SPA_MAX(attr->maxlength, frame_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	minfrag = usec_to_bytes_round_up(MIN_USEC, &s->ss);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0)
 | 
					 | 
				
			||||||
		attr->fragsize = usec_to_bytes_round_up(DEFAULT_FRAGSIZE_MSEC*1000, &s->ss);
 | 
					 | 
				
			||||||
	attr->fragsize -= attr->fragsize % frame_size;
 | 
					 | 
				
			||||||
	attr->fragsize = SPA_MAX(attr->fragsize, minfrag);
 | 
					 | 
				
			||||||
	attr->fragsize = SPA_MAX(attr->fragsize, frame_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (attr->fragsize > attr->maxlength)
 | 
					 | 
				
			||||||
		attr->fragsize = attr->maxlength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_log_info(NAME" %p: maxlength:%u fragsize:%u minfrag:%u", s,
 | 
					 | 
				
			||||||
			attr->maxlength, attr->fragsize, minfrag);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int do_create_record_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m)
 | 
					static int do_create_record_stream(struct client *client, uint32_t command, uint32_t tag, struct message *m)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = client->impl;
 | 
						struct impl *impl = client->impl;
 | 
				
			||||||
| 
						 | 
					@ -1319,11 +1332,9 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
 | 
				
			||||||
	struct cvolume volume;
 | 
						struct cvolume volume;
 | 
				
			||||||
	struct pw_properties *props = NULL;
 | 
						struct pw_properties *props = NULL;
 | 
				
			||||||
	uint8_t n_formats = 0;
 | 
						uint8_t n_formats = 0;
 | 
				
			||||||
	struct format_info *formats = NULL;
 | 
					 | 
				
			||||||
	struct stream *stream = NULL;
 | 
						struct stream *stream = NULL;
 | 
				
			||||||
	struct spa_audio_info_raw info;
 | 
						uint32_t n_params = 0, flags;
 | 
				
			||||||
	uint32_t n_params, flags;
 | 
						const struct spa_pod *params[32];
 | 
				
			||||||
	const struct spa_pod *params[1];
 | 
					 | 
				
			||||||
	uint8_t buffer[4096];
 | 
						uint8_t buffer[4096];
 | 
				
			||||||
	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
 | 
						struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1387,6 +1398,11 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
 | 
				
			||||||
				TAG_INVALID)) < 0)
 | 
									TAG_INVALID)) < 0)
 | 
				
			||||||
			goto error;
 | 
								goto error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (sample_spec_valid(&ss)) {
 | 
				
			||||||
 | 
							if ((params[n_params] = format_build_param(&b,
 | 
				
			||||||
 | 
									SPA_PARAM_EnumFormat, &ss, &map)) != NULL)
 | 
				
			||||||
 | 
								n_params++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (client->version >= 22) {
 | 
						if (client->version >= 22) {
 | 
				
			||||||
		if ((res = message_get(m,
 | 
							if ((res = message_get(m,
 | 
				
			||||||
				TAG_U8, &n_formats,
 | 
									TAG_U8, &n_formats,
 | 
				
			||||||
| 
						 | 
					@ -1395,12 +1411,16 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (n_formats) {
 | 
							if (n_formats) {
 | 
				
			||||||
			uint8_t i;
 | 
								uint8_t i;
 | 
				
			||||||
			formats = calloc(n_formats, sizeof(struct format_info));
 | 
					 | 
				
			||||||
			for (i = 0; i < n_formats; i++) {
 | 
								for (i = 0; i < n_formats; i++) {
 | 
				
			||||||
 | 
									struct format_info format;
 | 
				
			||||||
				if ((res = message_get(m,
 | 
									if ((res = message_get(m,
 | 
				
			||||||
						TAG_FORMAT_INFO, &formats[i],
 | 
											TAG_FORMAT_INFO, &format,
 | 
				
			||||||
						TAG_INVALID)) < 0)
 | 
											TAG_INVALID)) < 0)
 | 
				
			||||||
					goto error;
 | 
										goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if ((params[n_params] = format_info_build_param(&b,
 | 
				
			||||||
 | 
											SPA_PARAM_EnumFormat, &format)) != NULL)
 | 
				
			||||||
 | 
										n_params++;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if ((res = message_get(m,
 | 
							if ((res = message_get(m,
 | 
				
			||||||
| 
						 | 
					@ -1444,17 +1464,8 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
 | 
				
			||||||
	stream->volume_set = volume_set;
 | 
						stream->volume_set = volume_set;
 | 
				
			||||||
	stream->muted = muted;
 | 
						stream->muted = muted;
 | 
				
			||||||
	stream->muted_set = muted_set;
 | 
						stream->muted_set = muted_set;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream->frame_size = sample_spec_frame_size(&stream->ss);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((res = fix_record_buffer_attr(stream, &attr)) < 0)
 | 
					 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream->attr = attr;
 | 
						stream->attr = attr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u",
 | 
					 | 
				
			||||||
			stream->attr.fragsize / stream->frame_size, ss.rate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (peak_detect)
 | 
						if (peak_detect)
 | 
				
			||||||
		pw_properties_set(props, PW_KEY_STREAM_MONITOR, "true");
 | 
							pw_properties_set(props, PW_KEY_STREAM_MONITOR, "true");
 | 
				
			||||||
	flags = 0;
 | 
						flags = 0;
 | 
				
			||||||
| 
						 | 
					@ -1471,14 +1482,6 @@ static int do_create_record_stream(struct client *client, uint32_t command, uint
 | 
				
			||||||
			&stream->stream_listener,
 | 
								&stream->stream_listener,
 | 
				
			||||||
			&stream_events, stream);
 | 
								&stream_events, stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        info = SPA_AUDIO_INFO_RAW_INIT(
 | 
					 | 
				
			||||||
			.format = format_pa2id(ss.format),
 | 
					 | 
				
			||||||
			.channels = ss.channels,
 | 
					 | 
				
			||||||
			.rate = ss.rate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	n_params = 0;
 | 
					 | 
				
			||||||
	params[n_params++] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_stream_connect(stream->stream,
 | 
						pw_stream_connect(stream->stream,
 | 
				
			||||||
			PW_DIRECTION_INPUT,
 | 
								PW_DIRECTION_INPUT,
 | 
				
			||||||
			SPA_ID_INVALID,
 | 
								SPA_ID_INVALID,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue