f83a2b2058c2c95d49791e9c7bf22ec7ac9db838
[opus-tools.git] / src / diag_range.c
1 /* Copyright (C)2012 Xiph.Org Foundation
2    Copyright (C)2012 Gregory Maxwell
3    Copyright (C)2012 Jean-Marc Valin
4    File: diag_range.c
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9
10    - Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12
13    - Redistributions in binary form must reproduce the above copyright
14    notice, this list of conditions and the following disclaimer in the
15    documentation and/or other materials provided with the distribution.
16
17    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
21    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdio.h>
35 #include <opus.h>
36 #include "diag_range.h"
37
38 /*This is some non-exported code copied wholesale from libopus.
39  *Normal programs shouldn't need these functions, but we use them here
40  *to parse deep inside multichannel packets in order to get diagnostic
41  *data for save-range. If you're thinking about copying it and you aren't
42  *making an opus stream diagnostic tool, you're probably doing something
43  *wrong.*/
44 static int parse_size(const unsigned char *data, opus_int32 len, opus_int16 *size)
45 {
46    if (len<1)
47    {
48       *size = -1;
49       return -1;
50    } else if (data[0]<252)
51    {
52       *size = data[0];
53       return 1;
54    } else if (len<2)
55    {
56       *size = -1;
57       return -1;
58    } else {
59       *size = 4*data[1] + data[0];
60       return 2;
61    }
62 }
63
64 static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
65       int self_delimited, opus_int16 size[48], int *payload_offset,
66       opus_int32 *packet_offset)
67 {
68    int i, bytes;
69    int count;
70    int cbr;
71    unsigned char ch, toc;
72    int framesize;
73    opus_int32 last_size;
74    opus_int32 pad = 0;
75    const unsigned char *data0 = data;
76
77    if (size==NULL || len<0)
78       return OPUS_BAD_ARG;
79    if (len==0)
80       return OPUS_INVALID_PACKET;
81
82    framesize = opus_packet_get_samples_per_frame(data, 48000);
83
84    cbr = 0;
85    toc = *data++;
86    len--;
87    last_size = len;
88    switch (toc&0x3)
89    {
90    /* One frame */
91    case 0:
92       count=1;
93       break;
94    /* Two CBR frames */
95    case 1:
96       count=2;
97       cbr = 1;
98       if (!self_delimited)
99       {
100          if (len&0x1)
101             return OPUS_INVALID_PACKET;
102          last_size = len/2;
103          /* If last_size doesn't fit in size[0], we'll catch it later */
104          size[0] = (opus_int16)last_size;
105       }
106       break;
107    /* Two VBR frames */
108    case 2:
109       count = 2;
110       bytes = parse_size(data, len, size);
111       len -= bytes;
112       if (size[0]<0 || size[0] > len)
113          return OPUS_INVALID_PACKET;
114       data += bytes;
115       last_size = len-size[0];
116       break;
117    /* Multiple CBR/VBR frames (from 0 to 120 ms) */
118    default: /*case 3:*/
119       if (len<1)
120          return OPUS_INVALID_PACKET;
121       /* Number of frames encoded in bits 0 to 5 */
122       ch = *data++;
123       count = ch&0x3F;
124       if (count <= 0 || framesize*count > 5760)
125          return OPUS_INVALID_PACKET;
126       len--;
127       /* Padding flag is bit 6 */
128       if (ch&0x40)
129       {
130          int p;
131          do {
132             int tmp;
133             if (len<=0)
134                return OPUS_INVALID_PACKET;
135             p = *data++;
136             len--;
137             tmp = p==255 ? 254: p;
138             len -= tmp;
139             pad += tmp;
140          } while (p==255);
141       }
142       if (len<0)
143          return OPUS_INVALID_PACKET;
144       /* VBR flag is bit 7 */
145       cbr = !(ch&0x80);
146       if (!cbr)
147       {
148          /* VBR case */
149          last_size = len;
150          for (i=0;i<count-1;i++)
151          {
152             bytes = parse_size(data, len, size+i);
153             len -= bytes;
154             if (size[i]<0 || size[i] > len)
155                return OPUS_INVALID_PACKET;
156             data += bytes;
157             last_size -= bytes+size[i];
158          }
159          if (last_size<0)
160             return OPUS_INVALID_PACKET;
161       } else if (!self_delimited)
162       {
163          /* CBR case */
164          last_size = len/count;
165          if (last_size*count!=len)
166             return OPUS_INVALID_PACKET;
167          for (i=0;i<count-1;i++)
168             size[i] = (opus_int16)last_size;
169       }
170       break;
171    }
172    /* Self-delimited framing has an extra size for the last frame. */
173    if (self_delimited)
174    {
175       bytes = parse_size(data, len, size+count-1);
176       len -= bytes;
177       if (size[count-1]<0 || size[count-1] > len)
178          return OPUS_INVALID_PACKET;
179       data += bytes;
180       /* For CBR packets, apply the size to all the frames. */
181       if (cbr)
182       {
183          if (size[count-1]*count > len)
184             return OPUS_INVALID_PACKET;
185          for (i=0;i<count-1;i++)
186             size[i] = size[count-1];
187       } else if (bytes+size[count-1] > last_size)
188          return OPUS_INVALID_PACKET;
189    } else
190    {
191       /* Because it's not encoded explicitly, it's possible the size of the
192          last packet (or all the packets, for the CBR case) is larger than
193          1275. Reject them here.*/
194       if (last_size > 1275)
195          return OPUS_INVALID_PACKET;
196       size[count-1] = (opus_int16)last_size;
197    }
198
199    if (payload_offset)
200       *payload_offset = (int)(data-data0);
201
202    for (i=0;i<count;i++)
203       data += size[i];
204
205    if (packet_offset)
206       *packet_offset = pad+(opus_int32)(data-data0);
207
208    return count;
209 }
210
211 void save_range(FILE *frange, int frame_size, const unsigned char *packet, opus_int32 nbBytes, opus_uint32 *rngs, int nb_streams)
212 {
213   int i;
214   opus_int32 parsed_size;
215   const unsigned char *subpkt;
216   static const char *bw_strings[5]={"NB","MB","WB","SWB","FB"};
217   static const char *mode_strings[3]={"LP","HYB","MDCT"};
218   fprintf(frange,"%d, %ld, ",frame_size,(long)nbBytes);
219   subpkt=packet;
220   parsed_size=nbBytes;
221   for (i=0;i<nb_streams;i++) {
222     int j,payload_offset,nf;
223     opus_int32 packet_offset;
224     opus_int16 size[48];
225     payload_offset=0;
226     packet_offset=0;
227     nf=opus_packet_parse_impl(subpkt,parsed_size,i+1!=nb_streams,
228       size,&payload_offset,&packet_offset);
229     fprintf(frange,"[[%d",payload_offset);
230     for(j=0;j<nf;j++)fprintf(frange,", %d",(int)size[j]);
231     fprintf(frange,"], %s, %s, %c, %d",
232        mode_strings[((((subpkt[0]>>3)+48)&92)+4)>>5],
233        bw_strings[opus_packet_get_bandwidth(subpkt)-OPUS_BANDWIDTH_NARROWBAND],
234        subpkt[0]&4?'S':'M',opus_packet_get_samples_per_frame(subpkt,48000));
235     fprintf(frange,", %lu]%s",(unsigned long)rngs[i],i+1==nb_streams?"\n":", ");
236     parsed_size-=packet_offset;
237     subpkt+=packet_offset;
238   }
239 }