source: trunk/video/RpVideo.c @ 3929

Last change on this file since 3929 was 3929, checked in by ldelgass, 11 years ago

fix warnings

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