source: trunk/src/objects/RpVideo.c @ 1916

Last change on this file since 1916 was 1916, checked in by dkearney, 11 years ago

switching from RpMediaPlayer? to RpVideo? code for the video viewer widget. changed flowdial widget so the dial moved as needed for the video widget.

File size: 40.2 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  TkFFMPEG:  video
4 *
5 *  These routines support the methods in the "video" class, which is
6 *  a video stream that can be read from or written to.  The class
7 *  itself is defined in itcl, but when methods are called, execution
8 *  jumps down to this level.
9 * ======================================================================
10 *  AUTHOR:  Michael McLennan, Purdue University
11 *  Copyright (c) 2004-2008  Purdue Research Foundation
12 *
13 *  See the file "license.terms" for information on usage and
14 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * ======================================================================
16 */
17
18#include <stdlib.h>
19#include <string.h>
20
21#include <libavcodec/avcodec.h>
22#include <libavformat/avformat.h>
23#include <libswscale/swscale.h>
24
25#include "RpVideo.h"
26
27/*
28 * Each video object is represented by the following data:
29 */
30struct VideoObjRec {
31    int magic;
32
33    /* video input */
34    AVFormatContext *pFormatCtx;
35    int videoStream;
36    int frameNumber;
37    int atEnd;
38
39    /* video output */
40    AVFormatContext *outFormatCtx;
41    AVStream *outVideoStr;
42
43    /* used for both input/output */
44    AVFrame *pFrameYUV;
45    uint8_t *yuvbuffer;
46    int yuvw, yuvh;
47    AVFrame *pFrameRGB;
48    uint8_t *rgbbuffer;
49    int rgbw, rgbh;
50    struct SwsContext *scalingCtx;
51
52    char *fileName;
53    char mode[64];
54    char fmt[64];
55
56    /* tmp buffer to give images back to user */
57    void *img;
58    int imgHeaderLen;
59    int imgWidth;
60    int imgHeight;
61};
62
63/* magic stamp for VideoObj, to make sure data is valid */
64#define VIDEO_OBJ_MAGIC 0x0102abcd
65
66static VideoObj *VideoSetData ();
67
68static int VideoModeRead (VideoObj *vidPtr);
69// static int VideoModeWrite (Tcl_Interp *interp, int w, int h);
70
71static int VideoTime2Frame (AVStream *streamPtr, int64_t tval);
72static int64_t VideoFrame2Time (AVStream *streamPtr, int fval);
73static void VideoNextFrame (VideoObj *vidPtr);
74
75uint64_t global_video_pkt_pts = AV_NOPTS_VALUE;
76static int VideoAvGetBuffer (struct AVCodecContext *c, AVFrame *fr);
77static void VideoAvReleaseBuffer (struct AVCodecContext *c, AVFrame *fr);
78static int VideoWriteFrame (VideoObj *vidPtr, AVFrame *framePtr);
79
80static int VideoAllocImgBuffer (VideoObj *vidPtr, int width, int height);
81static int VideoFreeImgBuffer (VideoObj *vidPtr);
82static double VideoTransformFrames2Duration (VideoObj *vidPtr, int frame);
83static int VideoTransformDuration2Frames (VideoObj *vidPtr, double duration);
84
85/*
86 * ------------------------------------------------------------------------
87 *  VideoSetData()
88 *
89 *  Saves VideoObj data in the "_videodata" slot in the current object
90 *  context.  The data can be retrieved later by calling VideoGetData().
91 * ------------------------------------------------------------------------
92 */
93VideoObj *
94VideoSetData()
95{
96    VideoObj* vid = NULL;
97
98    vid = malloc(sizeof(VideoObj));
99
100    if (vid == NULL) {
101        return NULL;
102    }
103
104    vid->magic = VIDEO_OBJ_MAGIC;
105    vid->pFormatCtx = NULL;
106    vid->videoStream = 0;
107    vid->frameNumber = -1;
108    vid->atEnd = 0;
109
110    vid->outFormatCtx = NULL;
111    vid->outVideoStr = NULL;
112
113    vid->pFrameYUV = NULL;
114    vid->yuvbuffer = NULL;
115    vid->yuvw = 0;
116    vid->yuvh = 0;
117    vid->pFrameRGB = NULL;
118    vid->rgbbuffer = NULL;
119    vid->rgbw = 0;
120    vid->rgbh = 0;
121    vid->scalingCtx = NULL;
122
123    vid->fileName = NULL;
124    *vid->mode = '\0';
125    *vid->fmt = '\0';
126
127    vid->img = NULL;
128    vid->imgHeaderLen = 0;
129    vid->imgWidth = 0;
130    vid->imgHeight = 0;
131
132    return vid;
133}
134
135int
136VideoOpenFile(vidPtr, fileName, mode)
137    VideoObj *vidPtr;
138    const char *fileName;
139    const char *mode;
140{
141    int fnlen = 0;
142
143    if (fileName == NULL) {
144        // missing value for fileName
145        // return TCL_ERROR;
146        return -1;
147    }
148    if (fileName == '\0') {
149        /* no file name set -- do nothing */
150        return 0;
151    }
152
153    fnlen = strlen(fileName);
154    if (vidPtr->fileName != NULL) {
155        free(vidPtr->fileName);
156    }
157    vidPtr->fileName = (char *) malloc((fnlen+1)*sizeof(char));
158    if (vidPtr->fileName == NULL) {
159        // trouble mallocing space
160        return -1;
161    }
162    strncpy(vidPtr->fileName,fileName,fnlen);
163
164    // FIXME: remove this constraint when we support
165    // the modes: r, r+, w, w+, a, a+, b and combinations
166    if (strlen(mode) > 1) {
167        return -1;
168    }
169
170    if (*mode == 'r') {
171        /* we're now in "input" mode */
172        VideoModeRead(vidPtr);
173    } else if (*mode == 'w') {
174        /* we're now in "input" mode */
175        // VideoModeWrite(vidPtr);
176    } else {
177        // unrecognized mode
178        return -1;
179    }
180
181    return 0;
182}
183
184/*
185 * ------------------------------------------------------------------------
186 *  VideoModeRead()
187 *
188 *  Tries to force this video stream into "read" mode.  If the current
189 *  mode is "", then the -file is opened for reading.  If the current
190 *  mode is "write", then the stream is closed and then opened for
191 *  reading.  If the current mode is "read", then this call does nothing.
192 *  Usually called just before a "read" operation (get, go, etc.) is
193 *  performed.
194 *
195 *  Returns TCL_OK if successful, and TCL_ERROR if there is a problem
196 *  opening or closing the stream.
197 * ------------------------------------------------------------------------
198 */
199int
200VideoModeRead(vidPtr)
201    VideoObj *vidPtr;
202{
203    char c, buffer[64]; int i;
204    const char *fmt;
205    AVCodecContext *vcodecCtx;
206    AVCodec *vcodec;
207
208    if (vidPtr == NULL) {
209        return -1;
210    }
211
212    if (vidPtr->fileName == NULL) {
213        // Tcl_AppendResult(interp, "missing value for -file", (char*)NULL);
214        // return TCL_ERROR;
215        return -1;
216    }
217    if (*vidPtr->fileName == '\0') {
218        /* no file name set -- do nothing */
219        return 0;
220    }
221
222    if (strcmp(vidPtr->mode,"input") == 0) {
223        return 0;
224    } else if (strcmp(vidPtr->mode,"output") == 0) {
225        if (VideoClose(vidPtr) != 0) {
226            return -1;
227        }
228    }
229
230    /*
231     * Open the video stream from that file.
232     */
233    if (av_open_input_file(&vidPtr->pFormatCtx, vidPtr->fileName,
234            NULL, 0, NULL) != 0) {
235        // Tcl_AppendResult(interp, "couldn't open file \"",
236        //     fileName, "\"", (char*)NULL);
237        // return TCL_ERROR;
238        return -1;
239    }
240    if (av_find_stream_info(vidPtr->pFormatCtx) < 0) {
241        // Tcl_AppendResult(interp, "couldn't find streams in file \"",
242        //     fileName, "\"", (char*)NULL);
243        // return TCL_ERROR;
244        return -1;
245    }
246
247    /*
248     * Search for a video stream and its codec.
249     */
250    vidPtr->videoStream = -1;
251    for (i=0; i < vidPtr->pFormatCtx->nb_streams; i++) {
252        if (vidPtr->pFormatCtx->streams[i]->codec->codec_type
253                == CODEC_TYPE_VIDEO) {
254            vidPtr->videoStream = i;
255            break;
256        }
257    }
258    if (vidPtr->videoStream < 0) {
259        // Tcl_AppendResult(interp, "couldn't find video stream in file \"",
260        //     fileName, "\"", (char*)NULL);
261        // return TCL_ERROR;
262        return -1;
263    }
264
265    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
266    vcodec = avcodec_find_decoder(vcodecCtx->codec_id);
267    if (vcodec == NULL) {
268        // Tcl_AppendResult(interp, "unsupported codec for file \"",
269        //     fileName, "\"", (char*)NULL);
270        // return TCL_ERROR;
271        return -1;
272    }
273    if (avcodec_open(vcodecCtx, vcodec) < 0) {
274        // Tcl_AppendResult(interp, "couldn't open codec for file \"",
275        //     fileName, "\"", (char*)NULL);
276        // return TCL_ERROR;
277        return -1;
278    }
279
280    vcodecCtx->get_buffer = VideoAvGetBuffer;
281    vcodecCtx->release_buffer = VideoAvReleaseBuffer;
282
283    vidPtr->pFrameYUV = avcodec_alloc_frame();
284    vidPtr->pFrameRGB = avcodec_alloc_frame();
285    if (vidPtr->pFrameYUV == NULL || vidPtr->pFrameRGB == NULL) {
286        // Tcl_AppendResult(interp, "couldn't allocate frame space",
287        //     " for file \"", fileName, "\"", (char*)NULL);
288        // return TCL_ERROR;
289        return -1;
290    }
291
292    /* save the name of the codec as the -format option */
293    fmt = "?";
294    if (vcodecCtx->codec && vcodecCtx->codec->name) {
295        fmt = vcodecCtx->codec->name;
296        strcpy(vidPtr->fmt,fmt);
297    }
298//
299//    sprintf(buffer, "%d", vcodecCtx->width);
300//    if (Tcl_SetVar(interp, "width", buffer, TCL_LEAVE_ERR_MSG) == NULL) {
301//        return TCL_ERROR;
302//    }
303//    sprintf(buffer, "%d", vcodecCtx->height);
304//    if (Tcl_SetVar(interp, "height", buffer, TCL_LEAVE_ERR_MSG) == NULL) {
305//        return TCL_ERROR;
306//    }
307//
308
309    if (strcpy(vidPtr->mode,"input") == NULL) {
310        return -1;
311    }
312
313    return 0;
314}
315
316
317// FIXME: get this function working.
318///*
319// * ------------------------------------------------------------------------
320// *  VideoModeWrite()
321// *
322// *  Tries to force this video stream into "write" mode.  If the current
323// *  mode is "", then the -file is opened for writing.  If the current
324// *  mode is "read", then the stream is closed and then opened for
325// *  writing.  If the current mode is "write", then this call does nothing.
326// *  Usually called just before a "write" operation (put, etc.) is
327// *  performed.
328// *
329// *  Returns TCL_OK if successful, and TCL_ERROR if there is a problem
330// *  opening or closing the stream.
331// * ------------------------------------------------------------------------
332// */
333//int
334//VideoModeWrite(vidPtr, fileName, width, height, fmt)
335//    VideoObj *vidPtr;      /* video object to write */
336//    CONST84 char *fileName;
337//    int width;             /* native width of each frame */
338//    int height;            /* native height of each frame */
339//    CONST84 char *fmt
340//{
341//    char c;
342//    int numBytes, pixfmt, iwd, iht;
343//    CONST84 char *size;
344//    AVCodecContext *codecCtx;
345//    AVCodec *vcodec;
346//
347//    if (vidPtr == NULL) {
348//        return -1;
349//    }
350//
351//    /*
352//     * Get the current mode.  If we're already in "output", then we're
353//     * done.  Otherwise, close the stream if necessary and prepare to
354//     * open the file for write.
355//     */
356//    if (vidPtr->mode == NULL) {
357//        return -1;
358//    }
359//
360//    c = *vidPtr->mode;
361//    if (c == 'o' && strcmp(vidPtr->mode,"output") == 0) {
362//        return 0;
363//    }
364//    else if (c == 'i' && strcmp(vidPtr->mode,"input") == 0) {
365//        if (VideoClose(vidPtr) != 0) {
366//            return -1;
367//        }
368//    }
369//
370//    /*
371//     * Get the file name from the -file variable.
372//     */
373//    if ((fileName == NULL) || (*filename == '\0')) {
374//        /* no file name set -- do nothing */
375//        return 0;
376//    }
377//
378//    /*
379//     * Get the -width and -height of each frame.  If these are set
380//     * to 0 (default), then use the incoming width/height from an
381//     * actual frame.
382//     */
383//     iwd = width;
384//     iht = height;
385//
386//    /*
387//     * Get the format argument.
388//     */
389//    if (fmt == NULL) {
390////        Tcl_AppendResult(interp, "missing value for -format", (char*)NULL);
391////        return TCL_ERROR;
392//        return -1;
393//    }
394//    if (strcmp(fmt,"mpeg1video") == 0) {
395//        vidPtr->outFormatCtx = av_alloc_format_context();
396//        vidPtr->outFormatCtx->oformat = guess_format("mpeg", NULL, NULL);
397//    }
398//    else if (strcmp(fmt,"flv") == 0) {
399//        vidPtr->outFormatCtx = av_alloc_format_context();
400//        vidPtr->outFormatCtx->oformat = guess_format("flv", NULL, NULL);
401//    }
402//    else if (strcmp(fmt,"mov") == 0) {
403//        vidPtr->outFormatCtx = av_alloc_format_context();
404//        vidPtr->outFormatCtx->oformat = guess_format("mov", NULL, NULL);
405//        /* MOV normally uses MPEG4, but that may not be installed */
406//        vidPtr->outFormatCtx->oformat->video_codec = CODEC_ID_FFV1;
407//    }
408//    else if (strcmp(fmt,"avi") == 0) {
409//        vidPtr->outFormatCtx = av_alloc_format_context();
410//        vidPtr->outFormatCtx->oformat = guess_format("avi", NULL, NULL);
411//        /* AVI normally uses MPEG4, but that may not be installed */
412//        vidPtr->outFormatCtx->oformat->video_codec = CODEC_ID_FFV1;
413//    }
414//    else {
415////        Tcl_AppendResult(interp, "bad format \"", fmt, "\": should be",
416////            " avi, flv, mpeg1video, mov", (char*)NULL);
417////        return TCL_ERROR;
418//        return -1;
419//    }
420//
421//    /*
422//     * Open the video stream for writing.
423//     */
424//    strncpy(vidPtr->outFormatCtx->filename, fileName,
425//        sizeof(vidPtr->outFormatCtx->filename));
426//
427//    vidPtr->outVideoStr = av_new_stream(vidPtr->outFormatCtx, 0);
428//    if (vidPtr->outVideoStr == NULL) {
429////        Tcl_AppendResult(interp, "internal error:",
430////            " problem opening stream", (char*)NULL);
431////        return TCL_ERROR;
432//        retunr -1;
433//    }
434//    codecCtx = vidPtr->outVideoStr->codec;
435//
436//    codecCtx->codec_id = vidPtr->outFormatCtx->oformat->video_codec;
437//    codecCtx->codec_type = CODEC_TYPE_VIDEO;
438//
439//    /* put sample parameters */
440//    codecCtx->bit_rate = 400000;
441//    /* resolution must be a multiple of two */
442//    codecCtx->width = (iwd/2)*2;
443//    codecCtx->height = (iht/2)*2;
444//    codecCtx->time_base.den = 24;
445//    codecCtx->time_base.num = 1;
446//    codecCtx->gop_size = 12; /* emit one intra frame every so often */
447//    codecCtx->pix_fmt = PIX_FMT_YUV420P;
448//    if (codecCtx->codec_id == CODEC_ID_MPEG2VIDEO) {
449//        codecCtx->max_b_frames = 2;
450//    }
451//
452//    /* find the video encoder */
453//    vcodec = avcodec_find_encoder(codecCtx->codec_id);
454//    if (!vcodec || avcodec_open(codecCtx, vcodec) < 0) {
455//        // Tcl_AppendResult(interp, "internal error:",
456//        //     " problem opening codec", (char*)NULL);
457//        // return TCL_ERROR;
458//        return -1;
459//    }
460//
461//    if (av_set_parameters(vidPtr->outFormatCtx, NULL) < 0) {
462//        // Tcl_AppendResult(interp, "internal error:",
463//        //     " problem in av_set_parameters()", (char*)NULL);
464//        // return TCL_ERROR;
465//        return -1;
466//    }
467//
468//    if (url_fopen(&vidPtr->outFormatCtx->pb, fileName, URL_WRONLY) < 0) {
469//        // Tcl_AppendResult(interp, "can't open file \"", fileName,
470//        //     "\"", (char*)NULL);
471//        // return TCL_ERROR;
472//        return -1;
473//    }
474//    av_write_header(vidPtr->outFormatCtx);
475//
476//    vidPtr->pFrameYUV = avcodec_alloc_frame();
477//    vidPtr->pFrameRGB = avcodec_alloc_frame();
478//    if (vidPtr->pFrameYUV == NULL || vidPtr->pFrameRGB == NULL) {
479//        // Tcl_AppendResult(interp, "couldn't allocate frame space",
480//        //     " for file \"", fileName, "\"", (char*)NULL);
481//        // return TCL_ERROR;
482//        return -1;
483//    }
484//
485//    vidPtr->yuvw = vidPtr->outVideoStr->codec->width;
486//    vidPtr->yuvh = vidPtr->outVideoStr->codec->height;
487//    pixfmt = vidPtr->outVideoStr->codec->pix_fmt;
488//
489//    numBytes = avpicture_get_size(pixfmt, vidPtr->yuvw, vidPtr->yuvh);
490//    vidPtr->yuvbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
491//
492//    avpicture_fill((AVPicture*)vidPtr->pFrameYUV, vidPtr->yuvbuffer,
493//        pixfmt, vidPtr->yuvw, vidPtr->yuvh);
494//
495//
496//    if (strcpy(vid->mode,"output") == NULL) {
497//        return -1;
498//    }
499//
500//    return 0;
501//}
502
503
504/*
505 * ------------------------------------------------------------------------
506 *  VideoTime2Frame()
507 *
508 *  Converts a time value (as defined by the FFMPEG package) into an
509 *  integer frame number in the range 0-end for the stream.
510 * ------------------------------------------------------------------------
511 */
512int
513VideoTime2Frame(streamPtr, tval)
514    AVStream *streamPtr;   /* scale values according to this stream */
515    int64_t tval;          /* time value as defined by stream */
516{
517    AVRational one, factor;
518    one.num = 1;
519    one.den = 1;
520    factor.num = streamPtr->time_base.num * streamPtr->r_frame_rate.num;
521    factor.den = streamPtr->time_base.den * streamPtr->r_frame_rate.den;
522
523    if (tval > streamPtr->start_time) {
524        tval -= streamPtr->start_time;
525    } else {
526        tval = 0;
527    }
528    tval = av_rescale_q(tval, factor, one);
529    return (int)tval;
530}
531
532/*
533 * ------------------------------------------------------------------------
534 *  VideoFrame2Time()
535 *
536 *  Converts a frame number 0-end to the corresponding time value
537 *  (as defined by FFMPEG) for the given stream.
538 * ------------------------------------------------------------------------
539 */
540int64_t
541VideoFrame2Time(streamPtr, fval)
542    AVStream *streamPtr;   /* scale values according to this stream */
543    int fval;              /* frame value in the range 0-end */
544{
545    int64_t tval;
546    AVRational one, factor;
547    one.num = 1;
548    one.den = 1;
549
550    factor.num = streamPtr->time_base.num * streamPtr->r_frame_rate.num;
551    factor.den = streamPtr->time_base.den * streamPtr->r_frame_rate.den;
552
553    tval = av_rescale_q((int64_t)fval, one, factor) + streamPtr->start_time;
554    return tval;
555}
556
557/*
558 * ------------------------------------------------------------------------
559 *  VideoNextFrame()
560 *
561 *  Decodes a series of video packets until the end of the frame
562 *  is reached.  Updates the frameNumber and atEnd to maintain the
563 *  current status for this video stream.
564 * ------------------------------------------------------------------------
565 */
566void
567VideoNextFrame(vidPtr)
568    VideoObj *vidPtr;   /* get a frame from this video stream */
569{
570    int frameFinished;
571    uint64_t pts;
572    AVCodecContext *vcodecCtx;
573    AVStream *vstreamPtr;
574    AVPacket packet;
575
576    if (vidPtr->pFormatCtx) {
577        vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
578        vcodecCtx = vstreamPtr->codec;
579
580        /*
581         * Decode as many packets as necessary to get the next frame.
582         */
583        pts = 0;
584        while (1) {
585            if (av_read_frame(vidPtr->pFormatCtx, &packet) >= 0) {
586                if (packet.stream_index == vidPtr->videoStream) {
587                    /* save pts so we can grab it again in VideoAvGetBuffer */
588                    global_video_pkt_pts = packet.pts;
589
590                    // avcodec_decode_video(vcodecCtx, vidPtr->pFrameYUV,
591                    //     &frameFinished, packet.data, packet.size);
592
593                    // packet.flags = PKT_FLAG_KEY;
594                    avcodec_decode_video2(vcodecCtx, vidPtr->pFrameYUV,
595                        &frameFinished, &packet);
596
597                    // avcodec_decode_video2(_pCodecCtx, _pFrame, &frameFinished,
598                    //                                   &_packet);
599
600                    if (packet.dts == AV_NOPTS_VALUE
601                          && vidPtr->pFrameYUV->opaque
602                          && *(uint64_t*)vidPtr->pFrameYUV->opaque != AV_NOPTS_VALUE) {
603                        pts = *(uint64_t*)vidPtr->pFrameYUV->opaque;
604                    } else if (packet.dts != AV_NOPTS_VALUE) {
605                        pts = packet.dts;
606                    } else {
607                        pts = 0;
608                    }
609
610                    if (frameFinished) {
611                        vidPtr->frameNumber = VideoTime2Frame(vstreamPtr, pts);
612                        break;
613                    }
614                }
615            } else {
616                vidPtr->atEnd = 1;
617                break;
618            }
619        }
620        av_free_packet(&packet);
621    }
622}
623
624/*
625 * ------------------------------------------------------------------------
626 *  These two routines are called whenever a frame buffer is allocated,
627 *  which means that we're starting a new frame.  Grab the global pts
628 *  counter and squirrel it away in the opaque slot of the frame.  This
629 *  will give us a pts value that we can trust later.
630 * ------------------------------------------------------------------------
631 */
632int
633VideoAvGetBuffer(c,fr)
634    AVCodecContext *c;  /* codec doing the frame decoding */
635    AVFrame *fr;        /* frame being decoded */
636{
637    int rval = avcodec_default_get_buffer(c, fr);
638    uint64_t *ptsPtr = av_malloc(sizeof(uint64_t));
639    *ptsPtr = global_video_pkt_pts;
640    fr->opaque = ptsPtr;
641    return rval;
642}
643
644void
645VideoAvReleaseBuffer(c,fr)
646    AVCodecContext *c;  /* codec doing the frame decoding */
647    AVFrame *fr;        /* frame being decoded */
648{
649    if (fr && fr->opaque) {
650        av_freep(&fr->opaque);
651    }
652    avcodec_default_release_buffer(c,fr);
653}
654
655/*
656 * ------------------------------------------------------------------------
657 *  VideoInitCmd()
658 *
659 *  Implements the body of the _ffmpeg_init method in the "video" class.
660 *  Initializes the basic data structure and stores it in the _videodata
661 *  variable within the class.
662 * ------------------------------------------------------------------------
663 */
664VideoObj *
665VideoInitCmd()
666{
667    /*
668     * Create an object to represent this video stream.
669     */
670
671    /* Register all codecs and formats */
672    av_register_all();
673
674    return VideoSetData();
675}
676
677/*
678 * ------------------------------------------------------------------------
679 *  VideoCleanupCmd()
680 *
681 *  Implements the body of the _ffmpeg_cleanup method in the "video" class.
682 *  Accesses the data structure stored in the _videodata variable and
683 *  frees up the data.
684 * ------------------------------------------------------------------------
685 */
686int
687VideoCleanupCmd(vidPtr)
688    VideoObj *vidPtr;
689{
690    /*
691     *  Nothing much to do here.  Just close the file in case it is
692     *  still open.  Don't free vidPtr itself; that is cleaned up by
693     *  the ByteArrayObj in the class data member.
694     */
695    int ret = 0;
696
697    ret -= VideoClose(vidPtr);
698
699    if (vidPtr != NULL) {
700        VideoFreeImgBuffer(vidPtr);
701        if (vidPtr->fileName != NULL) {
702            free(vidPtr->fileName);
703            vidPtr->fileName = NULL;
704        }
705        free(vidPtr);
706        vidPtr = NULL;
707// FIXME: need a test to make sure vidPtr is null after the function returns.
708    }
709
710    return ret;
711}
712
713/*
714 * ------------------------------------------------------------------------
715 *  VideoSizeCmd()
716 *
717 *  Implements the body of the "size" method in the "video" class.
718 *  Returns the size of each frame in this video stream as a list {w h}.
719 * ------------------------------------------------------------------------
720 */
721int
722VideoSizeCmd(vidPtr, width, height)
723    VideoObj *vidPtr;
724    int *width;
725    int *height;
726{
727    AVCodecContext *vcodecCtx;
728
729    if (vidPtr == NULL) {
730        return -1;
731    }
732    if (vidPtr->pFormatCtx) {
733        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
734        if (width != NULL) {
735            *width = vcodecCtx->width;
736        }
737        if (height != NULL) {
738            *height = vcodecCtx->height;
739        }
740    }
741    return 0;
742}
743
744/*
745 * ------------------------------------------------------------------------
746 *  VideoGoCmd()
747 *
748 *  Implements the body of the "go" method in the "video" class.
749 *  Advances by one or more frames, or seeks backward in the stream.
750 *  Handles the following syntax:
751 *    obj go next ...... go to next frame (same as +1)
752 *    obj go +n ........ advance by n frames
753 *    obj go -n ........ go back by n frames
754 *    obj go n ......... go to frame n
755 * ------------------------------------------------------------------------
756 */
757int
758VideoGoNext(vidPtr)
759    VideoObj *vidPtr;
760{
761    return VideoGoPlusMinusN(vidPtr,1);
762}
763
764int
765VideoGoPlusMinusN(vidPtr, n)
766    VideoObj *vidPtr;
767    int n;
768{
769    int nabs;
770
771    if (vidPtr == NULL) {
772        return -1;
773    }
774
775    if (vidPtr->pFormatCtx == NULL) {
776        // "internal error: video stream is not open",
777        return -1;
778    }
779
780    nabs = vidPtr->frameNumber + n;
781    return VideoGoToN(vidPtr, nabs);
782}
783
784int
785VideoGoToN(vidPtr, n)
786    VideoObj *vidPtr;
787    int n;
788{
789    int nrel, nabs, seekFlags, gotframe;
790    int64_t nseek;
791    AVCodecContext *vcodecCtx;
792    AVStream *vstreamPtr;
793
794    if (vidPtr == NULL) {
795        return -1;
796    }
797
798    if (vidPtr->pFormatCtx == NULL) {
799        // "internal error: video stream is not open",
800        return -1;
801    }
802    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
803
804    nabs = n;
805
806    if (nabs < 0) {
807        nabs = 0;
808    }
809
810    if (nabs < vidPtr->frameNumber) {
811        seekFlags = AVSEEK_FLAG_BACKWARD;
812    } else {
813        seekFlags = 0;
814    }
815
816    /*
817     * If we're going to an absolute frame, or if we're going backward
818     * or too far forward, then seek the frame.
819     */
820    nrel = nabs-vidPtr->frameNumber;
821    if ((nrel > 50) || (seekFlags&AVSEEK_FLAG_BACKWARD)) {
822
823        vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
824        nseek = VideoFrame2Time(vstreamPtr, nabs);
825        if (nseek > 100) {
826            nseek -= 100;
827        } else {
828            nseek = 0;
829        }
830
831        /* first, seek the nearest reference frame for a good starting pt */
832        av_seek_frame(vidPtr->pFormatCtx, vidPtr->videoStream,
833            nseek, seekFlags);
834
835        vidPtr->frameNumber = VideoTime2Frame(vstreamPtr, nseek);
836        vidPtr->atEnd = 0;
837
838        /* then, move forward until we reach the desired frame */
839        gotframe = 0;
840        while (vidPtr->frameNumber < nabs && !vidPtr->atEnd) {
841            VideoNextFrame(vidPtr);
842            gotframe = 1;
843        }
844
845        /* get at least one frame, unless we're done or at the beginning*/
846        if (!gotframe && !vidPtr->atEnd) {
847            VideoNextFrame(vidPtr);
848        }
849    }
850    else {
851        while (nrel-- > 0) {
852            VideoNextFrame(vidPtr);
853        }
854    }
855
856    /*
857     * Send back the current frame number or "end" as the result.
858     */
859    return vidPtr->frameNumber;
860}
861
862/*
863 * ------------------------------------------------------------------------
864 *  VideoGetCmd()
865 *
866 *  Implements the body of the "get" method in the "video" class.
867 *  Returns information about the current frame via the following
868 *  syntax:
869 *    obj get start|position|end
870 *    obj get <imageHandle>
871 * ------------------------------------------------------------------------
872 */
873int
874VideoGetImage(vidPtr, width, height, img, bufSize)
875    VideoObj *vidPtr;
876    int width;
877    int height;
878    void **img;
879    int *bufSize;
880{
881
882    int nframe, iw, ih, numBytes;
883    char c, buffer[64];
884    AVCodecContext *vcodecCtx;
885    AVStream *vstreamPtr;
886
887    if (vidPtr == NULL) {
888        return -1;
889    }
890
891    if (VideoModeRead(vidPtr) != 0) {
892        return -1;
893    }
894
895    if (vidPtr->pFormatCtx) {
896        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
897    } else {
898        vcodecCtx = NULL;
899    }
900
901    /*
902     * Query the size for this photo and make sure that we have a
903     * buffer of the appropriate size for software scaling and
904     * format conversion.
905     */
906
907    iw = width;
908    ih = height;
909
910    if (iw != vidPtr->rgbw || ih != vidPtr->rgbh) {
911        if (vidPtr->rgbbuffer) {
912            av_free(vidPtr->rgbbuffer);
913            vidPtr->rgbbuffer = NULL;
914        }
915        numBytes = avpicture_get_size(PIX_FMT_RGB24, iw, ih);
916        vidPtr->rgbbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
917        vidPtr->rgbw = iw;
918        vidPtr->rgbh = ih;
919
920        avpicture_fill((AVPicture*)vidPtr->pFrameRGB, vidPtr->rgbbuffer,
921            PIX_FMT_RGB24, iw, ih);
922
923        vidPtr->scalingCtx = sws_getCachedContext(vidPtr->scalingCtx,
924            vcodecCtx->width, vcodecCtx->height, vcodecCtx->pix_fmt,
925            iw, ih, PIX_FMT_RGB24, SWS_BICUBIC|SWS_PRINT_INFO, NULL, NULL, NULL);
926    }
927
928    /*
929     * Rescale the current frame to the desired size, and translate
930     * into RGB format so we can copy into the destination image.
931     */
932    if (vidPtr->pFrameYUV && vidPtr->pFrameYUV->data[0]) {
933        sws_scale(vidPtr->scalingCtx, (const uint8_t * const*)
934            vidPtr->pFrameYUV->data, vidPtr->pFrameYUV->linesize,
935            0, vcodecCtx->height,
936            vidPtr->pFrameRGB->data, vidPtr->pFrameRGB->linesize);
937
938/*
939        iblock.pixelPtr  = (unsigned char*)vidPtr->pFrameRGB->data[0];
940        iblock.width     = iw;
941        iblock.height    = ih;
942        iblock.pitch     = vidPtr->pFrameRGB->linesize[0];
943        iblock.pixelSize = 3;
944        iblock.offset[0] = 0;
945        iblock.offset[1] = 1;
946        iblock.offset[2] = 2;
947        iblock.offset[3] = 0;
948
949        Tk_PhotoPutBlock_NoComposite(img, &iblock, 0, 0, iw, ih);
950*/
951
952        int bufsize = 0;
953        if (vidPtr->img == NULL) {
954            VideoAllocImgBuffer(vidPtr,iw,ih);
955        } else {
956            if ((vidPtr->imgWidth != iw) && (vidPtr->imgHeight != ih)) {
957                // new height or width
958                // resize the image buffer
959                free(vidPtr->img);
960                VideoAllocImgBuffer(vidPtr,iw,ih);
961            } else {
962                // image buffer is the correct size
963                // do nothing
964            }
965        }
966
967        // Write pixel data
968        memcpy(vidPtr->img+vidPtr->imgHeaderLen,
969            vidPtr->pFrameRGB->data[0],
970            vidPtr->imgWidth*3*vidPtr->imgHeight);
971    }
972    *img = vidPtr->img;
973    *bufSize = (vidPtr->imgWidth*3*vidPtr->imgHeight) + vidPtr->imgHeaderLen;
974    return 0;
975}
976
977int
978VideoGetFrameRate (vidPtr, fr)
979    VideoObj *vidPtr;
980    double *fr;
981{
982    AVStream *vstreamPtr;
983
984    if (vidPtr == NULL) {
985        return -1;
986    }
987
988    if (fr == NULL) {
989        return -1;
990    }
991
992    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
993
994    // http://trac.handbrake.fr/browser/trunk/libhb/decavcodec.c?rev=1490#L684
995    // there seems to be some controversy over what structure holds
996    // the correct frame rate information for different video codecs.
997    // for now we will use the stream's r_frame_rate.
998    // from the above post, it looks like this value can be interpreted
999    // as frames per second.
1000    *fr = av_q2d(vstreamPtr->r_frame_rate);
1001
1002    return 0;
1003}
1004
1005int
1006VideoAllocImgBuffer(vidPtr, width, height)
1007    VideoObj *vidPtr;
1008    int width;
1009    int height;
1010{
1011
1012    char header[64];
1013    int headerLen = 0;
1014    int bufsize = 0;
1015
1016    sprintf(header,"P6\n%d %d\n255\n", width, height);
1017    headerLen = strlen(header);
1018    bufsize = headerLen + (width*3*height);
1019    vidPtr->img = (void*) malloc(bufsize);
1020    vidPtr->imgHeaderLen = headerLen;
1021    vidPtr->imgWidth = width;
1022    vidPtr->imgHeight = height;
1023    memcpy(vidPtr->img,header,headerLen);
1024
1025    return 0;
1026}
1027
1028int
1029VideoFreeImgBuffer(vidPtr)
1030    VideoObj *vidPtr;
1031{
1032    if ((vidPtr != NULL) && (vidPtr->img != NULL)) {
1033        free(vidPtr->img);
1034        vidPtr->img = NULL;
1035    }
1036    return 0;
1037}
1038
1039int
1040VideoGetPositionCur(vidPtr, pos)
1041    VideoObj *vidPtr;      /* video object to act on */
1042    int *pos;
1043{
1044    int fnum = -1;
1045    AVStream *vstreamPtr;
1046
1047    if (vidPtr == NULL) {
1048        return -1;
1049    }
1050
1051    if (pos == NULL) {
1052        return -1;
1053    }
1054
1055// FIXME: store the name of the file within vidPtr object
1056    if (VideoModeRead(vidPtr) != 0) {
1057        return -1;
1058    }
1059
1060    if (vidPtr->pFormatCtx) {
1061        fnum = vidPtr->frameNumber;
1062    }
1063
1064    *pos = fnum;
1065    return 0;
1066}
1067
1068int
1069VideoGetPositionEnd(vidPtr, pos)
1070    VideoObj *vidPtr;      /* video object to act on */
1071    int *pos;
1072{
1073    int nframe = -1;
1074    AVStream *vstreamPtr;
1075
1076    if (vidPtr == NULL) {
1077        return -1;
1078    }
1079
1080    if (pos == NULL) {
1081        return -1;
1082    }
1083
1084// FIXME: store the name of the file within vidPtr object
1085    if (VideoModeRead(vidPtr) != 0) {
1086        return -1;
1087    }
1088
1089    if (vidPtr->pFormatCtx) {
1090        vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1091        nframe = VideoTime2Frame(vstreamPtr,
1092            vstreamPtr->start_time + vstreamPtr->duration);
1093    }
1094
1095    *pos = nframe;
1096    return 0;
1097}
1098
1099// FIXME: get this function working
1100///*
1101// * ------------------------------------------------------------------------
1102// *  VideoPutCmd()
1103// *
1104// *  Implements the body of the "put" method in the "video" class.
1105// *  Stores a single frame into the video stream:
1106// *    obj put <imageHandle>
1107// * ------------------------------------------------------------------------
1108// */
1109//int
1110//VideoPutCmd(cdata, interp, argc, argv)
1111//    ClientData cdata;      /* not used */
1112//    Tcl_Interp *interp;    /* interpreter */
1113//    int argc;              /* number of arguments */
1114//    CONST84 char* argv[];  /* argument strings */
1115//{
1116//    VideoObj *vidPtr;
1117//    int iw, ih, numBytes, roffs, goffs, boffs;
1118//    char buffer[64];
1119//    unsigned char* photodata;
1120//    uint8_t* rgbdata;
1121//    Tk_PhotoHandle img;
1122//    Tk_PhotoImageBlock iblock;
1123//    AVCodecContext *codecCtx;
1124//
1125//    if (VideoGetData(interp, &vidPtr) != TCL_OK) {
1126//        return TCL_ERROR;
1127//    }
1128//
1129//    if (argc != 2) {
1130//        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1131//            " image\"", (char*)NULL);
1132//        return TCL_ERROR;
1133//    }
1134//
1135//    /*
1136//     * Get the name of the image and copy from it.
1137//     */
1138//    img = Tk_FindPhoto(interp, argv[1]);
1139//    if (img == NULL) {
1140//        Tcl_AppendResult(interp, "bad value \"", argv[1],
1141//            "\": expected photo image", (char*)NULL);
1142//        return TCL_ERROR;
1143//    }
1144//
1145//    /*
1146//     * Query the size for this photo and make sure that we have a
1147//     * buffer of the appropriate size for software scaling and
1148//     * format conversion.
1149//     */
1150//    Tk_PhotoGetImage(img, &iblock);
1151//    Tk_PhotoGetSize(img, &iw, &ih);
1152//
1153//    if (VideoModeWrite(interp, iw, ih) != TCL_OK) {
1154//        return TCL_ERROR;
1155//    }
1156//    codecCtx = vidPtr->outVideoStr->codec;
1157//
1158//    if (iw != vidPtr->rgbw || ih != vidPtr->rgbh) {
1159//        if (vidPtr->rgbbuffer) {
1160//            av_free(vidPtr->rgbbuffer);
1161//            vidPtr->rgbbuffer = NULL;
1162//        }
1163//        numBytes = avpicture_get_size(PIX_FMT_RGB24, iw, ih);
1164//        vidPtr->rgbbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
1165//        vidPtr->rgbw = iw;
1166//        vidPtr->rgbh = ih;
1167//
1168//        avpicture_fill((AVPicture*)vidPtr->pFrameRGB, vidPtr->rgbbuffer,
1169//            PIX_FMT_RGB24, iw, ih);
1170//
1171//        vidPtr->scalingCtx = sws_getCachedContext(vidPtr->scalingCtx,
1172//            iw, ih, PIX_FMT_RGB24,
1173//            codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
1174//            SWS_BICUBIC, NULL, NULL, NULL);
1175//    }
1176//
1177//    /*
1178//     * Copy the data from the Tk photo block into the RGB frame.
1179//     */
1180//    roffs = iblock.offset[0];
1181//    goffs = iblock.offset[1];
1182//    boffs = iblock.offset[2];
1183//
1184//    for (ih=0; ih < iblock.height; ih++) {
1185//        rgbdata = vidPtr->pFrameRGB->data[0] + ih*vidPtr->pFrameRGB->linesize[0];
1186//        photodata = iblock.pixelPtr + ih*iblock.pitch;
1187//        for (iw=0; iw < iblock.width; iw++) {
1188//            rgbdata[0] = photodata[roffs];
1189//            rgbdata[1] = photodata[goffs];
1190//            rgbdata[2] = photodata[boffs];
1191//            rgbdata += 3;
1192//            photodata += iblock.pixelSize;
1193//        }
1194//    }
1195//
1196//    /*
1197//     * Rescale the current frame to the desired size, and translate
1198//     * from RGB to YUV so we can give the frame to the codec.
1199//     */
1200//    sws_scale(vidPtr->scalingCtx,
1201//        vidPtr->pFrameRGB->data, vidPtr->pFrameRGB->linesize,
1202//        0, ih,
1203//        vidPtr->pFrameYUV->data, vidPtr->pFrameYUV->linesize);
1204//
1205//    numBytes = VideoWriteFrame(vidPtr, vidPtr->pFrameYUV);
1206//    if (numBytes < 0) {
1207//        Tcl_AppendResult(interp, "error in av_write_frame()", (char*)NULL);
1208//        return TCL_ERROR;
1209//    }
1210//    sprintf(buffer, "frame %d (%d bytes)", vidPtr->frameNumber++, numBytes);
1211//    Tcl_SetResult(interp, buffer, TCL_VOLATILE);
1212//    return TCL_OK;
1213//}
1214
1215
1216/*
1217 * ------------------------------------------------------------------------
1218 *  VideoWriteFrame()
1219 *
1220 *  Used internally to write a single frame out to the output stream.
1221 *  Returns the number of bytes written to the frame, or -1 if an error
1222 *  occurred.
1223 * ------------------------------------------------------------------------
1224 */
1225int
1226VideoWriteFrame(vidPtr, framePtr)
1227    VideoObj *vidPtr;      /* video object being updated */
1228    AVFrame *framePtr;     /* picture frame being written out */
1229{
1230    int numBytes;
1231    AVCodecContext *codecCtx;
1232    AVPacket pkt;
1233
1234#define OUTBUF_SIZE 500000
1235    uint8_t outbuf[OUTBUF_SIZE];
1236
1237    codecCtx = vidPtr->outVideoStr->codec;
1238    numBytes = avcodec_encode_video(codecCtx, outbuf, OUTBUF_SIZE, framePtr);
1239
1240    if (numBytes > 0) {
1241        av_init_packet(&pkt);
1242
1243        if (codecCtx->coded_frame->pts != AV_NOPTS_VALUE) {
1244            pkt.pts = av_rescale_q(codecCtx->coded_frame->pts,
1245                codecCtx->time_base,
1246                vidPtr->outVideoStr->time_base);
1247        }
1248        if (codecCtx->coded_frame->key_frame) {
1249            pkt.flags |= PKT_FLAG_KEY;
1250        }
1251        pkt.stream_index = vidPtr->outVideoStr->index;
1252        pkt.data = outbuf;
1253        pkt.size = numBytes;
1254
1255        /* write the compressed frame in the media file */
1256        if (av_write_frame(vidPtr->outFormatCtx, &pkt) != 0) {
1257            return -1;
1258        }
1259    }
1260    return numBytes;
1261}
1262
1263/*
1264 * ------------------------------------------------------------------------
1265 *  VideoTransform()
1266 *
1267 *  Implements the body of the "transform" method in the "video" class.
1268 *  Translates one value into another--times into frames, etc.  Handles
1269 *  the following syntax:
1270 *    obj transform frames2duration <frames>
1271 *    obj transform duration2frames <duration>
1272 * ------------------------------------------------------------------------
1273 */
1274double
1275VideoTransformFrames2Duration(vidPtr, frame)
1276    VideoObj *vidPtr;
1277    int frame;
1278{
1279    double duration;
1280    AVCodecContext *vcodecCtx;
1281    AVStream *vstreamPtr;
1282    AVRational hundred;
1283    int64_t tval;
1284
1285    hundred.num = 100;
1286    hundred.den = 1;
1287
1288    if (vidPtr == NULL) {
1289        return -1;
1290    }
1291
1292    if (vidPtr->pFormatCtx == NULL) {
1293//        Tcl_AppendResult(interp, "can't compute transformations:",
1294//            " stream not opened", (char*)NULL);
1295//        return TCL_ERROR;
1296        return -1;
1297    }
1298
1299    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1300    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1301
1302    tval = av_rescale_q((int64_t)frame, hundred, vstreamPtr->r_frame_rate);
1303    duration = 0.01*tval;
1304
1305    return duration;
1306}
1307
1308int
1309VideoTransformDuration2Frames(vidPtr, duration)
1310    VideoObj *vidPtr;
1311    double duration;
1312{
1313    int frames;
1314    AVCodecContext *vcodecCtx;
1315    AVStream *vstreamPtr;
1316    AVRational hundred;
1317    int64_t tval;
1318
1319    hundred.num = 100;
1320    hundred.den = 1;
1321
1322    if (vidPtr == NULL) {
1323        return -1;
1324    }
1325    if (vidPtr->pFormatCtx == NULL) {
1326//        Tcl_AppendResult(interp, "can't compute transformations:",
1327//            " stream not opened", (char*)NULL);
1328//        return TCL_ERROR;
1329        return -1;
1330    }
1331
1332    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1333    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1334
1335    tval = (int64_t)(duration*100);
1336    frames = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1337    // check above for overflow
1338    // tval = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1339    // sprintf(buffer, "%lld", tval);
1340
1341    return frames;
1342}
1343
1344/*
1345 * ------------------------------------------------------------------------
1346 *  VideoClose()
1347 *
1348 *  Implements the body of the _ffmpeg_close method in the "video" class.
1349 *  Closes any file opened previously by the open methods for read/write.
1350 *  If nothing is open, this does nothing.
1351 * ------------------------------------------------------------------------
1352 */
1353int
1354VideoClose(vidPtr)
1355    VideoObj *vidPtr;
1356{
1357    AVCodecContext *vcodecCtx;
1358    int i;
1359
1360    if (vidPtr == NULL) {
1361        return -1;
1362    }
1363
1364    if (vidPtr->yuvbuffer) {
1365        av_free(vidPtr->yuvbuffer);
1366        vidPtr->yuvbuffer = NULL;
1367        vidPtr->yuvw = 0;
1368        vidPtr->yuvh = 0;
1369    }
1370    if (vidPtr->pFrameYUV) {
1371        av_free(vidPtr->pFrameYUV);
1372        vidPtr->pFrameYUV = NULL;
1373    }
1374
1375    if (vidPtr->rgbbuffer) {
1376        av_free(vidPtr->rgbbuffer);
1377        vidPtr->rgbbuffer = NULL;
1378        vidPtr->rgbw = 0;
1379        vidPtr->rgbh = 0;
1380    }
1381    if (vidPtr->pFrameRGB) {
1382        av_free(vidPtr->pFrameRGB);
1383        vidPtr->pFrameRGB = NULL;
1384    }
1385
1386    if (vidPtr->scalingCtx) {
1387        sws_freeContext(vidPtr->scalingCtx);
1388        vidPtr->scalingCtx = NULL;
1389    }
1390    if (vidPtr->pFormatCtx && vidPtr->videoStream >= 0) {
1391        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1392        if (vcodecCtx) {
1393            avcodec_close(vcodecCtx);
1394        }
1395    }
1396    if (vidPtr->pFormatCtx) {
1397        av_close_input_file(vidPtr->pFormatCtx);
1398        vidPtr->pFormatCtx = NULL;
1399    }
1400
1401    if (vidPtr->outFormatCtx) {
1402        while (VideoWriteFrame(vidPtr, NULL) > 0)
1403            ; /* write out any remaining frames */
1404
1405        av_write_trailer(vidPtr->outFormatCtx);
1406
1407        for (i=0; i < vidPtr->outFormatCtx->nb_streams; i++) {
1408            avcodec_close(vidPtr->outFormatCtx->streams[i]->codec);
1409            av_freep(&vidPtr->outFormatCtx->streams[i]->codec);
1410            av_freep(&vidPtr->outFormatCtx->streams[i]);
1411        }
1412
1413        if (vidPtr->outFormatCtx->pb) {
1414            url_fclose(vidPtr->outFormatCtx->pb);
1415        }
1416
1417        av_free(vidPtr->outFormatCtx);
1418        vidPtr->outFormatCtx = NULL;
1419    }
1420
1421    /* reset the mode to null */
1422    *vidPtr->mode = '\0';
1423
1424    return 0;
1425}
Note: See TracBrowser for help on using the repository browser.