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

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

adding aspect ratio calculations.
disable analyze button when no movie has been choosen in piv
rename some functions

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