1 /* Copyright 2019 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <errno.h>
7 #include <getopt.h>
8 #include <math.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/param.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 
19 #include "cras_sbc_codec.h"
20 #include "cras_plc.h"
21 
22 #define MSBC_CODE_SIZE 240
23 #define MSBC_PKT_FRAME_LEN 57
24 #define RND_SEED 7
25 
26 static const uint8_t msbc_zero_frame[] = {
27 	0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd,
28 	0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6,
29 	0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d,
30 	0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
31 	0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c
32 };
33 
generate_pl_seq(int input_file_size,float pl_percent)34 bool *generate_pl_seq(int input_file_size, float pl_percent)
35 {
36 	unsigned pk_count, pl_count;
37 	bool *seq;
38 
39 	pk_count = input_file_size / MSBC_CODE_SIZE;
40 	pl_count = pk_count * (pl_percent / 100.0);
41 	seq = (bool *)calloc(pk_count, sizeof(*seq));
42 	srand(RND_SEED);
43 	while (pl_count > 0) {
44 		bool *missed = &seq[rand() % pk_count];
45 		if (!*missed) {
46 			*missed = true;
47 			pl_count--;
48 		}
49 	}
50 	return seq;
51 }
52 
53 /* pl_hex is expected to be consecutive bytes(two chars) in hex format.*/
parse_pl_hex(int input_file_size,const char * pl_hex)54 bool *parse_pl_hex(int input_file_size, const char *pl_hex)
55 {
56 	char tmp[3];
57 	uint8_t val = 0;
58 	int i, pl_hex_len, seq_len;
59 	bool *seq;
60 
61 	pl_hex_len = strlen(pl_hex);
62 	seq_len = MAX(1 + input_file_size / MSBC_CODE_SIZE, pl_hex_len * 4);
63 	seq = (bool *)calloc(seq_len, sizeof(*seq));
64 
65 	for (i = 0; i < seq_len; i++) {
66 		/* If sequence is longer then the provided pl_hex, leave the
67 		 * rest to all zeros. */
68 		if (i > pl_hex_len * 4)
69 			break;
70 		if (i % 8 == 0) {
71 			memcpy(tmp, pl_hex + i / 4, 2);
72 			tmp[2] = '\0';
73 			val = strtol(tmp, NULL, 16);
74 		}
75 		seq[i] = val & 1U;
76 		val >>= 1;
77 	}
78 	printf("pl_hex string maps to %ld ms, total sequence size %f ms\n",
79 	       strlen(pl_hex) * 30, seq_len * 7.5f);
80 	return seq;
81 }
82 
plc_experiment(const char * input_filename,bool * pl_seq,bool with_plc)83 void plc_experiment(const char *input_filename, bool *pl_seq, bool with_plc)
84 {
85 	char output_filename[255];
86 	int input_fd, output_fd, rc;
87 	struct cras_audio_codec *msbc_input = cras_msbc_codec_create();
88 	struct cras_audio_codec *msbc_output = cras_msbc_codec_create();
89 	struct cras_msbc_plc *plc = cras_msbc_plc_create();
90 	uint8_t buffer[MSBC_CODE_SIZE], packet_buffer[MSBC_PKT_FRAME_LEN];
91 	size_t encoded, decoded;
92 	unsigned count = 0;
93 
94 	input_fd = open(input_filename, O_RDONLY);
95 	if (input_fd == -1) {
96 		fprintf(stderr, "Cannout open input file %s\n", input_filename);
97 		return;
98 	}
99 
100 	if (with_plc)
101 		sprintf(output_filename, "output_with_plc.raw");
102 	else
103 		sprintf(output_filename, "output_with_zero.raw");
104 
105 	output_fd = open(output_filename, O_CREAT | O_RDWR | O_TRUNC, 0644);
106 	if (output_fd == -1) {
107 		fprintf(stderr, "Cannot open output file %s\n",
108 			output_filename);
109 		return;
110 	}
111 
112 	while (1) {
113 		rc = read(input_fd, buffer, MSBC_CODE_SIZE);
114 		if (rc < 0) {
115 			fprintf(stderr, "Cannot read file %s", input_filename);
116 			return;
117 		} else if (rc == 0 || rc < MSBC_CODE_SIZE)
118 			break;
119 
120 		msbc_input->encode(msbc_input, buffer, MSBC_CODE_SIZE,
121 				   packet_buffer, MSBC_PKT_FRAME_LEN, &encoded);
122 
123 		if (pl_seq[count]) {
124 			if (with_plc) {
125 				cras_msbc_plc_handle_bad_frames(
126 					plc, msbc_output, buffer);
127 				decoded = MSBC_CODE_SIZE;
128 			} else
129 				msbc_output->decode(msbc_output,
130 						    msbc_zero_frame,
131 						    MSBC_PKT_FRAME_LEN, buffer,
132 						    MSBC_CODE_SIZE, &decoded);
133 		} else {
134 			msbc_output->decode(msbc_output, packet_buffer,
135 					    MSBC_PKT_FRAME_LEN, buffer,
136 					    MSBC_CODE_SIZE, &decoded);
137 			cras_msbc_plc_handle_good_frames(plc, buffer, buffer);
138 		}
139 
140 		count++;
141 		rc = write(output_fd, buffer, decoded);
142 		if (rc < 0) {
143 			fprintf(stderr, "Cannot write file %s\n",
144 				output_filename);
145 			return;
146 		}
147 	}
148 }
149 
show_usage()150 static void show_usage()
151 {
152 	printf("This test only supports reading/writing raw audio with format:\n"
153 	       "\t16000 sample rate, mono channel, S16_LE\n");
154 	printf("--help - Print this usage.\n");
155 	printf("--input_file - path to an audio file.\n");
156 	printf("--pattern - Hex string representing consecutive packets'"
157 	       "status.\n");
158 	printf("--random - Percentage of packet loss.\n");
159 }
160 
main(int argc,char ** argv)161 int main(int argc, char **argv)
162 {
163 	int fd;
164 	struct stat st;
165 	float pl_percent;
166 	int pl_percent_set = 0;
167 	int option_character;
168 	int option_index = 0;
169 	const char *input_file = NULL;
170 	const char *pl_hex = NULL;
171 	bool *pl_seq = NULL;
172 	static struct option long_options[] = {
173 		{ "help", no_argument, NULL, 'h' },
174 		{ "input", required_argument, NULL, 'i' },
175 		{ "pattern", required_argument, NULL, 'p' },
176 		{ "random", required_argument, NULL, 'r' },
177 		{ NULL, 0, NULL, 0 },
178 	};
179 
180 	while (true) {
181 		option_character = getopt_long(argc, argv, "i:r:p:h",
182 					       long_options, &option_index);
183 		if (option_character == -1)
184 			break;
185 		switch (option_character) {
186 		case 'h':
187 			show_usage();
188 			break;
189 		case 'i':
190 			input_file = optarg;
191 			break;
192 		case 'p':
193 			pl_hex = optarg;
194 			break;
195 		case 'r':
196 			pl_percent = atof(optarg);
197 			pl_percent_set = 1;
198 			break;
199 		default:
200 			break;
201 		}
202 	}
203 
204 	if ((!pl_percent_set && !pl_hex) || !input_file) {
205 		show_usage();
206 		return 1;
207 	}
208 
209 	fd = open(input_file, O_RDONLY);
210 	if (fd == -1) {
211 		fprintf(stderr, "Cannout open input file %s\n", input_file);
212 		return 1;
213 	}
214 	fstat(fd, &st);
215 	close(fd);
216 	if (pl_percent_set)
217 		pl_seq = generate_pl_seq(st.st_size, pl_percent);
218 	else if (pl_hex)
219 		pl_seq = parse_pl_hex(st.st_size, pl_hex);
220 
221 	plc_experiment(input_file, pl_seq, true);
222 	plc_experiment(input_file, pl_seq, false);
223 }
224