Ticket #6306: BugTestCase.c

File BugTestCase.c, 18.3 KB (added by s0m3, 9 years ago)

Test case to trigger the bug

Line 
1#include <stdio.h> /* For printf, fprintf */
2#include <stdbool.h> /* For bool */
3
4#ifdef _WIN32
5#define inline _inline
6#endif
7
8#include <libavfilter/buffersrc.h> /* For AVFilterContext */
9#include <libavformat\avformat.h> /* For AVFormatContext */
10#include <libavfilter/buffersink.h> /* For AVBufferSinkParams, av_buffersink_get_frame */
11#include <libavutil\opt.h> /* For av_opt_set_int_list */
12#include <libavutil/imgutils.h> /* For av_image_get_buffer_size */
13#include <inttypes.h> /* For PRId64 */
14
15int ret;
16bool hasNoError;
17
18#define CHECK(v) do { if ((v)) { fprintf(stderr, "[ERR]:%s:%d Unexpected failure\n", __FILE__, __LINE__); return false; } } while(0)
19#define CHECK2(v) do { if ((v)) { fprintf(stderr, "[ERR]:%s:%d Unexpected failure\n", __FILE__, __LINE__); goto onError; } } while(0)
20
21typedef struct testCaseContext
22{
23 AVFormatContext *format_context;
24 int64_t media_duration;
25 int video_stream_index;
26 AVFilterContext *source_filter_context;
27 AVFilterContext *sink_filter_context;
28 AVFilterGraph *filter_graph;
29 AVCodecContext *video_codec_context;
30 int frame_width;
31 int frame_height;
32} testCaseContext;
33
34/* no portable way to pack BMP structures so use fixed size array and offsets */
35typedef struct BITMAPFILEHEADER { uint8_t v[14]; } BITMAPFILEHEADER;
36typedef struct BITMAPINFOHEADER { uint8_t v[40]; } BITMAPINFOHEADER;
37
38void init(testCaseContext *zis)
39{
40 zis->format_context = NULL;
41 zis->media_duration = -1;
42 zis->video_stream_index = -1;
43 zis->source_filter_context = NULL;
44 zis->sink_filter_context = NULL;
45 zis->filter_graph = NULL;
46 zis->video_codec_context = NULL;
47
48 avcodec_register_all();
49 av_register_all();
50}
51
52bool setupAVDecoder(testCaseContext *zis);
53bool configureAVFilter(testCaseContext *zis);
54
55bool analyse(testCaseContext *zis)
56{
57 int videoStreamCount, videoStreamIndex;
58
59 ret = avformat_find_stream_info(zis->format_context, NULL);
60 CHECK(ret < 0);
61 CHECK(zis->format_context->nb_streams <= 0);
62
63 for (int i = videoStreamCount = 0; i < (int)zis->format_context->nb_streams; i++)
64 {
65 if (zis->format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
66 {
67 if (!videoStreamCount)
68 videoStreamIndex = i;
69 videoStreamCount++;
70 }
71 }
72
73 /* Media has no video stream - Aborting */
74 CHECK(videoStreamCount == 0);
75 /* Note that if videoStreamCount != 1, it means media has more than 1 video stream
76 videoStreamIndex is the first one */
77
78 zis->video_stream_index = videoStreamIndex;
79
80 /* duration (us), duration/AV_TIME_BASE (s), duration*10 (100ns) */
81 if (zis->format_context->duration)
82 zis->media_duration = zis->format_context->duration * 10;
83
84 zis->frame_width = zis->format_context->streams[zis->video_stream_index]->codecpar->width;
85 zis->frame_height = zis->format_context->streams[zis->video_stream_index]->codecpar->height;
86
87 hasNoError = setupAVDecoder(zis);
88 CHECK(!hasNoError);
89
90 hasNoError = configureAVFilter(zis);
91 CHECK(!hasNoError);
92
93 return true;
94}
95
96bool setupAVDecoder(testCaseContext *zis)
97{
98 AVCodec *videoCodec;
99
100 videoCodec = avcodec_find_decoder(zis->format_context->streams[zis->video_stream_index]->codecpar->codec_id);
101 CHECK(!videoCodec);
102
103 zis->video_codec_context = avcodec_alloc_context3(NULL);
104 CHECK(!zis->video_codec_context);
105
106 ret = avcodec_parameters_to_context(zis->video_codec_context, zis->format_context->streams[zis->video_stream_index]->codecpar);
107 CHECK(ret < 0);
108
109 /* set threads option to 0 to autodetect and use a maximum of threads */
110 AVDictionary *videoCodecOptions = NULL;
111 ret = av_dict_set(&videoCodecOptions, "threads", "0", 0);
112 CHECK(ret < 0);
113
114 ret = avcodec_open2(zis->video_codec_context, videoCodec, &videoCodecOptions);
115 CHECK(ret);
116
117 return true;
118}
119
120bool configureAVFilter(testCaseContext *zis)
121{
122 AVCodecContext *videoCodecContext;
123 char args[512];
124 AVRational timeBase;
125 AVFilter *sourceFilter;
126 AVBufferSinkParams *sinkParameters;
127 enum AVPixelFormat supportedPixelFormats[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
128 AVFilter *sinkFilter;
129 AVFilter *formatFilter;
130 AVFilterContext *formatFilterContext;
131
132 avfilter_register_all();
133
134 zis->filter_graph = avfilter_graph_alloc();
135 CHECK(!zis->filter_graph);
136
137 /* Create a filter graph to convert between decoder pixel format to RGB32.
138 We use the "format" filter to do convertion.
139 We'll have the following chain: [buffer] -> [format] -> [bufferSink] */
140
141 /* input/buffer filter */
142
143 videoCodecContext = avcodec_alloc_context3(NULL);
144 CHECK(!videoCodecContext);
145
146 ret = avcodec_parameters_to_context(videoCodecContext, zis->format_context->streams[zis->video_stream_index]->codecpar);
147 CHECK(ret < 0);
148
149 timeBase = zis->format_context->streams[zis->video_stream_index]->time_base;
150 _snprintf(args, sizeof(args),
151 "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
152 videoCodecContext->width, videoCodecContext->height, videoCodecContext->pix_fmt,
153 timeBase.num, timeBase.den,
154 videoCodecContext->sample_aspect_ratio.num, videoCodecContext->sample_aspect_ratio.den);
155 avcodec_free_context(&videoCodecContext);
156
157 sourceFilter = avfilter_get_by_name("buffer");
158 CHECK(!sourceFilter);
159
160 ret = avfilter_graph_create_filter(&zis->source_filter_context, sourceFilter, "in", args, NULL, zis->filter_graph);
161 CHECK(ret < 0);
162
163 /* output/sink filter */
164
165 sinkParameters = av_buffersink_params_alloc();
166 CHECK(!sinkParameters);
167
168 sinkParameters->pixel_fmts = supportedPixelFormats;
169
170 sinkFilter = avfilter_get_by_name("buffersink");
171 CHECK(!sinkFilter);
172
173 ret = avfilter_graph_create_filter(&zis->sink_filter_context, sinkFilter, "out", NULL, sinkParameters, zis->filter_graph);
174 av_free(sinkParameters);
175 CHECK(ret < 0);
176
177 /* format filter */
178
179 formatFilter = avfilter_get_by_name("format");
180 CHECK(!formatFilter);
181
182 /* We wants RGB32 frames */
183 ret = avfilter_graph_create_filter(&formatFilterContext, formatFilter, "pixel format converter", "pix_fmts=rgb32", NULL, zis->filter_graph);
184 CHECK(ret < 0);
185
186 ret = avfilter_link(zis->source_filter_context, 0, formatFilterContext, 0);
187 CHECK(ret != 0);
188
189 ret = avfilter_link(formatFilterContext, 0, zis->sink_filter_context, 0);
190 CHECK(ret != 0);
191
192 ret = avfilter_graph_config(zis->filter_graph, NULL);
193 CHECK(ret < 0);
194
195 return true;
196}
197
198bool getFrameFromCodec(testCaseContext *zis, AVFrame **decodedFrame, bool *gotFrame, bool *isEOF)
199{
200 AVFrame *decodedFrameTemp;
201
202 CHECK(!decodedFrame);
203 CHECK(!gotFrame);
204 CHECK(!isEOF);
205
206 decodedFrameTemp = av_frame_alloc();
207 CHECK(!decodedFrameTemp);
208
209 ret = avcodec_receive_frame(zis->video_codec_context, decodedFrameTemp);
210 CHECK(ret != 0 && ret != AVERROR_EOF && ret != AVERROR(EAGAIN));
211
212 /* End of stream reached */
213 if (ret == AVERROR_EOF)
214 {
215 *gotFrame = false;
216 *isEOF = true;
217 return true;
218 }
219
220 if (!ret)
221 *decodedFrame = decodedFrameTemp;
222 *gotFrame = !ret;
223 *isEOF = false;
224 return true;
225}
226
227bool convertFrameToRGB32(testCaseContext *zis, AVFrame *decodedFrame, int maxBufferLength,
228 uint8_t *dataBuffer, int bufferStride, int *currentBufferLength)
229{
230 int requiredSize;
231 AVFrame *frameOut;
232 int stride;
233 uint8_t *inData;
234 uint8_t *outData;
235 int lineLength;
236
237 /* We use "4" because there are 4 bytes per pixel in RGB32 */
238
239 CHECK(!decodedFrame);
240 CHECK(!dataBuffer);
241 CHECK(bufferStride <= 0);
242 CHECK(!currentBufferLength);
243
244 CHECK(decodedFrame->width != zis->format_context->streams[zis->video_stream_index]->codecpar->width);
245 CHECK(decodedFrame->height != zis->format_context->streams[zis->video_stream_index]->codecpar->height);
246
247 CHECK(maxBufferLength % bufferStride != 0);
248 CHECK(maxBufferLength / bufferStride != decodedFrame->height);
249 CHECK(bufferStride < (decodedFrame->width * 4));
250
251 requiredSize = av_image_get_buffer_size(AV_PIX_FMT_RGB32, decodedFrame->width, decodedFrame->height, 1);
252 CHECK(requiredSize < (int)(decodedFrame->width * decodedFrame->height * 4));
253
254 /* caution: reset all decodedFrame fields (so width and height fields will be 0) */
255 ret = av_buffersrc_add_frame(zis->source_filter_context, decodedFrame);
256 CHECK(ret < 0);
257
258 frameOut = av_frame_alloc();
259 CHECK(!frameOut);
260
261 /* We gave a full frame to buffersrc so we must get a full RGB32 frame */
262 /* Note: av_buffersink_get_frame allocates an internal AVFrame, decodes data
263 and then copy result to frameOut.
264 In other words, we don't need to allocate a buffer but we have to memcpy data to our own buffer
265 FIXME: Ask ffmpeg devs to fix that */
266 ret = av_buffersink_get_frame(zis->sink_filter_context, frameOut);
267 CHECK(ret < 0);
268
269 stride = frameOut->linesize[0];
270 CHECK(stride <= 0);
271 CHECK(stride < frameOut->width * 4);
272
273 /* data is stored bottom up */
274 inData = frameOut->data[0];
275 inData += frameOut->height * stride;
276 outData = dataBuffer;
277 outData += frameOut->height * bufferStride;
278 lineLength = frameOut->width * 4;
279 for (int i = 0; i < frameOut->height; i++)
280 {
281 inData -= stride;
282 outData -= bufferStride;
283 memcpy(outData, inData, lineLength);
284 }
285 *currentBufferLength = frameOut->height * bufferStride;
286
287 av_frame_unref(frameOut);
288 av_frame_free(&frameOut);
289
290 return true;
291}
292
293bool decodeNextPacket(testCaseContext *zis, int streamIndex, AVPacket *framePacket,
294 bool *isKeyFrame, int64_t *frameTime,
295 bool *isEOF)
296{
297 bool isKeyFrameTemp;
298
299 CHECK(!framePacket);
300 CHECK(!isKeyFrame);
301 CHECK(!frameTime);
302 CHECK(!isEOF);
303
304
305 for (;;)
306 {
307 ret = av_read_frame(zis->format_context, framePacket);
308 /* End of stream reached - returning S_FALSE */
309 if (ret == AVERROR_EOF)
310 {
311 *isEOF = true;
312 return true;
313 }
314 CHECK(ret < 0);
315
316 if ((framePacket->stream_index != streamIndex))
317 av_packet_unref(framePacket);
318 else
319 break;
320 }
321
322 isKeyFrameTemp = !!(framePacket->flags & AV_PKT_FLAG_KEY);
323 *isKeyFrame = isKeyFrameTemp;
324
325 /* Malformed media, couldn't get frame timestamp information */
326 if (framePacket->pts == AV_NOPTS_VALUE)
327 {
328 *frameTime = -1;
329 }
330 else
331 {
332 /* pts * time_base = seconds. Change formula to get 100ns. */
333 AVRational streamTimeBase = zis->format_context->streams[streamIndex]->time_base;
334 streamTimeBase.num *= 10000000;
335
336 *frameTime = (int64_t)(framePacket->pts * av_q2d(streamTimeBase));
337 }
338
339 *isEOF = false;
340 return true;
341}
342
343bool decodeNextFrame(testCaseContext *zis, int streamIndex, AVCodecContext *codecContext,
344 AVFrame **decodedFrame, int64_t *frameTime,
345 bool *isEOF)
346{
347 bool gotFrame;
348 AVFrame *decodedFrameTemp;
349 bool isEOFTmp;
350 int64_t frameTimestamp;
351 AVRational videoStreamTimeBase;
352
353 CHECK(!codecContext);
354 CHECK(!decodedFrame);
355 CHECK(!frameTime);
356 CHECK(!isEOF);
357
358 /* a codec may buffer several raw data packets before delivering a frame
359 and may deliver several frames from its cache so ask it first */
360
361 hasNoError = getFrameFromCodec(zis, &decodedFrameTemp, &gotFrame, &isEOFTmp);
362 CHECK(!hasNoError);
363
364 if (isEOFTmp)
365 {
366 /* End of stream reached */
367 *isEOF = true;
368 return true;
369 }
370
371 while (!gotFrame)
372 {
373 AVPacket framePacket;
374 bool isKeyFrame;
375 int64_t frameTimeTemp;
376
377 av_init_packet(&framePacket);
378 framePacket.data = NULL;
379 framePacket.size = 0;
380
381 hasNoError = decodeNextPacket(zis, streamIndex, &framePacket, &isKeyFrame, &frameTimeTemp, &isEOFTmp);
382 CHECK(!hasNoError);
383
384 if (isEOFTmp)
385 {
386 /* enter draining mode: codec may have cached several output frames */
387
388 /* Reached end of stream - entering draining mode */
389
390 ret = avcodec_send_packet(codecContext, NULL);
391 CHECK(ret < 0);
392
393 hasNoError = getFrameFromCodec(zis, &decodedFrameTemp, &gotFrame, &isEOFTmp);
394 CHECK(!hasNoError);
395
396 /* if we got a cached frame, process it.
397 Otherwise, tell the caller we reached the end of the stream */
398 if (isEOFTmp)
399 {
400 /* End of stream reached */
401 *isEOF = true;
402 return true;
403 }
404 if (gotFrame)
405 {
406 /* Got a frame from draining mode */
407 }
408 }
409
410 /* happens in draining mode */
411 if (gotFrame)
412 break;
413
414 /* send raw data to codec */
415
416 ret = avcodec_send_packet(codecContext, &framePacket);
417 av_packet_unref(&framePacket);
418 /* media file format not supported */
419 CHECK(ret == AVERROR_INVALIDDATA);
420 CHECK(ret < 0 && ret != AVERROR_EOF);
421 /* should not happen at this point */
422 CHECK(ret == AVERROR_EOF);
423
424 hasNoError = getFrameFromCodec(zis, &decodedFrameTemp, &gotFrame, &isEOFTmp);
425 CHECK(!hasNoError);
426 /* should not happen at this point */
427 CHECK(isEOFTmp);
428 }
429
430 if (decodedFrameTemp->pts == AV_NOPTS_VALUE)
431 {
432 /* Decoded frame doesn't have any PTS information - guessing it */
433 frameTimestamp = av_frame_get_best_effort_timestamp(decodedFrameTemp);
434 }
435 else
436 {
437 frameTimestamp = decodedFrameTemp->pts;
438 }
439
440 /* pts * time_base = seconds. Change formula to get 100ns. */
441 videoStreamTimeBase = zis->format_context->streams[streamIndex]->time_base;
442 videoStreamTimeBase.num *= 10000000;
443
444 *frameTime = (int64_t)(frameTimestamp * av_q2d(videoStreamTimeBase));
445 *decodedFrame = decodedFrameTemp;
446 *isEOF = false;
447 return true;
448}
449
450bool DecodeNextFrame(testCaseContext *zis, uint8_t *frameBuffer, bool *isEOF, int64_t *decodedFrameTime)
451{
452 int64_t frameTimeTemp;
453 AVFrame *decodedFrame;
454 bool isEOFTmp;
455 uint8_t *bufferData;
456 int sampleStride;
457 int maxBufferLength;
458 int currentBufferLengthTemp;
459
460 CHECK(!frameBuffer);
461
462 hasNoError = decodeNextFrame(zis, zis->video_stream_index, zis->video_codec_context,
463 &decodedFrame, &frameTimeTemp,
464 &isEOFTmp);
465 CHECK(!hasNoError);
466
467 if (isEOFTmp)
468 {
469 /* End of stream reached */
470 *isEOF = true;
471 return true;
472 }
473
474 bufferData = frameBuffer;
475 sampleStride = zis->frame_width * 4 /*RGB32*/;
476 maxBufferLength = zis->frame_width * zis->frame_height * 4;;
477
478 hasNoError = convertFrameToRGB32(zis, decodedFrame, maxBufferLength,
479 bufferData, sampleStride, &currentBufferLengthTemp);
480 CHECK(!hasNoError);
481
482 av_frame_unref(decodedFrame);
483 av_frame_free(&decodedFrame);
484
485 *isEOF = isEOFTmp;
486 *decodedFrameTime = frameTimeTemp;
487
488 return true;
489}
490
491bool SaveFrameToDisk(testCaseContext *zis, uint8_t *frameBuffer, int64_t frameTime)
492{
493 static int64_t currentFrameNumber = 0;
494 BITMAPINFOHEADER bih;
495 BITMAPFILEHEADER bfh;
496 /* int_64_t max value is 9223372036854775807 = 19 digits
497 <int64_t> + '_' + <int64_t> + '.' + 'bmp' + '\0' */
498 char filepath[19 + 1 + 19 + 1 + 3 + 1];
499 FILE *fileHandle;
500
501 /* set up bitmap header */
502
503#define SET_STRUCT_FIELD(s,o,t) *( (t *)((uint8_t *)&s + o) )
504 memset(&bih, 0, sizeof(BITMAPINFOHEADER));
505 SET_STRUCT_FIELD(bih, 14, unsigned long) = 32; /*bitCount*/
506 SET_STRUCT_FIELD(bih, 16, unsigned long) = 0 /*BI_RGB*/; /*compression*/
507 SET_STRUCT_FIELD(bih, 8, long) = zis->frame_height; /*height*/
508 SET_STRUCT_FIELD(bih, 4, long) = zis->frame_width; /*width*/
509 SET_STRUCT_FIELD(bih, 12, unsigned short) = 1; /*planes*/
510 SET_STRUCT_FIELD(bih, 0, unsigned long) = sizeof(BITMAPINFOHEADER); /*size*/
511
512 memset(&bfh, 0, sizeof(BITMAPFILEHEADER));
513 SET_STRUCT_FIELD(bfh, 0, unsigned short) = 19778 /* 'B' 'M' little-endian */; /*type*/
514 SET_STRUCT_FIELD(bfh, 10, unsigned long) = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); /*offBits*/
515 SET_STRUCT_FIELD(bfh, 2, unsigned long) = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))
516 + (zis->frame_width * 32 /*bitCount*/ + 7) / 8 * zis->frame_height; /*size*/
517#undef SET_STRUCT_FIELD
518
519 /* create output file */
520
521 sprintf(filepath, "%"PRId64"_%"PRId64".bmp", currentFrameNumber, frameTime);
522
523 fileHandle = fopen(filepath, "wb");
524 CHECK(!fileHandle);
525
526 /* retrieve data */
527
528 fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fileHandle);
529 fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fileHandle);
530
531 for (int i = 0; i < zis->frame_height; i++)
532 {
533 int lineLength = zis->frame_width * 4;
534 int sampleStride = lineLength;
535 uint8_t *currentLine = frameBuffer + (zis->frame_height - (i + 1))*sampleStride;
536 fwrite(currentLine, lineLength, 1, fileHandle);
537 }
538
539 fclose(fileHandle);
540
541 currentFrameNumber++;
542 return true;
543}
544
545int main(int argc, char **argv)
546{
547 testCaseContext zis;
548 uint8_t *frameBuffer = NULL;
549 bool isEOF;
550 int64_t decodedFrameTime;
551
552 if (argc != 2)
553 {
554 printf("Usage: %s <filepath>\n", *argv);
555 return -1;
556 }
557
558 init(&zis);
559
560 ret = avformat_open_input(&zis.format_context, argv[1], NULL, NULL);
561 /* media file format not supported */
562 CHECK2(ret == AVERROR_INVALIDDATA);
563
564 hasNoError = analyse(&zis);
565 CHECK2(!hasNoError);
566
567 frameBuffer = malloc(zis.frame_width * zis.frame_height * 4);
568 CHECK2(!frameBuffer);
569
570 do
571 {
572 hasNoError = DecodeNextFrame(&zis, frameBuffer, &isEOF, &decodedFrameTime);
573 CHECK2(!hasNoError);
574
575 hasNoError = SaveFrameToDisk(&zis, frameBuffer, decodedFrameTime);
576 CHECK2(!hasNoError);
577 }
578 while (!isEOF);
579
580onError:
581 if (zis.format_context) avformat_close_input(&zis.format_context);
582 if (zis.filter_graph) avfilter_graph_free(&zis.filter_graph);
583 if (zis.video_codec_context) avcodec_free_context(&zis.video_codec_context);
584 free(frameBuffer);
585 return 0;
586}