| /* |
| * Copyright 2016 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* Note: though this specific file is licensed under the Apache license, |
| * it exists in order to interface with the RAS_LIB.c implementation |
| * provided by TI which is not licensed under Apache. */ |
| |
| #include <arpa/inet.h> |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/un.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include "rcu-audio.h" |
| #include "remote_control_audio.pb.h" |
| #include "RAS_lib.h" |
| |
| #define TI_AUDIO_PATH "\0rc_audio_ti" |
| |
| int main(int argc, char **argv) |
| { |
| int is = -1, os = -1, connected = 0; |
| struct sockaddr_un sun; |
| struct sockaddr_in sin; |
| uint8 prev = 0; |
| int msgs = 0, missed = 0, errors = 0; |
| |
| if ((is = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { |
| perror("socket(AF_UNIX)"); |
| exit(1); |
| } |
| |
| memset(&sun, 0, sizeof(sun)); |
| sun.sun_family = AF_UNIX; |
| snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", TI_AUDIO_PATH); |
| if (bind(is, (const struct sockaddr *) &sun, sizeof(sun)) < 0) { |
| perror("bind(AF_UNIX)"); |
| exit(1); |
| } |
| |
| memset(&sin, 0, sizeof(sin)); |
| sin.sin_family = AF_INET; |
| sin.sin_port = htons(RCU_AUDIO_PORT); |
| sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| |
| while (1) { |
| uint8 ibuf[MAX_INPUT_BUF_SIZE + 6 + 1 + 4]; |
| ssize_t ilen; |
| |
| ilen = recv(is, ibuf, sizeof(ibuf), 0); |
| if (ilen < 23) { |
| /* end of an audio file, prepare for the next one */ |
| printf("Finished audio stream; msgs = %d, missed = %d, errors = %d\n", |
| msgs, missed, errors); |
| msgs = missed = errors = 0; |
| RAS_Init(RAS_NO_PEC); |
| } else { |
| uint8_t remote_type = ibuf[6]; |
| uint8 expected = (prev + 1) & 0x1f; |
| uint8 seqnum = (ibuf[7] >> 3) & 0x1f; |
| uint8 *data; |
| uint16 data_len; |
| int16 obuf[4 * MAX_INPUT_BUF_SIZE]; |
| uint16 olen; |
| |
| if (seqnum != expected) { |
| missed++; |
| } |
| prev = seqnum; |
| msgs++; |
| |
| /* |
| * We skip over: |
| * the first 6 bytes, which is the BDADDR of the remote. |
| * the remote type byte (0 == GFRM210) |
| * the sequence number byte |
| * |
| * The first three bytes of payload are some kind of RAS header. |
| * RAS_Decode() says the data pointer must include the three bytes |
| * of header, but that the length must not include those 3 bytes. |
| * |
| * HOWEVER, the documentation is a filthy liar. RAS_Decode decrements |
| * its inputLength by three to account for the header length. |
| * If we obey the instructions and omit those 3 bytes from the |
| * length, the audio quality is noticeably worse because it starts |
| * throwing away three bytes of audio data. |
| * |
| * So both the pointer and the length passed to RAS_Decode *include* |
| * the header bytes. |
| */ |
| data = &ibuf[6 + 1 + 1]; |
| data_len = ilen - (6 + 1 + 1); |
| if (RAS_Decode(RAS_DECODE_TI_TYPE1, data, data_len, obuf, &olen) == 0) { |
| rcaudio::AudioSamples samples; |
| char bdaddr[18]; |
| std::vector<uint8_t> pkt; |
| |
| snprintf(bdaddr, sizeof(bdaddr), |
| "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", |
| ibuf[0], ibuf[1], ibuf[2], ibuf[3], ibuf[4], ibuf[5]); |
| |
| samples.set_rc_address(bdaddr); |
| if (remote_type == 0) { |
| samples.set_audio_format(rcaudio::AudioSamples::PCM_16BIT_16KHZ); |
| samples.set_remote_type(rcaudio::AudioSamples::GFRM210); |
| } else { |
| samples.set_audio_format(rcaudio::AudioSamples::UNDEFINED_AUDIO_FORMAT); |
| samples.set_remote_type(rcaudio::AudioSamples::UNDEFINED_REMOTE_TYPE); |
| } |
| samples.set_audio_samples(obuf, olen); |
| |
| pkt.resize(samples.ByteSize()); |
| samples.SerializeToArray(&pkt[0], samples.ByteSize()); |
| |
| if (os < 0) { |
| os = get_socket_or_die(); |
| } |
| |
| if (!connected) { |
| if (connect(os, (const struct sockaddr *) &sin, sizeof(sin)) == 0) { |
| connected = 1; |
| } else { |
| sleep(2); /* rate limit how often we try */ |
| } |
| } |
| |
| if (connected) { |
| if (send(os, &pkt[0], pkt.size(), 0) != (ssize_t)pkt.size()) { |
| fprintf(stderr, "Audio send failed, will reconnect.\n"); |
| connected = 0; |
| close(os); |
| os = -1; |
| } |
| } |
| } else { |
| if (pacing()) { |
| printf("RAS_Decode(RAS_DECODE_TI_TYPE1) failed\n"); |
| } |
| errors++; |
| } |
| } |
| } |
| } |