When only SPS or PPS is present in the stream, copy the missing one from
AVCC before insertion to the output stream.
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
... | ... |
@@ -26,9 +26,12 @@ |
26 | 26 |
#include "avcodec.h" |
27 | 27 |
|
28 | 28 |
typedef struct H264BSFContext { |
29 |
+ int32_t sps_offset; |
|
30 |
+ int32_t pps_offset; |
|
29 | 31 |
uint8_t length_size; |
30 | 32 |
uint8_t new_idr; |
31 |
- uint8_t idr_sps_pps_seen; |
|
33 |
+ uint8_t idr_sps_seen; |
|
34 |
+ uint8_t idr_pps_seen; |
|
32 | 35 |
int extradata_parsed; |
33 | 36 |
} H264BSFContext; |
34 | 37 |
|
... | ... |
@@ -60,7 +63,7 @@ static int alloc_and_copy(uint8_t **poutbuf, int *poutbuf_size, |
60 | 60 |
return 0; |
61 | 61 |
} |
62 | 62 |
|
63 |
-static int h264_extradata_to_annexb(AVCodecContext *avctx, const int padding) |
|
63 |
+static int h264_extradata_to_annexb(H264BSFContext *ctx, AVCodecContext *avctx, const int padding) |
|
64 | 64 |
{ |
65 | 65 |
uint16_t unit_size; |
66 | 66 |
uint64_t total_size = 0; |
... | ... |
@@ -70,11 +73,14 @@ static int h264_extradata_to_annexb(AVCodecContext *avctx, const int padding) |
70 | 70 |
static const uint8_t nalu_header[4] = { 0, 0, 0, 1 }; |
71 | 71 |
int length_size = (*extradata++ & 0x3) + 1; // retrieve length coded size |
72 | 72 |
|
73 |
+ ctx->sps_offset = ctx->pps_offset = -1; |
|
74 |
+ |
|
73 | 75 |
/* retrieve sps and pps unit(s) */ |
74 | 76 |
unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */ |
75 | 77 |
if (!unit_nb) { |
76 | 78 |
goto pps; |
77 | 79 |
} else { |
80 |
+ ctx->sps_offset = 0; |
|
78 | 81 |
sps_seen = 1; |
79 | 82 |
} |
80 | 83 |
|
... | ... |
@@ -103,8 +109,10 @@ static int h264_extradata_to_annexb(AVCodecContext *avctx, const int padding) |
103 | 103 |
pps: |
104 | 104 |
if (!unit_nb && !sps_done++) { |
105 | 105 |
unit_nb = *extradata++; /* number of pps unit(s) */ |
106 |
- if (unit_nb) |
|
106 |
+ if (unit_nb) { |
|
107 |
+ ctx->pps_offset = (extradata - 1) - (avctx->extradata + 4); |
|
107 | 108 |
pps_seen = 1; |
109 |
+ } |
|
108 | 110 |
} |
109 | 111 |
} |
110 | 112 |
|
... | ... |
@@ -151,12 +159,13 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, |
151 | 151 |
|
152 | 152 |
/* retrieve sps and pps NAL units from extradata */ |
153 | 153 |
if (!ctx->extradata_parsed) { |
154 |
- ret = h264_extradata_to_annexb(avctx, FF_INPUT_BUFFER_PADDING_SIZE); |
|
154 |
+ ret = h264_extradata_to_annexb(ctx, avctx, FF_INPUT_BUFFER_PADDING_SIZE); |
|
155 | 155 |
if (ret < 0) |
156 | 156 |
return ret; |
157 | 157 |
ctx->length_size = ret; |
158 | 158 |
ctx->new_idr = 1; |
159 |
- ctx->idr_sps_pps_seen = 0; |
|
159 |
+ ctx->idr_sps_seen = 0; |
|
160 |
+ ctx->idr_pps_seen = 0; |
|
160 | 161 |
ctx->extradata_parsed = 1; |
161 | 162 |
} |
162 | 163 |
|
... | ... |
@@ -176,8 +185,25 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, |
176 | 176 |
if (buf + nal_size > buf_end || nal_size < 0) |
177 | 177 |
goto fail; |
178 | 178 |
|
179 |
- if (unit_type == 7 || unit_type == 8) |
|
180 |
- ctx->idr_sps_pps_seen = 1; |
|
179 |
+ if (unit_type == 7) |
|
180 |
+ ctx->idr_sps_seen = 1; |
|
181 |
+ else if (unit_type == 8) { |
|
182 |
+ ctx->idr_pps_seen = 1; |
|
183 |
+ /* if SPS has not been seen yet, prepend the AVCC one to PPS */ |
|
184 |
+ if (!ctx->idr_sps_seen) { |
|
185 |
+ if (ctx->sps_offset == -1) |
|
186 |
+ av_log(avctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n"); |
|
187 |
+ else { |
|
188 |
+ if ((ret = alloc_and_copy(poutbuf, poutbuf_size, |
|
189 |
+ avctx->extradata + ctx->sps_offset, |
|
190 |
+ ctx->pps_offset != -1 ? ctx->pps_offset : avctx->extradata_size - ctx->sps_offset, |
|
191 |
+ buf, nal_size)) < 0) |
|
192 |
+ goto fail; |
|
193 |
+ ctx->idr_sps_seen = 1; |
|
194 |
+ goto next_nal; |
|
195 |
+ } |
|
196 |
+ } |
|
197 |
+ } |
|
181 | 198 |
|
182 | 199 |
/* if this is a new IDR picture following an IDR picture, reset the idr flag. |
183 | 200 |
* Just check first_mb_in_slice to be 0 as this is the simplest solution. |
... | ... |
@@ -186,22 +212,35 @@ static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc, |
186 | 186 |
ctx->new_idr = 1; |
187 | 187 |
|
188 | 188 |
/* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ |
189 |
- if (ctx->new_idr && unit_type == 5 && !ctx->idr_sps_pps_seen) { |
|
189 |
+ if (ctx->new_idr && unit_type == 5 && !ctx->idr_sps_seen && !ctx->idr_pps_seen) { |
|
190 | 190 |
if ((ret=alloc_and_copy(poutbuf, poutbuf_size, |
191 | 191 |
avctx->extradata, avctx->extradata_size, |
192 | 192 |
buf, nal_size)) < 0) |
193 | 193 |
goto fail; |
194 | 194 |
ctx->new_idr = 0; |
195 |
+ /* if only SPS has been seen, also insert PPS */ |
|
196 |
+ } else if (ctx->new_idr && unit_type == 5 && ctx->idr_sps_seen && !ctx->idr_pps_seen) { |
|
197 |
+ if (ctx->pps_offset == -1) { |
|
198 |
+ av_log(avctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); |
|
199 |
+ if ((ret = alloc_and_copy(poutbuf, poutbuf_size, |
|
200 |
+ NULL, 0, buf, nal_size)) < 0) |
|
201 |
+ goto fail; |
|
202 |
+ } else if ((ret = alloc_and_copy(poutbuf, poutbuf_size, |
|
203 |
+ avctx->extradata + ctx->pps_offset, avctx->extradata_size - ctx->pps_offset, |
|
204 |
+ buf, nal_size)) < 0) |
|
205 |
+ goto fail; |
|
195 | 206 |
} else { |
196 | 207 |
if ((ret=alloc_and_copy(poutbuf, poutbuf_size, |
197 | 208 |
NULL, 0, buf, nal_size)) < 0) |
198 | 209 |
goto fail; |
199 | 210 |
if (!ctx->new_idr && unit_type == 1) { |
200 | 211 |
ctx->new_idr = 1; |
201 |
- ctx->idr_sps_pps_seen = 0; |
|
212 |
+ ctx->idr_sps_seen = 0; |
|
213 |
+ ctx->idr_pps_seen = 0; |
|
202 | 214 |
} |
203 | 215 |
} |
204 | 216 |
|
217 |
+next_nal: |
|
205 | 218 |
buf += nal_size; |
206 | 219 |
cumul_size += nal_size + ctx->length_size; |
207 | 220 |
} while (cumul_size < buf_size); |