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

Last change on this file since 1925 was 1925, checked in by dkearney, 10 years ago

updates for video widget code. adding uploadWord to filexfer to match the downloadWord. adding some images.

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