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

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

video widget updates
various bug fixes

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