Ticket #2322: Muxing.cpp

File Muxing.cpp, 18.4 KB (added by mytree, 3 years ago)

memory leak test cpp file

Line 
1
2#ifndef WIN32
3#error  "This test code is only supported Windows OS."
4#endif  //      WIN32
5
6#define _CRT_SECURE_NO_WARNINGS
7
8//--------------------------------------------------------------------------
9//              ffmpeg sample settings
10
11#include <Windows.h>
12
13#include <iostream>
14#include "stdint.h"                                                                                                     //      Integer & "inline" define
15
16#ifndef INT64_C
17#define INT64_C(val)    val##i64
18#define UINT64_C(val)   val##ui64
19#endif  //      INT64_C
20
21extern "C"
22{
23#include "libavutil/mathematics.h"
24#include "libavutil/log.h"
25#include "libavformat/avformat.h"
26#include "libswscale/swscale.h"
27}
28
29static char g_szErrorBuf[ AV_ERROR_MAX_STRING_SIZE ] = "";                      //      Error String Temp Buffer
30
31#ifdef av_err2str
32#undef av_err2str
33#define av_err2str( errcode )   av_make_error_string( g_szErrorBuf, AV_ERROR_MAX_STRING_SIZE, errcode )
34#endif          //      av_err2str
35
36#pragma comment( lib, "avutil.lib" )            //      _av_free
37#pragma comment( lib, "avformat.lib" )          //      _avio_close
38#pragma comment( lib, "avcodec.lib" )           //      _avcodec_*
39#pragma comment( lib, "swscale.lib" )           //      _sws_*
40
41//--------------------------------------------------------------------------
42
43/* 5 seconds stream duration */
44#define STREAM_DURATION   5.0   //200.0
45#define STREAM_FRAME_RATE 25 /* 25 images/s */
46#define STREAM_NB_FRAMES  ((int)(STREAM_DURATION * STREAM_FRAME_RATE))
47#define STREAM_PIX_FMT    AV_PIX_FMT_YUV420P /* default pix_fmt */
48
49static int sws_flags = SWS_BICUBIC;
50
51static float t, tincr, tincr2;
52static int16_t *samples;
53static int audio_input_frame_size;
54
55/* Add an output stream. */
56//static AVStream *add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
57//{
58//    AVCodecContext *c;
59//    AVStream *st;
60//
61//    /* find the encoder */
62//    *codec = avcodec_find_encoder(codec_id);
63//    if (!(*codec)) {
64//        fprintf(stderr, "Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
65//        exit(1);
66//    }
67//
68//    st = avformat_new_stream(oc, *codec);
69//    if (!st) {
70//        fprintf(stderr, "Could not allocate stream\n");
71//        exit(1);
72//    }
73//    st->id = oc->nb_streams-1;
74//    c = st->codec;
75//
76//    switch ((*codec)->type) {
77//    case AVMEDIA_TYPE_AUDIO:
78//        st->id = 1;
79//        c->sample_fmt  = AV_SAMPLE_FMT_S16;
80//        c->bit_rate    = 64000;
81//        c->sample_rate = 44100;
82//        c->channels    = 2;
83//        break;
84//
85//    case AVMEDIA_TYPE_VIDEO:
86//        avcodec_get_context_defaults3(c, *codec);
87//        c->codec_id = codec_id;
88//
89//        c->bit_rate = 400000;
90//        /* Resolution must be a multiple of two. */
91//        c->width    = 352;
92//        c->height   = 288;
93//        /* timebase: This is the fundamental unit of time (in seconds) in terms
94//         * of which frame timestamps are represented. For fixed-fps content,
95//         * timebase should be 1/framerate and timestamp increments should be
96//         * identical to 1. */
97//        c->time_base.den = STREAM_FRAME_RATE;
98//        c->time_base.num = 1;
99//        c->gop_size      = 12; /* emit one intra frame every twelve frames at most */
100//        c->pix_fmt       = STREAM_PIX_FMT;
101//
102//              c->qmin                         =       2;
103//              c->qmax                         =       31;
104//
105//        if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
106//            /* just for testing, we also add B frames */
107//            c->max_b_frames = 2;
108//        }
109//        if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
110//            /* Needed to avoid using macroblocks in which some coeffs overflow.
111//             * This does not happen with normal video, it just happens here as
112//             * the motion of the chroma plane does not match the luma plane. */
113//            c->mb_decision = 2;
114//        }
115//    break;
116//
117//    default:
118//        break;
119//    }
120//
121//    /* Some formats want stream headers to be separate. */
122//    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
123//        c->flags |= CODEC_FLAG_GLOBAL_HEADER;
124//
125//    return st;
126//}
127
128static AVStream *add_stream(AVFormatContext** ppOC, AVCodec **codec, enum AVCodecID codec_id)
129{
130        AVFormatContext* pOC = *ppOC;
131    AVCodecContext *c;
132    AVStream *st;
133
134    /* find the encoder */
135    *codec = avcodec_find_encoder(codec_id);
136    if (!(*codec)) {
137        fprintf(stderr, "Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
138        exit(1);
139    }
140
141    st = avformat_new_stream( pOC, *codec);
142    if (!st) {
143        fprintf(stderr, "Could not allocate stream\n");
144        exit(1);
145    }
146    st->id = pOC->nb_streams-1;
147    c = st->codec;
148
149    switch ((*codec)->type) {
150    case AVMEDIA_TYPE_AUDIO:
151        st->id = 1;
152        c->sample_fmt  = AV_SAMPLE_FMT_S16;
153        c->bit_rate    = 64000;
154        c->sample_rate = 44100;
155        c->channels    = 2;
156        break;
157
158    case AVMEDIA_TYPE_VIDEO:
159        avcodec_get_context_defaults3(c, *codec);
160        c->codec_id = codec_id;
161
162        c->bit_rate = 400000;
163        /* Resolution must be a multiple of two. */
164        c->width    = 352;
165        c->height   = 288;
166        /* timebase: This is the fundamental unit of time (in seconds) in terms
167         * of which frame timestamps are represented. For fixed-fps content,
168         * timebase should be 1/framerate and timestamp increments should be
169         * identical to 1. */
170        c->time_base.den = STREAM_FRAME_RATE;
171        c->time_base.num = 1;
172        c->gop_size      = 12; /* emit one intra frame every twelve frames at most */
173        c->pix_fmt       = STREAM_PIX_FMT;
174
175                c->qmin                         =       2;
176                c->qmax                         =       31;
177
178        if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
179            /* just for testing, we also add B frames */
180            c->max_b_frames = 2;
181        }
182        if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
183            /* Needed to avoid using macroblocks in which some coeffs overflow.
184             * This does not happen with normal video, it just happens here as
185             * the motion of the chroma plane does not match the luma plane. */
186            c->mb_decision = 2;
187        }
188    break;
189
190    default:
191        break;
192    }
193
194    /* Some formats want stream headers to be separate. */
195    if (pOC->oformat->flags & AVFMT_GLOBALHEADER)
196        c->flags |= CODEC_FLAG_GLOBAL_HEADER;
197
198    return st;
199}
200
201static void open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st)
202{
203    AVCodecContext *c;
204    int ret;
205
206    c = st->codec;
207
208        /* open it */
209    ret = avcodec_open2(c, codec, NULL);
210    if (ret < 0) {
211        fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret));
212                exit(1);
213    }
214
215    /* init signal generator */
216    t     = 0;
217    tincr = (float)( 2 * M_PI * 110.0 / c->sample_rate );
218    /* increment frequency by 110 Hz per second */
219    tincr2 = (float)( 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate );
220
221    if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
222        audio_input_frame_size = 10000;
223    else
224        audio_input_frame_size = c->frame_size;
225    samples = (int16_t*)av_malloc(audio_input_frame_size *
226                        av_get_bytes_per_sample(c->sample_fmt) *
227                        c->channels);
228    if (!samples) {
229        fprintf(stderr, "Could not allocate audio samples buffer\n");
230        exit(1);
231    }
232}
233
234static void close_audio(AVFormatContext *oc, AVStream *st)
235{
236    avcodec_close(st->codec);
237
238    av_free(samples);
239}
240
241static AVFrame *frame;
242static AVPicture src_picture, dst_picture;
243static int frame_count;
244
245static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
246{
247    int ret;
248    AVCodecContext *c = st->codec;
249
250        /* open the codec */
251    ret = avcodec_open2(c, codec, NULL);
252    if (ret < 0) {
253        fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
254        exit(1);
255    }
256
257    /* allocate and init a re-usable frame */
258    frame = avcodec_alloc_frame();
259    if (!frame) {
260        fprintf(stderr, "Could not allocate video frame\n");
261        exit(1);
262    }
263
264    /* Allocate the encoded raw picture. */
265    ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
266    if (ret < 0) {
267        fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret));
268        exit(1);
269    }
270
271    /* If the output format is not YUV420P, then a temporary YUV420P
272     * picture is needed too. It is then converted to the required
273     * output format. */
274    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
275        ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width, c->height);
276        if (ret < 0) {
277            fprintf(stderr, "Could not allocate temporary picture: %s\n",
278                    av_err2str(ret));
279            exit(1);
280        }
281    }
282
283    /* copy data and linesize picture pointers to frame */
284    *((AVPicture *)frame) = dst_picture;
285}
286
287static void close_video(AVFormatContext *oc, AVStream *st)
288{
289    avcodec_close(st->codec);
290        avpicture_free( &src_picture ); //av_free(src_picture.data[0]);
291        avpicture_free( &dst_picture ); //av_free(dst_picture.data[0]);
292    avcodec_free_frame(&frame);         //av_free(frame);
293}
294
295/* Prepare a 16 bit dummy audio frame of 'frame_size' samples and
296 * 'nb_channels' channels. */
297static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels)
298{
299    int j, i, v;
300    int16_t *q;
301
302    q = samples;
303    for (j = 0; j < frame_size; j++) {
304        v = (int)(sin(t) * 10000);
305        for (i = 0; i < nb_channels; i++)
306            *q++ = v;
307        t     += tincr;
308        tincr += tincr2;
309    }
310}
311
312static void write_audio_frame(AVFormatContext *oc, AVStream *st)
313{
314    AVCodecContext *c;
315    AVPacket pkt = { 0 }; // data and size must be 0;
316    AVFrame *frame = avcodec_alloc_frame();
317    int got_packet, ret;
318
319    av_init_packet(&pkt);
320    c = st->codec;
321
322    get_audio_frame(samples, audio_input_frame_size, c->channels);
323    frame->nb_samples = audio_input_frame_size;
324    avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
325                             (uint8_t *)samples,
326                             audio_input_frame_size *
327                             av_get_bytes_per_sample(c->sample_fmt) *
328                             c->channels, 1);
329
330    ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
331    if (ret < 0) {
332        fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
333        exit(1);
334    }
335
336    if (!got_packet)
337        return;
338
339    pkt.stream_index = st->index;
340
341    /* Write the compressed frame to the media file. */
342    ret = av_interleaved_write_frame(oc, &pkt);
343    if (ret != 0) {
344        fprintf(stderr, "Error while writing audio frame: %s\n",
345                av_err2str(ret));
346        exit(1);
347    }
348    avcodec_free_frame(&frame);
349}
350
351/* Prepare a dummy image. */
352static void fill_yuv_image(AVPicture *pict, int frame_index, int width, int height)
353{
354    int x, y, i;
355
356    i = frame_index;
357
358    /* Y */
359    for (y = 0; y < height; y++)
360        for (x = 0; x < width; x++)
361            pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
362
363    /* Cb and Cr */
364    for (y = 0; y < height / 2; y++) {
365        for (x = 0; x < width / 2; x++) {
366            pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
367            pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
368        }
369    }
370}
371
372static void write_video_frame(AVFormatContext *oc, AVStream *st)
373{
374    int ret;
375    static struct SwsContext *sws_ctx;
376    AVCodecContext *c = st->codec;
377
378    if (frame_count >= STREAM_NB_FRAMES) {
379        /* No more frames to compress. The codec has a latency of a few
380         * frames if using B-frames, so we get the last frames by
381         * passing the same picture again. */
382    } else {
383        if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
384            /* as we only generate a YUV420P picture, we must convert it
385             * to the codec pixel format if needed */
386            if (!sws_ctx) {
387                sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
388                                         c->width, c->height, c->pix_fmt,
389                                         sws_flags, NULL, NULL, NULL);
390                if (!sws_ctx) {
391                    fprintf(stderr,
392                            "Could not initialize the conversion context\n");
393                    exit(1);
394                }
395            }
396            fill_yuv_image(&src_picture, frame_count, c->width, c->height);
397            sws_scale(sws_ctx,
398                      (const uint8_t * const *)src_picture.data, src_picture.linesize,
399                      0, c->height, dst_picture.data, dst_picture.linesize);
400        } else {
401            fill_yuv_image(&dst_picture, frame_count, c->width, c->height);
402        }
403    }
404
405    if (oc->oformat->flags & AVFMT_RAWPICTURE) {
406        /* Raw video case - directly store the picture in the packet */
407        AVPacket pkt;
408        av_init_packet(&pkt);
409
410        pkt.flags        |= AV_PKT_FLAG_KEY;
411        pkt.stream_index  = st->index;
412        pkt.data          = dst_picture.data[0];
413        pkt.size          = sizeof(AVPicture);
414
415        ret = av_interleaved_write_frame(oc, &pkt);
416    } else {
417        /* encode the image */
418        AVPacket pkt;
419        int got_output;
420
421        av_init_packet(&pkt);
422        pkt.data = NULL;    // packet data will be allocated by the encoder
423        pkt.size = 0;
424
425        ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
426        if (ret < 0) {
427            fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
428            exit(1);
429        }
430
431        /* If size is zero, it means the image was buffered. */
432        if (got_output) {
433            if (c->coded_frame->key_frame)
434                pkt.flags |= AV_PKT_FLAG_KEY;
435
436            pkt.stream_index = st->index;
437
438            /* Write the compressed frame to the media file. */
439            ret = av_interleaved_write_frame(oc, &pkt);
440        } else {
441            ret = 0;
442        }
443    }
444    if (ret != 0) {
445        fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
446        exit(1);
447    }
448    frame_count++;
449}
450
451class CTest
452{
453private:
454        AVOutputFormat*                         fmt;
455    AVFormatContext*                    oc;
456    AVStream*                                   audio_st;
457        AVStream*                                       video_st;
458    AVCodec*                                    audio_codec;
459        AVCodec*                                        video_codec;
460
461        double                                          audio_pts, video_pts;
462
463public:
464        CTest( void ) : fmt( NULL ), oc( NULL ), audio_st( NULL ), video_st( NULL ), audio_codec( NULL ), video_codec( NULL )
465                ,audio_pts( 0.0 ), video_pts( 0.0 )
466        {
467        }
468
469        virtual ~CTest( void )
470        {
471
472        }
473
474        bool Open( std::string strFileName )
475        {
476                const char* pszFileName = strFileName.c_str();
477                int iRet;
478
479                /* allocate the output media context */
480                avformat_alloc_output_context2(&oc, NULL, NULL, pszFileName);
481                if (!oc) {
482                        printf("Could not deduce output format from file extension: using MPEG.\n");
483                        avformat_alloc_output_context2(&oc, NULL, "mpeg", pszFileName);
484                }
485                if (!oc) {
486                        return false;
487                }
488                fmt = oc->oformat;
489
490                /* Add the audio and video streams using the default format codecs and initialize the codecs. */
491                video_st = NULL;
492                audio_st = NULL;
493
494                if (fmt->video_codec != AV_CODEC_ID_NONE) {
495                        video_st = add_stream(&oc, &video_codec, fmt->video_codec);
496                }
497                if (fmt->audio_codec != AV_CODEC_ID_NONE) {
498                        audio_st = add_stream(&oc, &audio_codec, fmt->audio_codec);
499                }
500
501                /* Now that all the parameters are set, we can open the audio and
502                 * video codecs and allocate the necessary encode buffers. */
503                if (video_st)
504                        open_video(oc, video_codec, video_st);
505                if (audio_st)
506                        open_audio(oc, audio_codec, audio_st);
507
508                av_dump_format(oc, 0, pszFileName, 1);
509
510                // open the output file, if needed
511                if (!(fmt->flags & AVFMT_NOFILE)) {
512                        iRet = avio_open(&oc->pb, pszFileName, AVIO_FLAG_WRITE);
513                        if (iRet < 0) {
514                                fprintf(stderr, "Could not open '%s': %s\n", pszFileName, av_err2str(iRet));
515                                return false;
516                        }
517                }
518
519                // Write the stream header, if any.
520                iRet = avformat_write_header(oc, NULL);
521                if (iRet < 0) {
522                        fprintf(stderr, "Error occurred when opening output file: %s\n", av_err2str(iRet));
523                        return false;
524                }
525
526                return true;
527        }
528
529        void Close( void )
530        {
531                /* Write the trailer, if any. The trailer must be written before you
532                 * close the CodecContexts open when you wrote the header; otherwise
533                 * av_write_trailer() may try to use memory that was freed on
534                 * av_codec_close(). */
535                av_write_trailer(oc);
536
537                /* Close each codec. */
538                if (video_st)
539                        close_video(oc, video_st);
540                if (audio_st)
541                        close_audio(oc, audio_st);
542
543                /* Free the streams. */
544                for (unsigned int i = 0; i < oc->nb_streams; i++) {
545                        av_freep(&oc->streams[i]->codec);
546                        av_freep(&oc->streams[i]);
547
548                }
549
550                if (!(fmt->flags & AVFMT_NOFILE))
551                        /* Close the output file. */
552                        avio_close(oc->pb);
553
554                /* free the stream */
555                av_free(oc);
556               
557        }
558
559        void Write( void )
560        {
561                if (frame)
562                        frame->pts = 0;
563                for (;;) {
564                        /* Compute current audio and video time. */
565                        if (audio_st)
566                                audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
567                        else
568                                audio_pts = 0.0;
569
570                        if (video_st)
571                                video_pts = (double)video_st->pts.val * video_st->time_base.num /
572                                                        video_st->time_base.den;
573                        else
574                                video_pts = 0.0;
575
576                        if ((!audio_st || audio_pts >= STREAM_DURATION) &&
577                                (!video_st || video_pts >= STREAM_DURATION))
578                                break;
579
580                        /* write interleaved audio and video frames */
581                        if (!video_st || (video_st && audio_st && audio_pts < video_pts)) {
582                                write_audio_frame(oc, audio_st);
583                        } else {
584                                write_video_frame(oc, video_st);
585                                frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
586                        }
587                }
588        }
589};
590
591int main(int argc, char **argv)
592{
593        _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);          // Memory Leak Check
594        //_CrtSetBreakAlloc( 143 );
595
596        av_register_all();                      // Initialize libavcodec, and register all codecs and formats.
597       
598        const char *filename = "Muxing.mpg";
599        const DWORD dwLimitedTick = 30000;
600
601        char szEnd;
602        bool bIsOpen;
603        CTest test;
604        DWORD dwStartTick, dwEndTick, dwDiffTick;
605       
606        dwStartTick = GetTickCount();
607       
608        do
609        {
610                bIsOpen = test.Open( filename );
611   
612                if ( bIsOpen != false )
613                {
614                        test.Write();
615                        test.Close();
616                }
617
618                dwEndTick = GetTickCount();
619                dwDiffTick = dwEndTick - dwStartTick;
620
621                Sleep( 1 );
622
623        } while ( dwDiffTick < dwLimitedTick );
624
625        std::cout << "press any key to exit." << std::endl;
626        std::cin >> szEnd;
627       
628    return 0;
629}