source: branches/blt4/src/objects/RpVideo.c @ 2305

Last change on this file since 2305 was 2060, checked in by gah, 13 years ago
File size: 45.1 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#ifdef FFMPEG_AVCODEC_H
708                    // old avcodec decode video function
709                    avcodec_decode_video(vcodecCtx, vidPtr->pFrameYUV,
710                        &frameFinished, packet.data, packet.size);
711#else
712                    // new avcodec decode video function
713                    avcodec_decode_video2(vcodecCtx, vidPtr->pFrameYUV,
714                        &frameFinished, &packet);
715#endif
716
717                    if (packet.dts == AV_NOPTS_VALUE
718                          && vidPtr->pFrameYUV->opaque
719                          && *(uint64_t*)vidPtr->pFrameYUV->opaque != AV_NOPTS_VALUE) {
720                        pts = *(uint64_t*)vidPtr->pFrameYUV->opaque;
721                    } else if (packet.dts != AV_NOPTS_VALUE) {
722                        pts = packet.dts;
723                    } else {
724                        pts = 0;
725                    }
726
727                    if (frameFinished) {
728                        vidPtr->frameNumber = VideoTime2Frame(vstreamPtr, pts);
729                        break;
730                    }
731                }
732            } else {
733                vidPtr->atEnd = 1;
734                break;
735            }
736        }
737        av_free_packet(&packet);
738    }
739}
740
741/*
742 * ------------------------------------------------------------------------
743 *  These two routines are called whenever a frame buffer is allocated,
744 *  which means that we're starting a new frame.  Grab the global pts
745 *  counter and squirrel it away in the opaque slot of the frame.  This
746 *  will give us a pts value that we can trust later.
747 * ------------------------------------------------------------------------
748 */
749int
750VideoAvGetBuffer(c,fr)
751    AVCodecContext *c;  /* codec doing the frame decoding */
752    AVFrame *fr;        /* frame being decoded */
753{
754    int rval = avcodec_default_get_buffer(c, fr);
755    uint64_t *ptsPtr = av_malloc(sizeof(uint64_t));
756    *ptsPtr = global_video_pkt_pts;
757    fr->opaque = ptsPtr;
758    return rval;
759}
760
761void
762VideoAvReleaseBuffer(c,fr)
763    AVCodecContext *c;  /* codec doing the frame decoding */
764    AVFrame *fr;        /* frame being decoded */
765{
766    if (fr && fr->opaque) {
767        av_freep(&fr->opaque);
768    }
769    avcodec_default_release_buffer(c,fr);
770}
771
772/*
773 * ------------------------------------------------------------------------
774 *  VideoInit()
775 *
776 *  Implements the body of the _ffmpeg_init method in the "video" class.
777 *  Initializes the basic data structure and stores it in the _videodata
778 *  variable within the class.
779 * ------------------------------------------------------------------------
780 */
781VideoObj *
782VideoInit()
783{
784    /*
785     * Create an object to represent this video stream.
786     */
787
788    /* Register all codecs and formats */
789    av_register_all();
790
791    return VideoSetData();
792}
793
794/*
795 * ------------------------------------------------------------------------
796 *  VideoCleanup()
797 *
798 *  Implements the body of the _ffmpeg_cleanup method in the "video" class.
799 *  Accesses the data structure stored in the _videodata variable and
800 *  frees up the data.
801 * ------------------------------------------------------------------------
802 */
803int
804VideoCleanup(vidPtr)
805    VideoObj *vidPtr;
806{
807    /*
808     *  Nothing much to do here.  Just close the file in case it is
809     *  still open.  Don't free vidPtr itself; that is cleaned up by
810     *  the ByteArrayObj in the class data member.
811     */
812    int ret = 0;
813
814    ret -= VideoClose(vidPtr);
815
816    if (vidPtr != NULL) {
817        VideoFreeImgBuffer(vidPtr);
818        if (vidPtr->fileName != NULL) {
819            free(vidPtr->fileName);
820            vidPtr->fileName = NULL;
821        }
822        free(vidPtr);
823        vidPtr = NULL;
824// FIXME: need a test to make sure vidPtr is null after the function returns.
825    }
826
827    return ret;
828}
829
830/*
831 * ------------------------------------------------------------------------
832 *  VideoSize()
833 *
834 *  Implements the body of the "size" method in the "video" class.
835 *  Returns the size of each frame in this video stream as a list {w h}.
836 * ------------------------------------------------------------------------
837 */
838int
839VideoSize(vidPtr, width, height)
840    VideoObj *vidPtr;
841    int *width;
842    int *height;
843{
844    AVCodecContext *vcodecCtx;
845
846    if (vidPtr == NULL) {
847        return -1;
848    }
849
850    if (vidPtr->pFormatCtx == NULL) {
851        // "internal error: video stream is not open",
852        return -1;
853    }
854
855    if (vidPtr->pFormatCtx) {
856        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
857        if (width != NULL) {
858            *width = vcodecCtx->width;
859        }
860        if (height != NULL) {
861            *height = vcodecCtx->height;
862        }
863    }
864    return 0;
865}
866
867/*
868 * ------------------------------------------------------------------------
869 *  VideoGo()
870 *
871 *  Implements the body of the "go" method in the "video" class.
872 *  Advances by one or more frames, or seeks backward in the stream.
873 *  Handles the following syntax:
874 *    obj go next ...... go to next frame (same as +1)
875 *    obj go +n ........ advance by n frames
876 *    obj go -n ........ go back by n frames
877 *    obj go n ......... go to frame n
878 * ------------------------------------------------------------------------
879 */
880int
881VideoGoNext(vidPtr)
882    VideoObj *vidPtr;
883{
884    int nabs;
885
886    if (vidPtr == NULL) {
887        return -1;
888    }
889
890    nabs = vidPtr->frameNumber + 1;
891    return VideoGoToN(vidPtr, nabs);
892}
893
894int
895VideoGoPlusMinusN(vidPtr, n)
896    VideoObj *vidPtr;
897    int n;
898{
899    int nabs;
900
901    if (vidPtr == NULL) {
902        return -1;
903    }
904
905    nabs = vidPtr->frameNumber + n;
906    return VideoGoToN(vidPtr, nabs);
907}
908
909int
910VideoGoToN(vidPtr, n)
911    VideoObj *vidPtr;
912    int n;
913{
914    int nrel, nabs, seekFlags, gotframe, t;
915    int64_t nseek;
916    AVCodecContext *vcodecCtx;
917    AVStream *vstreamPtr;
918
919    if (vidPtr == NULL) {
920        return -1;
921    }
922
923    if (vidPtr->pFormatCtx == NULL) {
924        // "internal error: video stream is not open",
925        return -1;
926    }
927    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
928
929    nabs = n;
930
931    if (nabs < 0) {
932        nabs = 0;
933    }
934
935    if (nabs < vidPtr->frameNumber) {
936        seekFlags = AVSEEK_FLAG_BACKWARD;
937    } else {
938        seekFlags = 0;
939    }
940
941    /*
942     * If we're going to an absolute frame, or if we're going backward
943     * or too far forward, then seek the frame.
944     */
945    nrel = nabs-vidPtr->frameNumber;
946    if ((nrel > 50) || (seekFlags&AVSEEK_FLAG_BACKWARD)) {
947
948        vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
949        nseek = VideoFrame2Time(vstreamPtr, nabs);
950        // not sure why it is checking against the number 100
951        if (nseek > 100) {
952            nseek -= 100;
953        } else {
954            nseek = 0;
955        }
956
957        /* first, seek the nearest reference frame for a good starting pt */
958        av_seek_frame(vidPtr->pFormatCtx, vidPtr->videoStream,
959            nseek, seekFlags);
960
961        // this doesn't seem to give me back the true frame number
962        // feels like it is more of a reverse of the VideoFrame2Time call
963        // because vidPtr->frameNumber always equals nabs
964        vidPtr->frameNumber = VideoTime2Frame(vstreamPtr, nseek);
965        vidPtr->atEnd = 0;
966
967        /* read the frame to figure out what the frame number is */
968        VideoNextFrame(vidPtr);
969
970        /* then, move forward until we reach the desired frame */
971        gotframe = 0;
972        while (vidPtr->frameNumber < nabs && !vidPtr->atEnd) {
973            VideoNextFrame(vidPtr);
974            gotframe = 1;
975        }
976
977        /* get at least one frame, unless we're done or at the beginning*/
978        if (!gotframe && !vidPtr->atEnd) {
979            if (vidPtr->frameNumber > nabs) {
980                // we are probably at a key frame, just past
981                // the requested frame and need to seek backwards.
982                VideoGoToN(vidPtr,n);
983            } else {
984                VideoNextFrame(vidPtr);
985            }
986        }
987    }
988    else {
989        while (nrel-- > 0) {
990            VideoNextFrame(vidPtr);
991        }
992    }
993
994    /*
995     * Send back the current frame number or "end" as the result.
996     */
997    return vidPtr->frameNumber;
998}
999
1000/*
1001 * ------------------------------------------------------------------------
1002 *  VideoGet()
1003 *
1004 *  Implements the body of the "get" method in the "video" class.
1005 *  Returns information about the current frame via the following
1006 *  syntax:
1007 *    obj get start|position|end
1008 *    obj get <imageHandle>
1009 * ------------------------------------------------------------------------
1010 */
1011int
1012VideoGetImage(vidPtr, iw, ih, img, bufSize)
1013    VideoObj *vidPtr;
1014    int iw;
1015    int ih;
1016    void **img;
1017    int *bufSize;
1018{
1019
1020    int nframe, numBytes;
1021    char c, buffer[64];
1022    AVCodecContext *vcodecCtx;
1023    AVStream *vstreamPtr;
1024
1025    if (vidPtr == NULL) {
1026        return -1;
1027    }
1028
1029    if (VideoModeRead(vidPtr) != 0) {
1030        return -1;
1031    }
1032
1033    /*
1034    if (vidPtr->pFormatCtx) {
1035        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1036    } else {
1037        vcodecCtx = NULL;
1038    }
1039    */
1040
1041    if (vidPtr->pFormatCtx == NULL) {
1042        // vidPtr->pFormatCtx is NULL, video not open
1043        return -1;
1044    }
1045    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1046
1047    /*
1048     * Query the size for this photo and make sure that we have a
1049     * buffer of the appropriate size for software scaling and
1050     * format conversion.
1051     */
1052
1053    // if the user's desired size is less then 0,
1054    // use the default size
1055
1056    if (iw < 0) {
1057        iw = vcodecCtx->width;
1058    }
1059    if (ih < 0) {
1060        ih = vcodecCtx->height;
1061    }
1062
1063
1064    if (iw != vidPtr->rgbw || ih != vidPtr->rgbh) {
1065        if (vidPtr->rgbbuffer) {
1066            av_free(vidPtr->rgbbuffer);
1067            vidPtr->rgbbuffer = NULL;
1068        }
1069        numBytes = avpicture_get_size(PIX_FMT_RGB24, iw, ih);
1070        vidPtr->rgbbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
1071        vidPtr->rgbw = iw;
1072        vidPtr->rgbh = ih;
1073
1074        avpicture_fill((AVPicture*)vidPtr->pFrameRGB, vidPtr->rgbbuffer,
1075            PIX_FMT_RGB24, iw, ih);
1076
1077        vidPtr->scalingCtx = sws_getCachedContext(vidPtr->scalingCtx,
1078            vcodecCtx->width, vcodecCtx->height, vcodecCtx->pix_fmt,
1079            iw, ih, PIX_FMT_RGB24, SWS_BICUBIC|SWS_PRINT_INFO, NULL, NULL, NULL);
1080    }
1081
1082    /*
1083     * Rescale the current frame to the desired size, and translate
1084     * into RGB format so we can copy into the destination image.
1085     */
1086    if (vidPtr->pFrameYUV && vidPtr->pFrameYUV->data[0]) {
1087        sws_scale(vidPtr->scalingCtx, (const uint8_t * const*)
1088            vidPtr->pFrameYUV->data, vidPtr->pFrameYUV->linesize,
1089            0, vcodecCtx->height,
1090            vidPtr->pFrameRGB->data, vidPtr->pFrameRGB->linesize);
1091
1092/*
1093        iblock.pixelPtr  = (unsigned char*)vidPtr->pFrameRGB->data[0];
1094        iblock.width     = iw;
1095        iblock.height    = ih;
1096        iblock.pitch     = vidPtr->pFrameRGB->linesize[0];
1097        iblock.pixelSize = 3;
1098        iblock.offset[0] = 0;
1099        iblock.offset[1] = 1;
1100        iblock.offset[2] = 2;
1101        iblock.offset[3] = 0;
1102
1103        Tk_PhotoPutBlock_NoComposite(img, &iblock, 0, 0, iw, ih);
1104*/
1105
1106        int bufsize = 0;
1107        if (vidPtr->img == NULL) {
1108            VideoAllocImgBuffer(vidPtr,iw,ih);
1109        } else {
1110            if ((vidPtr->imgWidth != iw) && (vidPtr->imgHeight != ih)) {
1111                // new height or width
1112                // resize the image buffer
1113                free(vidPtr->img);
1114                VideoAllocImgBuffer(vidPtr,iw,ih);
1115            }
1116        }
1117
1118        // Write pixel data
1119        memcpy(vidPtr->img+vidPtr->imgHeaderLen,
1120            vidPtr->pFrameRGB->data[0],
1121            vidPtr->imgWidth*3*vidPtr->imgHeight);
1122    }
1123    *img = vidPtr->img;
1124    *bufSize = (vidPtr->imgWidth*3*vidPtr->imgHeight) + vidPtr->imgHeaderLen;
1125    return 0;
1126}
1127
1128int
1129VideoFrameRate (vidPtr, fr)
1130    VideoObj *vidPtr;
1131    double *fr;
1132{
1133    AVStream *vstreamPtr;
1134
1135    if (vidPtr == NULL) {
1136        return -1;
1137    }
1138
1139    if (fr == NULL) {
1140        return -1;
1141    }
1142
1143    if (vidPtr->pFormatCtx == NULL) {
1144        // vidPtr->pFormatCtx is NULL, video not open
1145        return -1;
1146    }
1147    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1148
1149    // http://trac.handbrake.fr/browser/trunk/libhb/decavcodec.c?rev=1490#L684
1150    // there seems to be some controversy over what structure holds
1151    // the correct frame rate information for different video codecs.
1152    // for now we will use the stream's r_frame_rate.
1153    // from the above post, it looks like this value can be interpreted
1154    // as frames per second.
1155    *fr = av_q2d(vstreamPtr->r_frame_rate);
1156
1157    return 0;
1158}
1159
1160int
1161VideoFileName (vidPtr, fname)
1162    VideoObj *vidPtr;
1163    const char **fname;
1164{
1165    AVStream *vstreamPtr;
1166
1167    if (vidPtr == NULL) {
1168        return -1;
1169    }
1170
1171    if (fname == NULL) {
1172        return -1;
1173    }
1174
1175    if (vidPtr->pFormatCtx == NULL) {
1176        // vidPtr->pFormatCtx is NULL, video not open
1177        return -1;
1178    }
1179
1180    *fname = vidPtr->fileName;
1181
1182    return 0;
1183}
1184
1185int
1186VideoPixelAspectRatio (vidPtr, num, den)
1187    VideoObj *vidPtr;
1188    int *num;
1189    int *den;
1190{
1191    AVCodecContext *vcodecCtx;
1192
1193    if (vidPtr == NULL) {
1194        return -1;
1195    }
1196
1197    if ((num == NULL) || (den == NULL)) {
1198        return -1;
1199    }
1200
1201    if (vidPtr->pFormatCtx == NULL) {
1202        // vidPtr->pFormatCtx is NULL, video not open
1203        return -1;
1204    }
1205
1206    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1207
1208    *num = vcodecCtx->sample_aspect_ratio.num;
1209    *den = vcodecCtx->sample_aspect_ratio.den;
1210
1211    return 0;
1212}
1213
1214int
1215VideoDisplayAspectRatio (vidPtr, num, den)
1216    VideoObj *vidPtr;
1217    int *num;
1218    int *den;
1219{
1220    AVCodecContext *vcodecCtx;
1221    int width = 0;
1222    int height = 0;
1223    int64_t gcd = 0;
1224    int64_t gcd2 = 0;
1225
1226    if (vidPtr == NULL) {
1227        return -1;
1228    }
1229
1230    if ((num == NULL) || (den == NULL)) {
1231        return -1;
1232    }
1233
1234    if (vidPtr->pFormatCtx == NULL) {
1235        // vidPtr->pFormatCtx is NULL, video not open
1236        return -1;
1237    }
1238
1239    VideoSize(vidPtr, &width, &height);
1240    VideoPixelAspectRatio(vidPtr, num, den);
1241
1242    width = (*num)*width;
1243    height = (*den)*height;
1244#ifdef FFMPEG_COMMON_H
1245    // old gcd function
1246    gcd = ff_gcd(FFABS(width), FFABS(height));
1247#else
1248    // new gcd function
1249    gcd = av_gcd(FFABS(width), FFABS(height));
1250#endif
1251
1252
1253    *num = width/gcd;
1254    *den = height/gcd;
1255
1256    if (*den == 0) {
1257        *num = 0;
1258        *den = 1;
1259    }
1260
1261    return 0;
1262}
1263
1264int
1265VideoAllocImgBuffer(vidPtr, width, height)
1266    VideoObj *vidPtr;
1267    int width;
1268    int height;
1269{
1270
1271    char header[64];
1272    int headerLen = 0;
1273    int bufsize = 0;
1274
1275    sprintf(header,"P6\n%d %d\n255\n", width, height);
1276    headerLen = strlen(header);
1277    bufsize = headerLen + (width*3*height);
1278    vidPtr->img = (void*) malloc(bufsize);
1279    vidPtr->imgHeaderLen = headerLen;
1280    vidPtr->imgWidth = width;
1281    vidPtr->imgHeight = height;
1282    memcpy(vidPtr->img,header,headerLen);
1283
1284    return 0;
1285}
1286
1287int
1288VideoFreeImgBuffer(vidPtr)
1289    VideoObj *vidPtr;
1290{
1291    if ((vidPtr != NULL) && (vidPtr->img != NULL)) {
1292        free(vidPtr->img);
1293        vidPtr->img = NULL;
1294    }
1295    return 0;
1296}
1297
1298int
1299VideoGetPositionCur(vidPtr, pos)
1300    VideoObj *vidPtr;      /* video object to act on */
1301    int *pos;
1302{
1303    int fnum = -1;
1304    AVStream *vstreamPtr;
1305
1306    if (vidPtr == NULL) {
1307        return -1;
1308    }
1309
1310    if (pos == NULL) {
1311        return -1;
1312    }
1313
1314    if (VideoModeRead(vidPtr) != 0) {
1315        return -1;
1316    }
1317
1318    if (vidPtr->pFormatCtx) {
1319        fnum = vidPtr->frameNumber;
1320    }
1321
1322    *pos = fnum;
1323    return 0;
1324}
1325
1326int
1327VideoGetPositionEnd(vidPtr, pos)
1328    VideoObj *vidPtr;      /* video object to act on */
1329    int *pos;
1330{
1331    AVStream *vstreamPtr;
1332
1333    if (vidPtr == NULL) {
1334        return -1;
1335    }
1336
1337    if (pos == NULL) {
1338        return -1;
1339    }
1340
1341    if (VideoModeRead(vidPtr) != 0) {
1342        return -1;
1343    }
1344
1345    *pos = vidPtr->lastframe;
1346    return 0;
1347}
1348
1349// FIXME: get this function working
1350///*
1351// * ------------------------------------------------------------------------
1352// *  VideoPut()
1353// *
1354// *  Implements the body of the "put" method in the "video" class.
1355// *  Stores a single frame into the video stream:
1356// *    obj put <imageHandle>
1357// * ------------------------------------------------------------------------
1358// */
1359//int
1360//VideoPut(cdata, interp, argc, argv)
1361//    ClientData cdata;      /* not used */
1362//    Tcl_Interp *interp;    /* interpreter */
1363//    int argc;              /* number of arguments */
1364//    CONST84 char* argv[];  /* argument strings */
1365//{
1366//    VideoObj *vidPtr;
1367//    int iw, ih, numBytes, roffs, goffs, boffs;
1368//    char buffer[64];
1369//    unsigned char* photodata;
1370//    uint8_t* rgbdata;
1371//    Tk_PhotoHandle img;
1372//    Tk_PhotoImageBlock iblock;
1373//    AVCodecContext *codecCtx;
1374//
1375//    if (VideoGetData(interp, &vidPtr) != TCL_OK) {
1376//        return TCL_ERROR;
1377//    }
1378//
1379//    if (argc != 2) {
1380//        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1381//            " image\"", (char*)NULL);
1382//        return TCL_ERROR;
1383//    }
1384//
1385//    /*
1386//     * Get the name of the image and copy from it.
1387//     */
1388//    img = Tk_FindPhoto(interp, argv[1]);
1389//    if (img == NULL) {
1390//        Tcl_AppendResult(interp, "bad value \"", argv[1],
1391//            "\": expected photo image", (char*)NULL);
1392//        return TCL_ERROR;
1393//    }
1394//
1395//    /*
1396//     * Query the size for this photo and make sure that we have a
1397//     * buffer of the appropriate size for software scaling and
1398//     * format conversion.
1399//     */
1400//    Tk_PhotoGetImage(img, &iblock);
1401//    Tk_PhotoGetSize(img, &iw, &ih);
1402//
1403//    if (VideoModeWrite(interp, iw, ih) != TCL_OK) {
1404//        return TCL_ERROR;
1405//    }
1406//    codecCtx = vidPtr->outVideoStr->codec;
1407//
1408//    if (iw != vidPtr->rgbw || ih != vidPtr->rgbh) {
1409//        if (vidPtr->rgbbuffer) {
1410//            av_free(vidPtr->rgbbuffer);
1411//            vidPtr->rgbbuffer = NULL;
1412//        }
1413//        numBytes = avpicture_get_size(PIX_FMT_RGB24, iw, ih);
1414//        vidPtr->rgbbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
1415//        vidPtr->rgbw = iw;
1416//        vidPtr->rgbh = ih;
1417//
1418//        avpicture_fill((AVPicture*)vidPtr->pFrameRGB, vidPtr->rgbbuffer,
1419//            PIX_FMT_RGB24, iw, ih);
1420//
1421//        vidPtr->scalingCtx = sws_getCachedContext(vidPtr->scalingCtx,
1422//            iw, ih, PIX_FMT_RGB24,
1423//            codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
1424//            SWS_BICUBIC, NULL, NULL, NULL);
1425//    }
1426//
1427//    /*
1428//     * Copy the data from the Tk photo block into the RGB frame.
1429//     */
1430//    roffs = iblock.offset[0];
1431//    goffs = iblock.offset[1];
1432//    boffs = iblock.offset[2];
1433//
1434//    for (ih=0; ih < iblock.height; ih++) {
1435//        rgbdata = vidPtr->pFrameRGB->data[0] + ih*vidPtr->pFrameRGB->linesize[0];
1436//        photodata = iblock.pixelPtr + ih*iblock.pitch;
1437//        for (iw=0; iw < iblock.width; iw++) {
1438//            rgbdata[0] = photodata[roffs];
1439//            rgbdata[1] = photodata[goffs];
1440//            rgbdata[2] = photodata[boffs];
1441//            rgbdata += 3;
1442//            photodata += iblock.pixelSize;
1443//        }
1444//    }
1445//
1446//    /*
1447//     * Rescale the current frame to the desired size, and translate
1448//     * from RGB to YUV so we can give the frame to the codec.
1449//     */
1450//    sws_scale(vidPtr->scalingCtx,
1451//        vidPtr->pFrameRGB->data, vidPtr->pFrameRGB->linesize,
1452//        0, ih,
1453//        vidPtr->pFrameYUV->data, vidPtr->pFrameYUV->linesize);
1454//
1455//    numBytes = VideoWriteFrame(vidPtr, vidPtr->pFrameYUV);
1456//    if (numBytes < 0) {
1457//        Tcl_AppendResult(interp, "error in av_write_frame()", (char*)NULL);
1458//        return TCL_ERROR;
1459//    }
1460//    sprintf(buffer, "frame %d (%d bytes)", vidPtr->frameNumber++, numBytes);
1461//    Tcl_SetResult(interp, buffer, TCL_VOLATILE);
1462//    return TCL_OK;
1463//}
1464
1465
1466/*
1467 * ------------------------------------------------------------------------
1468 *  VideoWriteFrame()
1469 *
1470 *  Used internally to write a single frame out to the output stream.
1471 *  Returns the number of bytes written to the frame, or -1 if an error
1472 *  occurred.
1473 * ------------------------------------------------------------------------
1474 */
1475int
1476VideoWriteFrame(vidPtr, framePtr)
1477    VideoObj *vidPtr;      /* video object being updated */
1478    AVFrame *framePtr;     /* picture frame being written out */
1479{
1480    int numBytes;
1481    AVCodecContext *codecCtx;
1482    AVPacket pkt;
1483
1484#define OUTBUF_SIZE 500000
1485    uint8_t outbuf[OUTBUF_SIZE];
1486
1487    codecCtx = vidPtr->outVideoStr->codec;
1488    numBytes = avcodec_encode_video(codecCtx, outbuf, OUTBUF_SIZE, framePtr);
1489
1490    if (numBytes > 0) {
1491        av_init_packet(&pkt);
1492
1493        if (codecCtx->coded_frame->pts != AV_NOPTS_VALUE) {
1494            pkt.pts = av_rescale_q(codecCtx->coded_frame->pts,
1495                codecCtx->time_base,
1496                vidPtr->outVideoStr->time_base);
1497        }
1498        if (codecCtx->coded_frame->key_frame) {
1499            pkt.flags |= PKT_FLAG_KEY;
1500        }
1501        pkt.stream_index = vidPtr->outVideoStr->index;
1502        pkt.data = outbuf;
1503        pkt.size = numBytes;
1504
1505        /* write the compressed frame in the media file */
1506        if (av_write_frame(vidPtr->outFormatCtx, &pkt) != 0) {
1507            return -1;
1508        }
1509    }
1510    return numBytes;
1511}
1512
1513/*
1514 * ------------------------------------------------------------------------
1515 *  VideoTransform()
1516 *
1517 *  Implements the body of the "transform" method in the "video" class.
1518 *  Translates one value into another--times into frames, etc.  Handles
1519 *  the following syntax:
1520 *    obj transform frames2duration <frames>
1521 *    obj transform duration2frames <duration>
1522 * ------------------------------------------------------------------------
1523 */
1524double
1525VideoTransformFrames2Duration(vidPtr, frame)
1526    VideoObj *vidPtr;
1527    int frame;
1528{
1529    double duration;
1530    AVCodecContext *vcodecCtx;
1531    AVStream *vstreamPtr;
1532    AVRational hundred;
1533    int64_t tval;
1534
1535    hundred.num = 100;
1536    hundred.den = 1;
1537
1538    if (vidPtr == NULL) {
1539        return -1;
1540    }
1541
1542    if (vidPtr->pFormatCtx == NULL) {
1543//        Tcl_AppendResult(interp, "can't compute transformations:",
1544//            " stream not opened", (char*)NULL);
1545//        return TCL_ERROR;
1546        return -1;
1547    }
1548
1549    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1550    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1551
1552    tval = av_rescale_q((int64_t)frame, hundred, vstreamPtr->r_frame_rate);
1553    duration = 0.01*tval;
1554
1555    return duration;
1556}
1557
1558int
1559VideoTransformDuration2Frames(vidPtr, duration)
1560    VideoObj *vidPtr;
1561    double duration;
1562{
1563    int frames;
1564    AVCodecContext *vcodecCtx;
1565    AVStream *vstreamPtr;
1566    AVRational hundred;
1567    int64_t tval;
1568
1569    hundred.num = 100;
1570    hundred.den = 1;
1571
1572    if (vidPtr == NULL) {
1573        return -1;
1574    }
1575    if (vidPtr->pFormatCtx == NULL) {
1576//        Tcl_AppendResult(interp, "can't compute transformations:",
1577//            " stream not opened", (char*)NULL);
1578//        return TCL_ERROR;
1579        return -1;
1580    }
1581
1582    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1583    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1584
1585    tval = (int64_t)(duration*100);
1586    frames = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1587    // check above for overflow
1588    // tval = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1589    // sprintf(buffer, "%lld", tval);
1590
1591    return frames;
1592}
1593
1594/*
1595 * ------------------------------------------------------------------------
1596 *  VideoClose()
1597 *
1598 *  Implements the body of the _ffmpeg_close method in the "video" class.
1599 *  Closes any file opened previously by the open methods for read/write.
1600 *  If nothing is open, this does nothing.
1601 * ------------------------------------------------------------------------
1602 */
1603int
1604VideoClose(vidPtr)
1605    VideoObj *vidPtr;
1606{
1607    AVCodecContext *vcodecCtx;
1608    int i;
1609
1610    if (vidPtr == NULL) {
1611        return -1;
1612    }
1613
1614    if (vidPtr->yuvbuffer) {
1615        av_free(vidPtr->yuvbuffer);
1616        vidPtr->yuvbuffer = NULL;
1617        vidPtr->yuvw = 0;
1618        vidPtr->yuvh = 0;
1619    }
1620    if (vidPtr->pFrameYUV) {
1621        av_free(vidPtr->pFrameYUV);
1622        vidPtr->pFrameYUV = NULL;
1623    }
1624
1625    if (vidPtr->rgbbuffer) {
1626        av_free(vidPtr->rgbbuffer);
1627        vidPtr->rgbbuffer = NULL;
1628        vidPtr->rgbw = 0;
1629        vidPtr->rgbh = 0;
1630    }
1631    if (vidPtr->pFrameRGB) {
1632        av_free(vidPtr->pFrameRGB);
1633        vidPtr->pFrameRGB = NULL;
1634    }
1635
1636    if (vidPtr->scalingCtx) {
1637        sws_freeContext(vidPtr->scalingCtx);
1638        vidPtr->scalingCtx = NULL;
1639    }
1640    if (vidPtr->pFormatCtx && vidPtr->videoStream >= 0) {
1641        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1642        if (vcodecCtx) {
1643            avcodec_close(vcodecCtx);
1644        }
1645    }
1646    if (vidPtr->pFormatCtx) {
1647        av_close_input_file(vidPtr->pFormatCtx);
1648        vidPtr->pFormatCtx = NULL;
1649    }
1650
1651    if (vidPtr->outFormatCtx) {
1652        while (VideoWriteFrame(vidPtr, NULL) > 0)
1653            ; /* write out any remaining frames */
1654
1655        av_write_trailer(vidPtr->outFormatCtx);
1656
1657        for (i=0; i < vidPtr->outFormatCtx->nb_streams; i++) {
1658            avcodec_close(vidPtr->outFormatCtx->streams[i]->codec);
1659            av_freep(&vidPtr->outFormatCtx->streams[i]->codec);
1660            av_freep(&vidPtr->outFormatCtx->streams[i]);
1661        }
1662
1663        if (vidPtr->outFormatCtx->pb) {
1664            url_fclose(vidPtr->outFormatCtx->pb);
1665        }
1666
1667        av_free(vidPtr->outFormatCtx);
1668        vidPtr->outFormatCtx = NULL;
1669    }
1670
1671    /* reset the mode to null */
1672    *vidPtr->mode = '\0';
1673
1674    return 0;
1675}
Note: See TracBrowser for help on using the repository browser.