Browse code

Fix incorrectly constructed Dirac parse units that caused A/V sync loss. Fixes issue 694. patch by Anuradha Suraparaju, anuradha rd.bbc.co uk

Originally committed as revision 16261 to svn://svn.ffmpeg.org/ffmpeg/trunk

Anuradha Suraparaju authored on 2008/12/22 09:01:39
Showing 3 changed files
... ...
@@ -1,7 +1,8 @@
1 1
 /*
2 2
  * Dirac parser
3 3
  *
4
- * Copyright (c) 2007 Marco Gerards <marco@gnu.org>
4
+ * Copyright (c) 2007-2008 Marco Gerards <marco@gnu.org>
5
+ * Copyright (c) 2008 BBC, Anuradha Suraparaju <asuraparaju@gmail.com>
5 6
  *
6 7
  * This file is part of FFmpeg.
7 8
  *
... ...
@@ -34,42 +35,200 @@
34 34
  * Finds the end of the current frame in the bitstream.
35 35
  * @return the position of the first byte of the next frame or -1
36 36
  */
37
-static int find_frame_end(ParseContext *pc, const uint8_t *buf, int buf_size)
37
+typedef struct DiracParseContext {
38
+    int state;
39
+    int is_synced;
40
+    int sync_offset;
41
+    int header_bytes_needed;
42
+    int overread_index;
43
+    int buffer_size;
44
+    int index;
45
+    uint8_t *buffer;
46
+    int dirac_unit_size;
47
+    uint8_t *dirac_unit;
48
+} DiracParseContext;
49
+
50
+static int find_frame_end(DiracParseContext *pc,
51
+                          const uint8_t *buf, int buf_size)
38 52
 {
39 53
     uint32_t state = pc->state;
40
-    int i;
41
-
42
-    for (i = 0; i < buf_size; i++) {
43
-        state = (state << 8) | buf[i];
44
-        if (state == DIRAC_PARSE_INFO_PREFIX) {
45
-            pc->frame_start_found ^= 1;
46
-            if (!pc->frame_start_found) {
47
-                pc->state = -1;
48
-                return i - 3;
54
+    int i = 0;
55
+
56
+    if (!pc->is_synced) {
57
+        for (i = 0; i < buf_size; i++) {
58
+            state = (state << 8) | buf[i];
59
+            if (state == DIRAC_PARSE_INFO_PREFIX) {
60
+                state                   = -1;
61
+                pc->is_synced           = 1;
62
+                pc->header_bytes_needed = 9;
63
+                pc->sync_offset         = i;
64
+                break;
49 65
             }
50 66
         }
51 67
     }
52 68
 
69
+    if (pc->is_synced) {
70
+        pc->sync_offset = 0;
71
+        for (; i < buf_size; i++) {
72
+            if (state == DIRAC_PARSE_INFO_PREFIX) {
73
+                if ((buf_size-i) >= pc->header_bytes_needed) {
74
+                    pc->state = -1;
75
+                    return i + pc->header_bytes_needed;
76
+                } else {
77
+                    pc->header_bytes_needed = 9-(buf_size-i);
78
+                    break;
79
+                }
80
+            } else
81
+              state = (state << 8) | buf[i];
82
+        }
83
+    }
53 84
     pc->state = state;
85
+    return -1;
86
+}
87
+
88
+typedef struct DiracParseUnit
89
+{
90
+    int next_pu_offset;
91
+    int prev_pu_offset;
92
+    uint8_t pu_type;
93
+} DiracParseUnit;
94
+
95
+static int unpack_parse_unit(DiracParseUnit *pu, DiracParseContext *pc,
96
+                             int offset)
97
+{
98
+    uint8_t *start = pc->buffer + offset;
99
+    uint8_t *end   = pc->buffer + pc->index;
100
+    if (start < pc->buffer || (start+13 > end))
101
+        return 0;
102
+    pu->pu_type = start[4];
103
+
104
+    pu->next_pu_offset = AV_RB32(start+5);
105
+    pu->prev_pu_offset = AV_RB32(start+9);
106
+
107
+    if (pu->pu_type == 0x10 && pu->next_pu_offset == 0)
108
+        pu->next_pu_offset = 13;
109
+
110
+    return 1;
111
+}
112
+
113
+static int dirac_combine_frame(AVCodecParserContext *s, AVCodecContext *avctx,
114
+                               int next, const uint8_t **buf, int *buf_size)
115
+{
116
+    int parse_timing_info = (s->pts == AV_NOPTS_VALUE &&
117
+                             s->dts == AV_NOPTS_VALUE);
118
+    DiracParseContext *pc = s->priv_data;
119
+
120
+    if (pc->overread_index) {
121
+        memcpy(pc->buffer, pc->buffer + pc->overread_index,
122
+               pc->index - pc->overread_index);
123
+        pc->index -= pc->overread_index;
124
+        pc->overread_index = 0;
125
+        if (*buf_size == 0 && pc->buffer[4] == 0x10) {
126
+            *buf      = pc->buffer;
127
+            *buf_size = pc->index;
128
+            return 0;
129
+        }
130
+    }
131
+
132
+    if ( next == -1) {
133
+        /* Found a possible frame start but not a frame end */
134
+        void *new_buffer = av_fast_realloc(pc->buffer, &pc->buffer_size,
135
+                                           pc->index + (*buf_size -
136
+                                                        pc->sync_offset));
137
+        pc->buffer = new_buffer;
138
+        memcpy(pc->buffer+pc->index, (*buf + pc->sync_offset),
139
+               *buf_size - pc->sync_offset);
140
+        pc->index += *buf_size - pc->sync_offset;
141
+        return -1;
142
+    } else {
143
+        /* Found a possible frame start and a  possible frame end */
144
+        DiracParseUnit pu1, pu;
145
+        void *new_buffer = av_fast_realloc(pc->buffer, &pc->buffer_size,
146
+                                           pc->index + next);
147
+        pc->buffer = new_buffer;
148
+        memcpy(pc->buffer + pc->index, *buf, next);
149
+        pc->index += next;
54 150
 
55
-    return END_NOT_FOUND;
151
+        /* Need to check if we have a valid Parse Unit. We can't go by the
152
+         * sync pattern 'BBCD' alone because arithmetic coding of the residual
153
+         * and motion data can cause the pattern triggering a false start of
154
+         * frame. So check if the previous parse offset of the next parse unit
155
+         * is equal to the next parse offset of the current parse unit then
156
+         * we can be pretty sure that we have a valid parse unit */
157
+        if (!unpack_parse_unit(&pu1, pc, pc->index - 13)                     ||
158
+            !unpack_parse_unit(&pu, pc, pc->index - 13 - pu1.prev_pu_offset) ||
159
+            pu.next_pu_offset != pu1.prev_pu_offset) {
160
+            pc->index -= 9;
161
+            *buf_size = next-9;
162
+            pc->header_bytes_needed = 9;
163
+            return -1;
164
+        }
165
+
166
+        /* All non-frame data must be accompanied by frame data. This is to
167
+         * ensure that pts is set correctly. So if the current parse unit is
168
+         * not frame data, wait for frame data to come along */
169
+
170
+        pc->dirac_unit = pc->buffer + pc->index - 13 -
171
+                         pu1.prev_pu_offset - pc->dirac_unit_size;
172
+
173
+        pc->dirac_unit_size += pu.next_pu_offset;
174
+
175
+        if ((pu.pu_type&0x08) != 0x08) {
176
+            pc->header_bytes_needed = 9;
177
+            *buf_size = next;
178
+            return -1;
179
+        }
180
+
181
+        /* Get the picture number to set the pts and dts*/
182
+        if (parse_timing_info) {
183
+            uint8_t *cur_pu = pc->buffer +
184
+                              pc->index - 13 - pu1.prev_pu_offset;
185
+            int pts =  AV_RB32(cur_pu + 13);
186
+            if (s->last_pts == 0 && s->last_dts == 0)
187
+                s->dts = pts - 1;
188
+            else
189
+                s->dts = s->last_dts+1;
190
+            s->pts = pts;
191
+            if (!avctx->has_b_frames && (cur_pu[4] & 0x03))
192
+                avctx->has_b_frames = 1;
193
+        }
194
+        if (avctx->has_b_frames && s->pts == s->dts)
195
+             s->pict_type = FF_B_TYPE;
196
+
197
+        /* Finally have a complete Dirac data unit */
198
+        *buf      = pc->dirac_unit;
199
+        *buf_size = pc->dirac_unit_size;
200
+
201
+        pc->dirac_unit_size     = 0;
202
+        pc->overread_index      = pc->index-13;
203
+        pc->header_bytes_needed = 9;
204
+    }
205
+    return next;
56 206
 }
57 207
 
58 208
 static int dirac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
59 209
                        const uint8_t **poutbuf, int *poutbuf_size,
60 210
                        const uint8_t *buf, int buf_size)
61 211
 {
62
-    ParseContext *pc = s->priv_data;
212
+    DiracParseContext *pc = s->priv_data;
63 213
     int next;
64 214
 
215
+    *poutbuf = NULL;
216
+    *poutbuf_size = 0;
217
+
65 218
     if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
66 219
         next = buf_size;
67
-    }else{
220
+        *poutbuf = buf;
221
+        *poutbuf_size = buf_size;
222
+        /* Assume that data has been packetized into an encapsulation unit. */
223
+    } else {
68 224
         next = find_frame_end(pc, buf, buf_size);
225
+        if (!pc->is_synced && next == -1) {
226
+            /* No frame start found yet. So throw away the entire buffer. */
227
+            return buf_size;
228
+        }
69 229
 
70
-        if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) {
71
-            *poutbuf = NULL;
72
-            *poutbuf_size = 0;
230
+        if (dirac_combine_frame(s, avctx, next, &buf, &buf_size) < 0) {
73 231
             return buf_size;
74 232
         }
75 233
     }
... ...
@@ -79,10 +238,18 @@ static int dirac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
79 79
     return next;
80 80
 }
81 81
 
82
+static void dirac_parse_close(AVCodecParserContext *s)
83
+{
84
+    DiracParseContext *pc = s->priv_data;
85
+
86
+    if (pc->buffer_size > 0)
87
+        av_free(pc->buffer);
88
+}
89
+
82 90
 AVCodecParser dirac_parser = {
83 91
     { CODEC_ID_DIRAC },
84
-    sizeof(ParseContext),
92
+    sizeof(DiracParseContext),
85 93
     NULL,
86 94
     dirac_parse,
87
-    ff_parse_close,
95
+    dirac_parse_close,
88 96
 };
... ...
@@ -88,10 +88,12 @@ static int libdirac_decode_frame(AVCodecContext *avccontext,
88 88
 
89 89
     *data_size = 0;
90 90
 
91
-    if (buf_size>0)
91
+    if (buf_size>0) {
92 92
         /* set data to decode into buffer */
93 93
         dirac_buffer (p_dirac_params->p_decoder, buf, buf+buf_size);
94
-
94
+        if ((buf[4] &0x08) == 0x08 && (buf[4] & 0x03))
95
+            avccontext->has_b_frames = 1;
96
+    }
95 97
     while (1) {
96 98
          /* parse data and process result */
97 99
         DecoderState state = dirac_parse (p_dirac_params->p_decoder);
... ...
@@ -235,6 +235,9 @@ static int libschroedinger_decode_frame(AVCodecContext *avccontext,
235 235
     do {
236 236
         if ((enc_buf = FfmpegFindNextSchroParseUnit(&parse_ctx))) {
237 237
             /* Push buffer into decoder. */
238
+            if (SCHRO_PARSE_CODE_IS_PICTURE(enc_buf->data[4]) &&
239
+                SCHRO_PARSE_CODE_NUM_REFS(enc_buf->data[4]) > 0)
240
+                avccontext->has_b_frames = 1;
238 241
             state = schro_decoder_push (decoder, enc_buf);
239 242
             if (state == SCHRO_DECODER_FIRST_ACCESS_UNIT)
240 243
                   libschroedinger_handle_first_access_unit(avccontext);