d04f852368
The error-concealer is plugged in after any motion vectors have been decoded. It tries to estimate any missing motion vectors from the motion vectors of the previous frame. Intra blocks with missing residual are replaced with inter blocks with estimated motion vectors. This feature was developed in a separate sandbox (sandbox/holmer/error-concealment). Change-Id: I5c8917b031078d79dbafd90f6006680e84a23412
233 lines
6.6 KiB
Plaintext
233 lines
6.6 KiB
Plaintext
@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, NULL, 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 != 5)
|
|
die("Usage: %s <infile> <outfile> <N-M|N/M|L,S>\n", argv[0]);
|
|
{
|
|
char *nptr;
|
|
n = strtol(argv[3], &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);
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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;
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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
|