summaryrefslogtreecommitdiff
path: root/examples/decode_with_partial_drops.txt
blob: 7b0d3d2ca8e1b1f9b9f03f901cfb367c6a830fe5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@TEMPLATE decoder_tmpl.c
Decode With Partial Drops Example
=========================
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTRODUCTION
This is an example utility which drops a series of frames (or parts of frames),
as specified on the command line. This is useful for observing the error
recovery features of the codec.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTRODUCTION

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_INCLUDES
#include <time.h>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_INCLUDES

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS
struct parsed_header
{
    char key_frame;
    int version;
    char show_frame;
    int first_part_size;
};

int next_packet(struct parsed_header* hdr, int pos, int length, int mtu)
{
    int size = 0;
    int remaining = length - pos;
    /* Uncompressed part is 3 bytes for P frames and 10 bytes for I frames */
    int uncomp_part_size = (hdr->key_frame ? 10 : 3);
    /* number of bytes yet to send from header and the first partition */
    int remainFirst = uncomp_part_size + hdr->first_part_size - pos;
    if (remainFirst > 0)
    {
        if (remainFirst <= mtu)
        {
            size = remainFirst;
        }
        else
        {
            size = mtu;
        }

        return size;
    }

    /* second partition; just slot it up according to MTU */
    if (remaining <= mtu)
    {
        size = remaining;
        return size;
    }
    return mtu;
}

void throw_packets(unsigned char* frame, int* size, int loss_rate,
                   int* thrown, int* kept)
{
    unsigned char loss_frame[256*1024];
    int pkg_size = 1;
    int pos = 0;
    int loss_pos = 0;
    struct parsed_header hdr;
    unsigned int tmp;
    int mtu = 1500;

    if (*size < 3)
    {
        return;
    }
    putc('|', stdout);
    /* parse uncompressed 3 bytes */
    tmp = (frame[2] << 16) | (frame[1] << 8) | frame[0];
    hdr.key_frame = !(tmp & 0x1); /* inverse logic */
    hdr.version = (tmp >> 1) & 0x7;
    hdr.show_frame = (tmp >> 4) & 0x1;
    hdr.first_part_size = (tmp >> 5) & 0x7FFFF;

    /* don't drop key frames */
    if (hdr.key_frame)
    {
        int i;
        *kept = *size/mtu + ((*size % mtu > 0) ? 1 : 0); /* approximate */
        for (i=0; i < *kept; i++)
            putc('.', stdout);
        return;
    }

    while ((pkg_size = next_packet(&hdr, pos, *size, mtu)) > 0)
    {
        int loss_event = ((rand() + 1.0)/(RAND_MAX + 1.0) < loss_rate/100.0);
        if (*thrown == 0 && !loss_event)
        {
            memcpy(loss_frame + loss_pos, frame + pos, pkg_size);
            loss_pos += pkg_size;
            (*kept)++;
            putc('.', stdout);
        }
        else
        {
            (*thrown)++;
            putc('X', stdout);
        }
        pos += pkg_size;
    }
    memcpy(frame, loss_frame, loss_pos);
    memset(frame + loss_pos, 0, *size - loss_pos);
    *size = loss_pos;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DEC_INIT
/* Initialize codec */
flags = VPX_CODEC_USE_ERROR_CONCEALMENT;
res = vpx_codec_dec_init(&codec, interface, &dec_cfg, flags);
if(res)
    die_codec(&codec, "Failed to initialize decoder");

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DEC_INIT

Usage
-----
This example adds a single argument to the `simple_decoder` example,
which specifies the range or pattern of frames to drop. The parameter is
parsed as follows:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE
if(argc < 4 || argc > 6)
    die("Usage: %s <infile> <outfile> [-t <num threads>] <N-M|N/M|L,S>\n",
        argv[0]);
{
    char *nptr;
    int arg_num = 3;
    if (argc == 6 && strncmp(argv[arg_num++], "-t", 2) == 0)
        dec_cfg.threads = strtol(argv[arg_num++], NULL, 0);
    n = strtol(argv[arg_num], &nptr, 0);
    mode = (*nptr == '\0' || *nptr == ',') ? 2 : (*nptr == '-') ? 1 : 0;

    m = strtol(nptr+1, NULL, 0);
    if((!n && !m) || (*nptr != '-' && *nptr != '/' &&
        *nptr != '\0' && *nptr != ','))
        die("Couldn't parse pattern %s\n", argv[3]);
}
seed = (m > 0) ? m : (unsigned int)time(NULL);
srand(seed);thrown_frame = 0;
printf("Seed: %u\n", seed);
printf("Threads: %d\n", dec_cfg.threads);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USAGE


Dropping A Range Of Frames
--------------------------
To drop a range of frames, specify the starting frame and the ending
frame to drop, separated by a dash. The following command will drop
frames 5 through 10 (base 1).

  $ ./decode_with_partial_drops in.ivf out.i420 5-10


Dropping A Pattern Of Frames
----------------------------
To drop a pattern of frames, specify the number of frames to drop and
the number of frames after which to repeat the pattern, separated by
a forward-slash. The following command will drop 3 of 7 frames.
Specifically, it will decode 4 frames, then drop 3 frames, and then
repeat.

  $ ./decode_with_partial_drops in.ivf out.i420 3/7

Dropping Random Parts Of Frames
-------------------------------
A third argument tuple is available to split the frame into 1500 bytes pieces
and randomly drop pieces rather than frames. The frame will be split at
partition boundaries where possible. The following example will seed the RNG
with the seed 123 and drop approximately 5% of the pieces. Pieces which
are depending on an already dropped piece will also be dropped.

  $ ./decode_with_partial_drops in.ivf out.i420 5,123


Extra Variables
---------------
This example maintains the pattern passed on the command line in the
`n`, `m`, and `is_range` variables:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_VARS
int              n, m, mode;
unsigned int     seed;
int              thrown=0, kept=0;
int              thrown_frame=0, kept_frame=0;
vpx_codec_dec_cfg_t  dec_cfg = {0};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EXTRA_VARS


Making The Drop Decision
------------------------
The example decides whether to drop the frame based on the current
frame number, immediately before decoding the frame.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PRE_DECODE
/* Decide whether to throw parts of the frame or the whole frame
   depending on the drop mode */
thrown_frame = 0;
kept_frame = 0;
switch (mode)
{
case 0:
    if (m - (frame_cnt-1)%m <= n)
    {
        frame_sz = 0;
    }
    break;
case 1:
    if (frame_cnt >= n && frame_cnt <= m)
    {
        frame_sz = 0;
    }
    break;
case 2:
    throw_packets(frame, &frame_sz, n, &thrown_frame, &kept_frame);
    break;
default: break;
}
if (mode < 2)
{
    if (frame_sz == 0)
    {
        putc('X', stdout);
        thrown_frame++;
    }
    else
    {
        putc('.', stdout);
        kept_frame++;
    }
}
thrown += thrown_frame;
kept += kept_frame;
fflush(stdout);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PRE_DECODE