source: trunk/video/RpVideo.c @ 3919

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

Fix a few unused variable warnings

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