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

Last change on this file since 2044 was 2044, checked in by gah, 14 years ago
File size: 44.9 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 *  VideoInit()
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 *
778VideoInit()
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 *  VideoCleanup()
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
800VideoCleanup(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 *  VideoSize()
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
835VideoSize(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 *  VideoGo()
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 *  VideoGet()
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
1125VideoFrameRate (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
1157VideoFileName (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
1182VideoPixelAspectRatio (vidPtr, num, den)
1183    VideoObj *vidPtr;
1184    int *num;
1185    int *den;
1186{
1187    AVCodecContext *vcodecCtx;
1188
1189    if (vidPtr == NULL) {
1190        return -1;
1191    }
1192
1193    if ((num == NULL) || (den == NULL)) {
1194        return -1;
1195    }
1196
1197    if (vidPtr->pFormatCtx == NULL) {
1198        // vidPtr->pFormatCtx is NULL, video not open
1199        return -1;
1200    }
1201
1202    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1203
1204    *num = vcodecCtx->sample_aspect_ratio.num;
1205    *den = vcodecCtx->sample_aspect_ratio.den;
1206
1207    return 0;
1208}
1209
1210int
1211VideoDisplayAspectRatio (vidPtr, num, den)
1212    VideoObj *vidPtr;
1213    int *num;
1214    int *den;
1215{
1216    AVCodecContext *vcodecCtx;
1217    int width = 0;
1218    int height = 0;
1219    int64_t gcd = 0;
1220    int64_t gcd2 = 0;
1221
1222    if (vidPtr == NULL) {
1223        return -1;
1224    }
1225
1226    if ((num == NULL) || (den == NULL)) {
1227        return -1;
1228    }
1229
1230    if (vidPtr->pFormatCtx == NULL) {
1231        // vidPtr->pFormatCtx is NULL, video not open
1232        return -1;
1233    }
1234
1235    VideoSize(vidPtr, &width, &height);
1236    VideoPixelAspectRatio(vidPtr, num, den);
1237
1238    width = (*num)*width;
1239    height = (*den)*height;
1240    gcd = av_gcd(FFABS(width), FFABS(height));
1241
1242    *num = width/gcd;
1243    *den = height/gcd;
1244
1245    if (*den == 0) {
1246        *num = 0;
1247        *den = 1;
1248    }
1249
1250    return 0;
1251}
1252
1253int
1254VideoAllocImgBuffer(vidPtr, width, height)
1255    VideoObj *vidPtr;
1256    int width;
1257    int height;
1258{
1259
1260    char header[64];
1261    int headerLen = 0;
1262    int bufsize = 0;
1263
1264    sprintf(header,"P6\n%d %d\n255\n", width, height);
1265    headerLen = strlen(header);
1266    bufsize = headerLen + (width*3*height);
1267    vidPtr->img = (void*) malloc(bufsize);
1268    vidPtr->imgHeaderLen = headerLen;
1269    vidPtr->imgWidth = width;
1270    vidPtr->imgHeight = height;
1271    memcpy(vidPtr->img,header,headerLen);
1272
1273    return 0;
1274}
1275
1276int
1277VideoFreeImgBuffer(vidPtr)
1278    VideoObj *vidPtr;
1279{
1280    if ((vidPtr != NULL) && (vidPtr->img != NULL)) {
1281        free(vidPtr->img);
1282        vidPtr->img = NULL;
1283    }
1284    return 0;
1285}
1286
1287int
1288VideoGetPositionCur(vidPtr, pos)
1289    VideoObj *vidPtr;      /* video object to act on */
1290    int *pos;
1291{
1292    int fnum = -1;
1293    AVStream *vstreamPtr;
1294
1295    if (vidPtr == NULL) {
1296        return -1;
1297    }
1298
1299    if (pos == NULL) {
1300        return -1;
1301    }
1302
1303    if (VideoModeRead(vidPtr) != 0) {
1304        return -1;
1305    }
1306
1307    if (vidPtr->pFormatCtx) {
1308        fnum = vidPtr->frameNumber;
1309    }
1310
1311    *pos = fnum;
1312    return 0;
1313}
1314
1315int
1316VideoGetPositionEnd(vidPtr, pos)
1317    VideoObj *vidPtr;      /* video object to act on */
1318    int *pos;
1319{
1320    AVStream *vstreamPtr;
1321
1322    if (vidPtr == NULL) {
1323        return -1;
1324    }
1325
1326    if (pos == NULL) {
1327        return -1;
1328    }
1329
1330    if (VideoModeRead(vidPtr) != 0) {
1331        return -1;
1332    }
1333
1334    *pos = vidPtr->lastframe;
1335    return 0;
1336}
1337
1338// FIXME: get this function working
1339///*
1340// * ------------------------------------------------------------------------
1341// *  VideoPut()
1342// *
1343// *  Implements the body of the "put" method in the "video" class.
1344// *  Stores a single frame into the video stream:
1345// *    obj put <imageHandle>
1346// * ------------------------------------------------------------------------
1347// */
1348//int
1349//VideoPut(cdata, interp, argc, argv)
1350//    ClientData cdata;      /* not used */
1351//    Tcl_Interp *interp;    /* interpreter */
1352//    int argc;              /* number of arguments */
1353//    CONST84 char* argv[];  /* argument strings */
1354//{
1355//    VideoObj *vidPtr;
1356//    int iw, ih, numBytes, roffs, goffs, boffs;
1357//    char buffer[64];
1358//    unsigned char* photodata;
1359//    uint8_t* rgbdata;
1360//    Tk_PhotoHandle img;
1361//    Tk_PhotoImageBlock iblock;
1362//    AVCodecContext *codecCtx;
1363//
1364//    if (VideoGetData(interp, &vidPtr) != TCL_OK) {
1365//        return TCL_ERROR;
1366//    }
1367//
1368//    if (argc != 2) {
1369//        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1370//            " image\"", (char*)NULL);
1371//        return TCL_ERROR;
1372//    }
1373//
1374//    /*
1375//     * Get the name of the image and copy from it.
1376//     */
1377//    img = Tk_FindPhoto(interp, argv[1]);
1378//    if (img == NULL) {
1379//        Tcl_AppendResult(interp, "bad value \"", argv[1],
1380//            "\": expected photo image", (char*)NULL);
1381//        return TCL_ERROR;
1382//    }
1383//
1384//    /*
1385//     * Query the size for this photo and make sure that we have a
1386//     * buffer of the appropriate size for software scaling and
1387//     * format conversion.
1388//     */
1389//    Tk_PhotoGetImage(img, &iblock);
1390//    Tk_PhotoGetSize(img, &iw, &ih);
1391//
1392//    if (VideoModeWrite(interp, iw, ih) != TCL_OK) {
1393//        return TCL_ERROR;
1394//    }
1395//    codecCtx = vidPtr->outVideoStr->codec;
1396//
1397//    if (iw != vidPtr->rgbw || ih != vidPtr->rgbh) {
1398//        if (vidPtr->rgbbuffer) {
1399//            av_free(vidPtr->rgbbuffer);
1400//            vidPtr->rgbbuffer = NULL;
1401//        }
1402//        numBytes = avpicture_get_size(PIX_FMT_RGB24, iw, ih);
1403//        vidPtr->rgbbuffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
1404//        vidPtr->rgbw = iw;
1405//        vidPtr->rgbh = ih;
1406//
1407//        avpicture_fill((AVPicture*)vidPtr->pFrameRGB, vidPtr->rgbbuffer,
1408//            PIX_FMT_RGB24, iw, ih);
1409//
1410//        vidPtr->scalingCtx = sws_getCachedContext(vidPtr->scalingCtx,
1411//            iw, ih, PIX_FMT_RGB24,
1412//            codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
1413//            SWS_BICUBIC, NULL, NULL, NULL);
1414//    }
1415//
1416//    /*
1417//     * Copy the data from the Tk photo block into the RGB frame.
1418//     */
1419//    roffs = iblock.offset[0];
1420//    goffs = iblock.offset[1];
1421//    boffs = iblock.offset[2];
1422//
1423//    for (ih=0; ih < iblock.height; ih++) {
1424//        rgbdata = vidPtr->pFrameRGB->data[0] + ih*vidPtr->pFrameRGB->linesize[0];
1425//        photodata = iblock.pixelPtr + ih*iblock.pitch;
1426//        for (iw=0; iw < iblock.width; iw++) {
1427//            rgbdata[0] = photodata[roffs];
1428//            rgbdata[1] = photodata[goffs];
1429//            rgbdata[2] = photodata[boffs];
1430//            rgbdata += 3;
1431//            photodata += iblock.pixelSize;
1432//        }
1433//    }
1434//
1435//    /*
1436//     * Rescale the current frame to the desired size, and translate
1437//     * from RGB to YUV so we can give the frame to the codec.
1438//     */
1439//    sws_scale(vidPtr->scalingCtx,
1440//        vidPtr->pFrameRGB->data, vidPtr->pFrameRGB->linesize,
1441//        0, ih,
1442//        vidPtr->pFrameYUV->data, vidPtr->pFrameYUV->linesize);
1443//
1444//    numBytes = VideoWriteFrame(vidPtr, vidPtr->pFrameYUV);
1445//    if (numBytes < 0) {
1446//        Tcl_AppendResult(interp, "error in av_write_frame()", (char*)NULL);
1447//        return TCL_ERROR;
1448//    }
1449//    sprintf(buffer, "frame %d (%d bytes)", vidPtr->frameNumber++, numBytes);
1450//    Tcl_SetResult(interp, buffer, TCL_VOLATILE);
1451//    return TCL_OK;
1452//}
1453
1454
1455/*
1456 * ------------------------------------------------------------------------
1457 *  VideoWriteFrame()
1458 *
1459 *  Used internally to write a single frame out to the output stream.
1460 *  Returns the number of bytes written to the frame, or -1 if an error
1461 *  occurred.
1462 * ------------------------------------------------------------------------
1463 */
1464int
1465VideoWriteFrame(vidPtr, framePtr)
1466    VideoObj *vidPtr;      /* video object being updated */
1467    AVFrame *framePtr;     /* picture frame being written out */
1468{
1469    int numBytes;
1470    AVCodecContext *codecCtx;
1471    AVPacket pkt;
1472
1473#define OUTBUF_SIZE 500000
1474    uint8_t outbuf[OUTBUF_SIZE];
1475
1476    codecCtx = vidPtr->outVideoStr->codec;
1477    numBytes = avcodec_encode_video(codecCtx, outbuf, OUTBUF_SIZE, framePtr);
1478
1479    if (numBytes > 0) {
1480        av_init_packet(&pkt);
1481
1482        if (codecCtx->coded_frame->pts != AV_NOPTS_VALUE) {
1483            pkt.pts = av_rescale_q(codecCtx->coded_frame->pts,
1484                codecCtx->time_base,
1485                vidPtr->outVideoStr->time_base);
1486        }
1487        if (codecCtx->coded_frame->key_frame) {
1488            pkt.flags |= PKT_FLAG_KEY;
1489        }
1490        pkt.stream_index = vidPtr->outVideoStr->index;
1491        pkt.data = outbuf;
1492        pkt.size = numBytes;
1493
1494        /* write the compressed frame in the media file */
1495        if (av_write_frame(vidPtr->outFormatCtx, &pkt) != 0) {
1496            return -1;
1497        }
1498    }
1499    return numBytes;
1500}
1501
1502/*
1503 * ------------------------------------------------------------------------
1504 *  VideoTransform()
1505 *
1506 *  Implements the body of the "transform" method in the "video" class.
1507 *  Translates one value into another--times into frames, etc.  Handles
1508 *  the following syntax:
1509 *    obj transform frames2duration <frames>
1510 *    obj transform duration2frames <duration>
1511 * ------------------------------------------------------------------------
1512 */
1513double
1514VideoTransformFrames2Duration(vidPtr, frame)
1515    VideoObj *vidPtr;
1516    int frame;
1517{
1518    double duration;
1519    AVCodecContext *vcodecCtx;
1520    AVStream *vstreamPtr;
1521    AVRational hundred;
1522    int64_t tval;
1523
1524    hundred.num = 100;
1525    hundred.den = 1;
1526
1527    if (vidPtr == NULL) {
1528        return -1;
1529    }
1530
1531    if (vidPtr->pFormatCtx == NULL) {
1532//        Tcl_AppendResult(interp, "can't compute transformations:",
1533//            " stream not opened", (char*)NULL);
1534//        return TCL_ERROR;
1535        return -1;
1536    }
1537
1538    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1539    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1540
1541    tval = av_rescale_q((int64_t)frame, hundred, vstreamPtr->r_frame_rate);
1542    duration = 0.01*tval;
1543
1544    return duration;
1545}
1546
1547int
1548VideoTransformDuration2Frames(vidPtr, duration)
1549    VideoObj *vidPtr;
1550    double duration;
1551{
1552    int frames;
1553    AVCodecContext *vcodecCtx;
1554    AVStream *vstreamPtr;
1555    AVRational hundred;
1556    int64_t tval;
1557
1558    hundred.num = 100;
1559    hundred.den = 1;
1560
1561    if (vidPtr == NULL) {
1562        return -1;
1563    }
1564    if (vidPtr->pFormatCtx == NULL) {
1565//        Tcl_AppendResult(interp, "can't compute transformations:",
1566//            " stream not opened", (char*)NULL);
1567//        return TCL_ERROR;
1568        return -1;
1569    }
1570
1571    vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1572    vstreamPtr = vidPtr->pFormatCtx->streams[vidPtr->videoStream];
1573
1574    tval = (int64_t)(duration*100);
1575    frames = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1576    // check above for overflow
1577    // tval = av_rescale_q(tval, vstreamPtr->r_frame_rate, hundred);
1578    // sprintf(buffer, "%lld", tval);
1579
1580    return frames;
1581}
1582
1583/*
1584 * ------------------------------------------------------------------------
1585 *  VideoClose()
1586 *
1587 *  Implements the body of the _ffmpeg_close method in the "video" class.
1588 *  Closes any file opened previously by the open methods for read/write.
1589 *  If nothing is open, this does nothing.
1590 * ------------------------------------------------------------------------
1591 */
1592int
1593VideoClose(vidPtr)
1594    VideoObj *vidPtr;
1595{
1596    AVCodecContext *vcodecCtx;
1597    int i;
1598
1599    if (vidPtr == NULL) {
1600        return -1;
1601    }
1602
1603    if (vidPtr->yuvbuffer) {
1604        av_free(vidPtr->yuvbuffer);
1605        vidPtr->yuvbuffer = NULL;
1606        vidPtr->yuvw = 0;
1607        vidPtr->yuvh = 0;
1608    }
1609    if (vidPtr->pFrameYUV) {
1610        av_free(vidPtr->pFrameYUV);
1611        vidPtr->pFrameYUV = NULL;
1612    }
1613
1614    if (vidPtr->rgbbuffer) {
1615        av_free(vidPtr->rgbbuffer);
1616        vidPtr->rgbbuffer = NULL;
1617        vidPtr->rgbw = 0;
1618        vidPtr->rgbh = 0;
1619    }
1620    if (vidPtr->pFrameRGB) {
1621        av_free(vidPtr->pFrameRGB);
1622        vidPtr->pFrameRGB = NULL;
1623    }
1624
1625    if (vidPtr->scalingCtx) {
1626        sws_freeContext(vidPtr->scalingCtx);
1627        vidPtr->scalingCtx = NULL;
1628    }
1629    if (vidPtr->pFormatCtx && vidPtr->videoStream >= 0) {
1630        vcodecCtx = vidPtr->pFormatCtx->streams[vidPtr->videoStream]->codec;
1631        if (vcodecCtx) {
1632            avcodec_close(vcodecCtx);
1633        }
1634    }
1635    if (vidPtr->pFormatCtx) {
1636        av_close_input_file(vidPtr->pFormatCtx);
1637        vidPtr->pFormatCtx = NULL;
1638    }
1639
1640    if (vidPtr->outFormatCtx) {
1641        while (VideoWriteFrame(vidPtr, NULL) > 0)
1642            ; /* write out any remaining frames */
1643
1644        av_write_trailer(vidPtr->outFormatCtx);
1645
1646        for (i=0; i < vidPtr->outFormatCtx->nb_streams; i++) {
1647            avcodec_close(vidPtr->outFormatCtx->streams[i]->codec);
1648            av_freep(&vidPtr->outFormatCtx->streams[i]->codec);
1649            av_freep(&vidPtr->outFormatCtx->streams[i]);
1650        }
1651
1652        if (vidPtr->outFormatCtx->pb) {
1653            url_fclose(vidPtr->outFormatCtx->pb);
1654        }
1655
1656        av_free(vidPtr->outFormatCtx);
1657        vidPtr->outFormatCtx = NULL;
1658    }
1659
1660    /* reset the mode to null */
1661    *vidPtr->mode = '\0';
1662
1663    return 0;
1664}
Note: See TracBrowser for help on using the repository browser.