DigestPlay.c 6.17 KB
Newer Older
Artem Prilutskiy's avatar
Artem Prilutskiy committed
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/timerfd.h>

#include "Version.h"
#include "RewindClient.h"

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
13
#define TDMA_FRAME_DURATION   60
Artem Prilutskiy's avatar
Artem Prilutskiy committed
14
15
16
17
18

#define DSD_MAGIC_TEXT        ".amb"
#define DSD_MAGIC_SIZE        4
#define DSD_AMBE_CHUNK_SIZE   8

Artem Prilutskiy's avatar
Artem Prilutskiy committed
19
20
#define LINEAR_FRAME_SIZE     7

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
21
22
#define HELPER(value)         #value
#define STRING(value)         HELPER(value)
Artem Prilutskiy's avatar
Artem Prilutskiy committed
23

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
24
#define COUNT(array)          sizeof(array) / sizeof(array[0])
Artem Prilutskiy's avatar
Artem Prilutskiy committed
25

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
26
27
28
29
#define BUFFER_SIZE           1024
#define ATTEMPT_COUNT         5

#define CLIENT_NAME           "DigestPlay " STRING(VERSION) " " BUILD
Artem Prilutskiy's avatar
Artem Prilutskiy committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

int main(int argc, char* argv[])
{
  printf("\n");
  printf("DigestPlay for BrandMeister DMR Master Server\n");
  printf("Copyright 2017 Artem Prilutskiy (R3ABM, cyanide.burnout@gmail.com)\n");
  printf("Software revision " STRING(VERSION) " build " BUILD "\n");
  printf("\n");

  // Main variables

  uint32_t number = 0;
  const char* port = "54005";
  const char* location = NULL;
  const char* password = NULL;

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
46
47
  struct RewindSuperHeader header;
  memset(&header, 0, sizeof(struct RewindSuperHeader));
Artem Prilutskiy's avatar
Artem Prilutskiy committed
48
49
50
51
52
53
54
55
56

  // Start up

  struct option options[] =
  {
    { "client-password",  required_argument, NULL, 'w' },
    { "client-number",    required_argument, NULL, 'c' },
    { "server-address",   required_argument, NULL, 's' },
    { "server-port",      required_argument, NULL, 'p' },
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
57
    { "source-id",        required_argument, NULL, 'u' },
Artem Prilutskiy's avatar
Artem Prilutskiy committed
58
    { "group-id",         required_argument, NULL, 'g' },
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
59
    { "talker-alias",     required_argument, NULL, 't' },
Artem Prilutskiy's avatar
Artem Prilutskiy committed
60
61
62
63
64
65
66
    { NULL,               0,                 NULL, 0   }
  };

  int value = 0;
  int control = 0;
  int selection = 0;

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
67
  while ((selection = getopt_long(argc, argv, "w:c:s:p:u:g:t:", options, NULL)) != EOF)
Artem Prilutskiy's avatar
Artem Prilutskiy committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    switch (selection)
    {
      case 'w':
        password = optarg;
        control |= 0b00001;
        break;

      case 's':
        location = optarg;
        control |= 0b00010;
        break;

      case 'p':
        port = optarg;
        break;

      case 'c':
        number = strtol(optarg, NULL, 10);
        control |= 0b00100;
        break;

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
89
      case 'u':
Artem Prilutskiy's avatar
Artem Prilutskiy committed
90
91
92
        value = strtol(optarg, NULL, 10);
        if (value > 0)
        {
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
93
          header.sourceID = htole32(value);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
94
95
96
97
98
99
100
101
          control |= 0b01000;
        }
        break;

      case 'g':
        value = strtol(optarg, NULL, 10);
        if (value > 0)
        {
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
102
          header.destinationID = htole32(value);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
103
104
105
          control |= 0b10000;
        }
        break;
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
106
107
108
109

      case 't':
        strncpy(header.sourceCall, optarg, REWIND_CALL_LENGTH);
        break;
Artem Prilutskiy's avatar
Artem Prilutskiy committed
110
111
112
113
114
115
116
117
118
119
120
121
122
    }

  if (control != 0b11111)
  {
    printf(
      "Usage:\n"
      "  %s\n"
      "    --client-number <Registered ID of client>\n"
      "    --client-password <access password for BrandMeister DMR Server>\n"
      "    --server-address <domain name of BrandMeister DMR Server>\n"
      "    --server-port <service port for BrandMeister DMR Server>\n"
      "    --source-id <ID to use as a source>\n"
      "    --group-id <TG ID>\n"
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
123
      "    --talker-alias <text to send as Talker Alias>\n"
Artem Prilutskiy's avatar
Artem Prilutskiy committed
124
125
126
127
128
129
130
      "\n",
      argv[0]);
    return EXIT_FAILURE;
  }

  // Create Rewind client context

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
131
  struct RewindContext* context = CreateRewindContext(number, CLIENT_NAME);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

  if (context == NULL)
  {
    printf("Error creating context\n");
    return EXIT_FAILURE;
  }

  // Check input data format

  char* buffer = (char*)alloca(BUFFER_SIZE);
  ssize_t length = read(STDIN_FILENO, buffer, DSD_MAGIC_SIZE);

  if ((length != DSD_MAGIC_SIZE) ||
      (memcmp(buffer, DSD_MAGIC_TEXT, length) != 0))
  {
    printf("Error checking input data format\n");
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
148
    ReleaseRewindContext(context);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
149
150
151
152
153
    return EXIT_FAILURE;
  }

  // Connect to the server

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
154
  int result = ConnectRewindClient(context, location, port, password, REWIND_OPTION_LINEAR_FRAME);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
155
156
157
158

  if (result < 0)
  {
    printf("Cannot connect to the server (%i)\n", result);
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
159
    ReleaseRewindContext(context);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    return EXIT_FAILURE;
  }

  // Initialize timer handle

  int handle;
  struct itimerspec interval;

  memset(&interval, 0, sizeof(struct itimerspec));

  interval.it_interval.tv_sec = 0;
  interval.it_interval.tv_nsec = TDMA_FRAME_DURATION * 1000000;

  handle = timerfd_create(CLOCK_MONOTONIC, 0);
  timerfd_settime(handle, 0, &interval, NULL);

  // Transmit voice header

  printf("Playing...\n");

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
180
181
182
183
  header.type = htole32(SESSION_TYPE_GROUP_VOICE);
  TransmitRewindData(context, REWIND_TYPE_SUPER_HEADER, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct RewindSuperHeader));
  TransmitRewindData(context, REWIND_TYPE_SUPER_HEADER, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct RewindSuperHeader));
  TransmitRewindData(context, REWIND_TYPE_SUPER_HEADER, REWIND_FLAG_REAL_TIME_1, &header, sizeof(struct RewindSuperHeader));
Artem Prilutskiy's avatar
Artem Prilutskiy committed
184
185
186
187
188
189
 
  // Main loop

  uint64_t mark;
  size_t count = 0;

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
190
191
192
  uint8_t* pointer;
  uint8_t* limit = buffer + 3 * DSD_AMBE_CHUNK_SIZE;

Artem Prilutskiy's avatar
Artem Prilutskiy committed
193
194
195
  // Wait for timer event (60 milliseconds)
  while (read(handle, &mark, sizeof(uint64_t)) > 0)
  {
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
196
    // Read AMBE chunks in DSD format
Artem Prilutskiy's avatar
Artem Prilutskiy committed
197
 
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
198
199
200
201
202
203
    pointer = buffer;
    while ((pointer < limit) &&
           (read(STDIN_FILENO, pointer, DSD_AMBE_CHUNK_SIZE) == DSD_AMBE_CHUNK_SIZE))
    {
      pointer += DSD_AMBE_CHUNK_SIZE;
    }
Artem Prilutskiy's avatar
Artem Prilutskiy committed
204

Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
205
    if (pointer < limit)
Artem Prilutskiy's avatar
Artem Prilutskiy committed
206
207
208
209
210
    {
      printf("Input data stream stopped\n");
      break;
    }

Artem Prilutskiy's avatar
Artem Prilutskiy committed
211
212
    // Convert to linear format

Artem Prilutskiy's avatar
Artem Prilutskiy committed
213
214
215
216
    buffer[0 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;
    buffer[1 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;
    buffer[2 * DSD_AMBE_CHUNK_SIZE + 7] <<= 7;

Artem Prilutskiy's avatar
Artem Prilutskiy committed
217
218
219
220
221
222
223
    memmove(buffer + 0 * LINEAR_FRAME_SIZE, buffer + 0 * DSD_AMBE_CHUNK_SIZE + 1, LINEAR_FRAME_SIZE);
    memmove(buffer + 1 * LINEAR_FRAME_SIZE, buffer + 1 * DSD_AMBE_CHUNK_SIZE + 1, LINEAR_FRAME_SIZE);
    memmove(buffer + 2 * LINEAR_FRAME_SIZE, buffer + 2 * DSD_AMBE_CHUNK_SIZE + 1, LINEAR_FRAME_SIZE);

    // Transmit DMR linear frame

    TransmitRewindData(context, REWIND_TYPE_DMR_AUDIO_FRAME, REWIND_FLAG_REAL_TIME_1, buffer, 3 * LINEAR_FRAME_SIZE);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

    if ((count % 83) == 0)
    {
      // Every 5 seconds of transmission
      TransmitRewindKeepAlive(context);
    }

    count ++;
  }

  // Clean up

  printf("Done\n");

  TransmitRewindCloae(context);
Artem Prilutskiy's avatar
..    
Artem Prilutskiy committed
239
  ReleaseRewindContext(context);
Artem Prilutskiy's avatar
Artem Prilutskiy committed
240
241
242

  return EXIT_SUCCESS;
};