~ubuntu-branches/ubuntu/oneiric/bombono-dvd/oneiric

« back to all changes in this revision

Viewing changes to src/mgui/ffviewer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessio Treglia
  • Date: 2011-01-03 10:25:30 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20110103102530-mos2l5do984anaw8
Tags: 1.0.0-0ubuntu1
* New upstream release (LP: #695754).
* Build-depends on libavformat-dev,libswscale-dev.
* Recommends on ttf-freefont.
* Don't install FreeSans.ttf, already available in ttf-freefont.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// mgui/mgui/ffviewer.cpp
 
3
// This file is part of Bombono DVD project.
 
4
//
 
5
// Copyright (c) 2010 Ilya Murav'jov
 
6
//
 
7
// This program is free software; you can redistribute it and/or modify
 
8
// it under the terms of the GNU General Public License as published by
 
9
// the Free Software Foundation; either version 2 of the License, or
 
10
// (at your option) any later version.
 
11
//
 
12
// This program is distributed in the hope that it will be useful,
 
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
// GNU General Public License for more details.
 
16
//
 
17
// You should have received a copy of the GNU General Public License
 
18
// along with this program; if not, write to the Free Software
 
19
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
// 
 
21
 
 
22
#include <mgui/_pc_.h>
 
23
 
 
24
#include "ffviewer.h"
 
25
#include "img_utils.h"
 
26
#include "render/common.h" // FillEmpty()
 
27
 
 
28
#include <mlib/gettext.h>
 
29
 
 
30
/////////////////////////////////////////
 
31
// :KLUDGE: потому что riff.h не копируют
 
32
C_LINKAGE_BEGIN
 
33
 
 
34
typedef struct AVCodecTag {
 
35
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,39,00)
 
36
    enum CodecID id;
 
37
#else
 
38
    int id;
 
39
#endif
 
40
    unsigned int tag;
 
41
} AVCodecTag;
 
42
 
 
43
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,34,00)
 
44
static uint FFCodecID2Tag(CodecID codec_id) 
 
45
{
 
46
    unsigned int ff_codec_get_tag(const AVCodecTag *tags, int id);
 
47
    extern const AVCodecTag ff_codec_bmp_tags[];
 
48
    return ff_codec_get_tag(ff_codec_bmp_tags, codec_id);
 
49
}
 
50
#else
 
51
static uint FFCodecID2Tag(CodecID codec_id) 
 
52
{
 
53
    unsigned int codec_get_tag(const AVCodecTag *tags, int id);
 
54
    extern const AVCodecTag codec_bmp_tags[];
 
55
    return codec_get_tag(codec_bmp_tags, codec_id);
 
56
}
 
57
#endif
 
58
 
 
59
C_LINKAGE_END
 
60
/////////////////////////////////////////
 
61
 
 
62
static AVStream* VideoStream(FFData& ffv)
 
63
{
 
64
    return ffv.iCtx->streams[ffv.videoIdx];
 
65
}
 
66
 
 
67
AVCodecContext* GetVideoCtx(FFData& ffv)
 
68
{
 
69
    return VideoStream(ffv)->codec;
 
70
}
 
71
 
 
72
Point DAspectRatio(FFData& ffv)
 
73
{
 
74
    if( !ffv.IsOpened() )
 
75
        return Point(4, 3);
 
76
 
 
77
    // по примеру ffplay
 
78
    AVRational sample_r = VideoStream(ffv)->sample_aspect_ratio;
 
79
    if( !sample_r.num )
 
80
        sample_r = GetVideoCtx(ffv)->sample_aspect_ratio;
 
81
    if( !sample_r.num || !sample_r.den )
 
82
    {
 
83
        sample_r.num = 1;
 
84
        sample_r.den = 1;
 
85
    }
 
86
 
 
87
    Point res(sample_r.num*ffv.vidSz.x, sample_r.den*ffv.vidSz.y);
 
88
    ReducePair(res);
 
89
    return res;
 
90
}
 
91
 
 
92
static double VideoFrameLength(AVCodecContext* dec, int ticks)
 
93
{
 
94
    return av_q2d(dec->time_base) * ticks;
 
95
}
 
96
 
 
97
double FrameFPS(FFData& ffv)
 
98
{
 
99
    double res = Mpeg::PAL_SECAM_FRAME_FPS;
 
100
    if( ffv.IsOpened() )
 
101
    {
 
102
        // не работает для mpegts (Панама, Плавание)
 
103
        // хоть и пишется, что r_frame_rate "just a guess", но
 
104
        // все применяют его (mplayer, ffmpeg.c)
 
105
        res = av_q2d(VideoStream(ffv)->r_frame_rate);
 
106
 
 
107
        // не всегда работает для MPEG4+AVI (Пацаны)
 
108
        //AVCodecContext* dec = GetVideoCtx(ffv);
 
109
        //res = 1.0/VideoFrameLength(dec, dec->ticks_per_frame);
 
110
    }
 
111
    return res; 
 
112
}
 
113
 
 
114
bool IsFTSValid(int64_t ts)
 
115
{
 
116
    return ts != (int64_t)AV_NOPTS_VALUE;
 
117
}
 
118
 
 
119
static double AVTime2Sec(int64_t val)
 
120
{
 
121
    ASSERT( IsFTSValid(val) );
 
122
    return val / (double)AV_TIME_BASE;
 
123
}
 
124
 
 
125
static double Duration(AVFormatContext* ic)
 
126
{
 
127
    return AVTime2Sec(ic->duration);
 
128
}
 
129
 
 
130
static double StartTime(AVFormatContext* ic)
 
131
{
 
132
    return AVTime2Sec(ic->start_time);
 
133
}
 
134
 
 
135
void CheckOpen(VideoViewer& vwr, const std::string& fname)
 
136
{
 
137
    bool is_open = vwr.Open(fname.c_str());
 
138
    ASSERT_OR_UNUSED( is_open );
 
139
}
 
140
 
 
141
void RGBOpen(VideoViewer& vwr, const std::string& fname)
 
142
{
 
143
    //SetOutputFormat(plyr, fofRGB);
 
144
    if( !fname.empty() )
 
145
        CheckOpen(vwr, fname);
 
146
}
 
147
 
 
148
double FrameTime(VideoViewer& ffv, int fram_pos)
 
149
{
 
150
    return fram_pos / FrameFPS(ffv);
 
151
}
 
152
 
 
153
bool TryGetFrame(RefPtr<Gdk::Pixbuf>& pix, double time, FFViewer& ffv)
 
154
{
 
155
    bool res = false;
 
156
    RefPtr<Gdk::Pixbuf> img_pix = GetRawFrame(time, ffv);
 
157
    if( img_pix )
 
158
    {
 
159
        res = true;
 
160
        // заполняем кадр
 
161
        if( pix )
 
162
            //RGBA::Scale(pix, img_pix);
 
163
            RGBA::CopyOrScale(pix, img_pix);
 
164
        else
 
165
            pix = img_pix->copy();
 
166
    }
 
167
    return res;
 
168
}
 
169
 
 
170
RefPtr<Gdk::Pixbuf> GetFrame(RefPtr<Gdk::Pixbuf>& pix, double time, FFViewer& ffv)
 
171
{
 
172
    if( !TryGetFrame(pix, time, ffv) )
 
173
        FillEmpty(pix);
 
174
    return pix;
 
175
}
 
176
 
 
177
double Duration(FFData& ffv)
 
178
{
 
179
    double res = 0.;
 
180
    if( ffv.IsOpened() )
 
181
        res = Duration(ffv.iCtx);
 
182
    return res;
 
183
}
 
184
 
 
185
// длина медиа в кадрах
 
186
double FramesLength(FFViewer& ffv)
 
187
{
 
188
    return Duration(ffv) * FrameFPS(ffv);
 
189
}
 
190
 
 
191
// FFViewer
 
192
 
 
193
FFData::FFData(): iCtx(0), videoIdx(-1) {}
 
194
 
 
195
bool FFData::IsOpened()
 
196
{
 
197
    return iCtx != 0;
 
198
}
 
199
 
 
200
void CloseInfo(FFData& ffi)
 
201
{
 
202
    if( ffi.IsOpened() )
 
203
    {
 
204
        // контекст кодека закрывается отдельно
 
205
        if( ffi.videoIdx != -1 )
 
206
            avcodec_close(GetVideoCtx(ffi));
 
207
        ffi.videoIdx = -1;
 
208
 
 
209
        // судя по тому как, например, поле ctx_flags нигде не обнуляется
 
210
        // (кроме как при инициализации), то повторно использовать структуру
 
211
        // не принято -> все заново создаем при переоткрытии
 
212
        av_close_input_file(ffi.iCtx);
 
213
        ffi.iCtx = 0;
 
214
    }
 
215
}
 
216
 
 
217
static void ResetCurPTS(FFViewer& ffv);
 
218
 
 
219
FFViewer::FFViewer(): rgbBuf(0), rgbCnvCtx(0)
 
220
{
 
221
    ResetCurPTS(*this);
 
222
}
 
223
 
 
224
FFViewer::~FFViewer()
 
225
{
 
226
    Close();
 
227
}
 
228
 
 
229
static bool IsCurPTS(FFViewer& ffv)
 
230
{
 
231
    return IsTSValid(ffv.curPTS);
 
232
}
 
233
 
 
234
void FFViewer::Close()
 
235
{
 
236
    if( IsOpened() )
 
237
    {
 
238
        ResetCurPTS(*this);
 
239
 
 
240
        av_free(rgbBuf);
 
241
        rgbBuf = 0;
 
242
        sws_freeContext(rgbCnvCtx);
 
243
        rgbCnvCtx = 0;
 
244
    }
 
245
 
 
246
    CloseInfo(*this);
 
247
}
 
248
 
 
249
static void DumpIFile(AVFormatContext* ic, int idx = 0, const std::string& fname = std::string())
 
250
{
 
251
    //
 
252
    // Инфо о всем контейнере как ее показывает ffmpeg
 
253
    //     
 
254
    // idx - идентификатор файла (для клиента)
 
255
    //const char* fname = "n/a";
 
256
    // входной/выходной файл
 
257
    int is_output = 0;
 
258
    dump_format(ic, idx, fname.c_str(), is_output);
 
259
}
 
260
 
 
261
Point VideoSize(AVCodecContext* dec)
 
262
{
 
263
    return Point(dec->width, dec->height);
 
264
}
 
265
 
 
266
static bool SeekCall(AVFormatContext* ic, int64_t ts, bool is_byte_seek)
 
267
{
 
268
    int flags = is_byte_seek ? AVSEEK_FLAG_BYTE
 
269
        : AVSEEK_FLAG_BACKWARD; // чтоб раньше времени пришли
 
270
 
 
271
    // вполне подойдет поиск по умолчальному потоку (все равно видео выберут)
 
272
    int av_res = av_seek_frame(ic, -1, ts, flags);
 
273
    return av_res == 0;
 
274
}
 
275
 
 
276
static bool IsFFError(int av_res)
 
277
{
 
278
    return av_res < 0;
 
279
}
 
280
 
 
281
static unsigned char GetChar(uint tag, int bit_begin)
 
282
{
 
283
    return (tag>>bit_begin) & 0xFF;
 
284
}
 
285
 
 
286
static bool SetIndex(int& idx, int i, bool b)
 
287
{
 
288
    bool res = (idx == -1) && b;
 
289
    if( res )
 
290
        idx = i;
 
291
    return res;
 
292
}
 
293
 
 
294
bool OpenInfo(FFData& ffi, const char* fname, std::string& err_str)
 
295
{
 
296
    av_register_all();
 
297
 
 
298
    ASSERT( !ffi.IsOpened() );
 
299
    bool res = false;
 
300
 
 
301
    // AVInputFormat* для форсирования формата контейнера
 
302
    // создается из av_find_input_format(str), где str из опции -f для ffmpeg
 
303
    // (ffmpeg -formats)
 
304
    AVInputFormat* file_iformat = 0;
 
305
    // для уточнения параметров входного потока; используется в случаях, когда
 
306
    // по самому потоку невозможно определить их (не для контейнеров, а для 
 
307
    // элементарных потоков
 
308
    AVFormatParameters* ap = 0;
 
309
    // всегда нуль (ffmpeg, ffplay)
 
310
    int buf_size = 0;
 
311
 
 
312
    AVFormatContext* ic = 0;
 
313
    int av_res = av_open_input_file(&ic, fname, file_iformat, buf_size, ap);
 
314
    if( IsFFError(av_res) ) // ошибка
 
315
    {
 
316
        switch( av_res )
 
317
        {
 
318
        case AVERROR_NOENT:
 
319
            // :TODO: решить, ставить в конце точки или нет (сообщения пользователю
 
320
            // показывается не HIG-ого)
 
321
            err_str = _("No such file");
 
322
            break;
 
323
        case AVERROR_NOFMT:
 
324
            err_str = _("Unknown file format");
 
325
            break;
 
326
        case AVERROR_UNKNOWN:
 
327
        default:
 
328
            err_str = boost::format("FFmpeg unknown error: %1%") % av_res % bf::stop;
 
329
            break;
 
330
        }
 
331
    }
 
332
    else
 
333
    {
 
334
        //
 
335
        // * файл открыт
 
336
        //
 
337
        ffi.iCtx = ic;
 
338
    
 
339
        av_res = av_find_stream_info(ic);
 
340
        if( IsFFError(av_res) )
 
341
        {
 
342
            // например .webm для FFmpeg <= 0.5 
 
343
            err_str = BF_("Can't find stream information: %1%") % av_res % bf::stop;
 
344
            return false;
 
345
        }
 
346
        if( LogFilter->IsEnabled(::Log::Info) )
 
347
            DumpIFile(ic);
 
348
    
 
349
        int video_idx = -1, audio_idx = -1;
 
350
        for( int i=0; i < (int)ic->nb_streams; i++ )
 
351
        {
 
352
            AVStream* strm = ic->streams[i];
 
353
            AVCodecContext* avctx = strm->codec;
 
354
            if( SetIndex(video_idx, i, avctx->codec_type == CODEC_TYPE_VIDEO) )
 
355
                ;
 
356
            else
 
357
                // для демиксера имеет значение только NONE и ALL
 
358
                strm->discard = AVDISCARD_ALL;
 
359
 
 
360
            SetIndex(audio_idx, i, avctx->codec_type == CODEC_TYPE_AUDIO);
 
361
        }
 
362
 
 
363
        if( video_idx == -1 )
 
364
        {
 
365
            err_str = _("No video stream found");
 
366
            return false;
 
367
        }
 
368
        // включить по требованию (и поправить flower.mpg)
 
369
        //if( audio_idx == -1 )
 
370
        //{
 
371
        //    err_str = _("No audio stream found");
 
372
        //    return false;
 
373
        //}
 
374
                    
 
375
        if( !IsFTSValid(ic->duration) )
 
376
        {
 
377
            err_str = _("Can't find the file duration");
 
378
            return false;
 
379
        }
 
380
 
 
381
        if( !IsFTSValid(ic->start_time) )
 
382
        {
 
383
            // в 99% отсутствие нач. времени - элементарный поток = без контейнера;
 
384
            // см. особенности ffmpeg, update_initial_timestamps()
 
385
            err_str = _("Start time of the file is unknown");
 
386
            return false;
 
387
        }
 
388
 
 
389
        if( !SeekCall(ic, ic->start_time, false) )
 
390
        {
 
391
            // проверка индекса/возможности перемещения
 
392
            err_str = _("Can't seek through the file");
 
393
            return false;
 
394
        }
 
395
    
 
396
        // открытие кодека
 
397
        AVCodecContext* dec = ic->streams[video_idx]->codec;
 
398
        // для H.264 и плохих TS
 
399
        dec->strict_std_compliance = FF_COMPLIANCE_STRICT;
 
400
    
 
401
        // Chromium зачем-то выставляет явно, но такие значения уже по умолчанию
 
402
        //dec->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
 
403
        //dec->error_recognition = FF_ER_CAREFUL;
 
404
    
 
405
        uint tag = FFCodecID2Tag(dec->codec_id);
 
406
        std::string tag_str = boost::format("0x%1$04x") % tag % bf::stop;
 
407
        unsigned char c0 = GetChar(tag, 0), c8 = GetChar(tag, 8), 
 
408
            c16 = GetChar(tag, 16), c24 = GetChar(tag, 24);
 
409
        if( isprint(c0) && isprint(c8) && isprint(c16) && isprint(c24) )
 
410
            tag_str = boost::format("%1%%2%%3%%4% / %5%") 
 
411
                % c0 % c8 % c16 % c24 % tag_str % bf::stop;
 
412
                            
 
413
        // AVCodec - это одиночка, а AVCodecContext - состояние для него
 
414
        // в соответ. потоке контейнера 
 
415
        AVCodec* codec = avcodec_find_decoder(dec->codec_id);
 
416
        if( !codec )
 
417
        {
 
418
            err_str = BF_("No decoder found for the stream: %1%") % tag_str % bf::stop;
 
419
            return false;
 
420
        }
 
421
 
 
422
        if( IsFFError(avcodec_open(dec, codec)) )
 
423
        {
 
424
            err_str = boost::format("Can't open codec: %1%") % tag_str % bf::stop;
 
425
            return false;
 
426
        }
 
427
 
 
428
        //
 
429
        // * декодер настроен
 
430
        //
 
431
        ffi.videoIdx = video_idx;
 
432
    
 
433
        Point sz(VideoSize(dec));
 
434
        if( sz.IsNull() )
 
435
        {
 
436
            err_str = "Video has null size";
 
437
            return false;
 
438
        }
 
439
        ffi.vidSz = sz;
 
440
 
 
441
        res = true;
 
442
    }
 
443
    return res;
 
444
}
 
445
 
 
446
FFInfo::FFInfo() {}
 
447
 
 
448
FFInfo::FFInfo(const std::string& fname)
 
449
{
 
450
    std::string err_str;
 
451
    bool res = OpenInfo(*this, fname.c_str(), err_str);
 
452
    ASSERT_OR_UNUSED( res );
 
453
}
 
454
 
 
455
FFInfo::~FFInfo()
 
456
{
 
457
    CloseInfo(*this);
 
458
}
 
459
 
 
460
bool CanOpenAsFFmpegVideo(const char* fname, std::string& err_str)
 
461
{
 
462
    FFInfo ffi;
 
463
    return OpenInfo(ffi, fname, err_str);
 
464
}
 
465
 
 
466
bool FFViewer::Open(const char* fname, std::string& err_str)
 
467
{
 
468
    // * закрываем открытое ранее
 
469
    Close();
 
470
 
 
471
    bool res = OpenInfo(*this, fname, err_str);
 
472
    if( res )
 
473
    {
 
474
        Point sz(vidSz);
 
475
        // по умолчанию такое использует ffmpeg/ffplay
 
476
        // (для переопределения у них используется временный&глобальный
 
477
        //     sws_opts = sws_getContext(16,16,0, 16,16,0, sws_flags, NULL,NULL,NULL);
 
478
        //     opt_default(); // обновление sws_opts по -sws_flags
 
479
        //     sws_flags = av_get_int(sws_opts, "sws_flags", NULL); // = sws_opts.flags    
 
480
        int sws_flags = SWS_BICUBIC;
 
481
        // при сборке с --enable-runtime-cpudetect (появилось после 0.5), который полюбили пакетировщики,
 
482
        // лучшая оптимизация выбирается на этапе выполнения, а не сборке; однако для 0.6 времени
 
483
        // maverick оно еще не доделано, см. http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=1&t=1185
 
484
        // :KLUDGE: потому добавляем явно
 
485
        sws_flags |= SWS_CPU_CAPS_MMX|SWS_CPU_CAPS_MMX2;
 
486
 
 
487
        // :TRICKY: почему-то ffmpeg'у "нравится" BGR24 и не нравиться RGB24 в плане использования
 
488
        // MMX (ускорения); цена по времени неизвестна,- используем только ради того, чтобы не было 
 
489
        // предупреждений
 
490
        // Другой вариант - PIX_FMT_RGB32, но там зависимый порядок байтов (в GdkPixbuf - нет) и
 
491
        // мы нацелены на RGB24
 
492
        // :TODO: с версии LIBSWSCALE_VERSION_INT >= 0.8.11 появился прямой yuv -> rgb24, поправить
 
493
        PixelFormat dst_pf = PIX_FMT_BGR24; // PIX_FMT_RGB24;
 
494
        rgbCnvCtx = sws_getContext(sz.x, sz.y, GetVideoCtx(*this)->pix_fmt, sz.x, sz.y,
 
495
            dst_pf, sws_flags, 0, 0, 0);
 
496
        ASSERT( rgbCnvCtx );
 
497
    
 
498
        Point dst_sz(sz);
 
499
        uint8_t* rgbBuf = (uint8_t*)av_malloc(avpicture_get_size(dst_pf, dst_sz.x, dst_sz.y) * sizeof(uint8_t));
 
500
        avcodec_get_frame_defaults(&rgbFrame); // не помешает
 
501
        avpicture_fill((AVPicture*)&rgbFrame, rgbBuf, dst_pf, dst_sz.x, dst_sz.y);
 
502
    }
 
503
    else
 
504
        // защита от неполных открытий
 
505
        Close();
 
506
    return res;
 
507
}
 
508
 
 
509
bool FFViewer::Open(const char* fname)
 
510
{
 
511
    std::string err;
 
512
    return Open(fname, err);
 
513
}
 
514
 
 
515
static double TS2Time(int64_t ts, FFViewer& ffv)
 
516
{
 
517
    double tm = INV_TS;
 
518
    if( IsFTSValid(ts) )
 
519
        tm = ts * av_q2d(VideoStream(ffv)->time_base);
 
520
    return tm;
 
521
}
 
522
 
 
523
static bool IsInHurry(AVCodecContext* dec)
 
524
{
 
525
    return dec->hurry_up != 0;
 
526
}
 
527
 
 
528
struct HurryModeEnabler
 
529
{
 
530
    AVCodecContext* dec;
 
531
 
 
532
    HurryModeEnabler(AVCodecContext* dec_): dec(dec_)
 
533
    {
 
534
        // как признак (хоть и устаревший)
 
535
        dec->hurry_up = 1;
 
536
        // Прирост скорости (h264): 
 
537
        // - AVDISCARD_NONREF: 2x
 
538
        // - AVDISCARD_BIDIR: для h264 (и других современных кодеков?) разница в скорости 
 
539
        //   с AVDISCARD_NONREF небольшая, зато корректно все необходимые кадры распакуем
 
540
        // - AVDISCARD_NONKEY: неотличим от AVDISCARD_ALL, так как I-кадры очень
 
541
        //   редки
 
542
        // - AVDISCARD_ALL: 3,6-4 (но тогда декодер вообще перестанет выдавать
 
543
        //   кадры, например для mpeg4 - без переделки нельзя использовать)
 
544
        // 
 
545
        // 
 
546
        dec->skip_frame = AVDISCARD_NONREF;
 
547
 
 
548
        // незначительный прирост дают
 
549
        //dec->skip_loop_filter = AVDISCARD_ALL;
 
550
        //dec->skip_idct = AVDISCARD_ALL;
 
551
 
 
552
    }
 
553
   ~HurryModeEnabler()
 
554
    {
 
555
        dec->hurry_up = 0;
 
556
        dec->skip_frame = AVDISCARD_DEFAULT;
 
557
        //dec->skip_idct = AVDISCARD_DEFAULT;
 
558
        //dec->skip_loop_filter = AVDISCARD_DEFAULT;
 
559
    }
 
560
};
 
561
 
 
562
 
 
563
// для включения логов (де)кодера на время,
 
564
// чтобы другое консоль не забивало
 
565
struct CodecDebugEnabler
 
566
{
 
567
    int oldLvl;
 
568
 
 
569
    CodecDebugEnabler(AVCodecContext* dec, int flags)
 
570
    {
 
571
        oldLvl = av_log_get_level();
 
572
        av_log_set_level(AV_LOG_DEBUG);
 
573
        dec->debug |= flags;
 
574
    }
 
575
   ~CodecDebugEnabler()
 
576
    {
 
577
        av_log_set_level(oldLvl);
 
578
    }
 
579
};
 
580
 
 
581
static void DoVideoDecode(FFViewer& ffv, int& got_picture, AVPacket* pkt)
 
582
{
 
583
    // FF_DEBUG_PICT_INFO - вывод типов декодируемых картинок
 
584
    // FF_DEBUG_MMCO - (h.264) управление зависимыми кадрами + порядок кадров (poc) 
 
585
    //CodecDebugEnabler cde(GetVideoCtx(ffv), FF_DEBUG_PICT_INFO);
 
586
 
 
587
    AVFrame& picture = ffv.srcFrame;
 
588
    avcodec_get_frame_defaults(&picture); // ffmpeg.c очищает каждый раз
 
589
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,00)
 
590
    if( !pkt )
 
591
    {
 
592
        // никогда бы не использовал alloca(), но не хочется создавать 
 
593
        // на стеке лишние байты для исключительных случаев 
 
594
        pkt = (AVPacket*)alloca(sizeof(AVPacket));
 
595
        av_init_packet(pkt);
 
596
        pkt->data = 0;
 
597
        pkt->size = 0;
 
598
    }
 
599
    int av_res = avcodec_decode_video2(GetVideoCtx(ffv), &picture, &got_picture, pkt);
 
600
#else
 
601
    const uint8_t* buf = 0;
 
602
    int buf_sz = 0;
 
603
    if( pkt )
 
604
    {
 
605
        buf = pkt->data;
 
606
        buf_sz = pkt->size;
 
607
    }
 
608
    int av_res = avcodec_decode_video(GetVideoCtx(ffv), &picture, &got_picture, buf, buf_sz);
 
609
#endif
 
610
    if( av_res < 0 )
 
611
        // ничего не требуется делать в случае ошибок
 
612
        LOG_WRN << "Error while decoding frame!" << io::endl;
 
613
}
 
614
 
 
615
static void ResetCurPTS(FFViewer& ffv)
 
616
{
 
617
    ffv.curPTS  = INV_TS;
 
618
    ffv.prevPTS = INV_TS;
 
619
}
 
620
 
 
621
static void UpdatePTS(FFViewer& ffv, double new_pts)
 
622
{
 
623
    ffv.prevPTS = ffv.curPTS;
 
624
    ffv.curPTS  = new_pts;
 
625
}
 
626
 
 
627
static bool DoDecode(FFViewer& ffv)
 
628
{
 
629
    double cur_pts = ffv.curPTS;
 
630
    AVCodecContext* dec = GetVideoCtx(ffv);
 
631
 
 
632
    AVPacket pkt;
 
633
    int got_picture = 0;
 
634
 
 
635
    // предположительное время начала следующего кадра (если не будет явно 
 
636
    // установлено) - расчет до следующего av_read_frame()
 
637
    double next_pts = cur_pts;
 
638
    if( IsTSValid(cur_pts) )
 
639
    {
 
640
        // в идеале длительность уже была рассчитана в предыдущем pkt->duration;
 
641
        // пока же сделаем копипаст как в ffmpeg.c - см. особенности ffmpeg (compute_pkt_fields()) 
 
642
        AVStream* st = VideoStream(ffv);
 
643
        int ticks    = st->parser ? st->parser->repeat_pict + 1 : dec->ticks_per_frame ;
 
644
        next_pts    += VideoFrameLength(dec, ticks);
 
645
    }
 
646
 
 
647
    bool res = true;
 
648
    int av_res = av_read_frame(ffv.iCtx, &pkt);
 
649
    if( av_res >= 0 )
 
650
    {
 
651
        // хотя только одно видео фильтруем, по ходу работы
 
652
        // может найтись новый поток - 
 
653
        // samples.mplayerhq.hu/MPEG2/dothack2.mpg (субтитры на 8й секунде)
 
654
        if( pkt.stream_index == ffv.videoIdx )
 
655
        {
 
656
            dec->reordered_opaque = pkt.pts;
 
657
            
 
658
            DoVideoDecode(ffv, got_picture, &pkt);
 
659
        }
 
660
        av_free_packet(&pkt);
 
661
    }
 
662
    else if( av_res == AVERROR_EOF ) // для mpegts также -EIO приходит
 
663
    {
 
664
        // остатки в декодере забираем
 
665
        DoVideoDecode(ffv, got_picture, 0);
 
666
 
 
667
        if( !got_picture )
 
668
            // больше ничего нет, даже того что было
 
669
            res = false;
 
670
    }
 
671
    else
 
672
        // остальные проблемы демиксера
 
673
        res = false;
 
674
 
 
675
    if( res )
 
676
    {
 
677
        if( got_picture )
 
678
        {
 
679
            // * PTS текущего кадра
 
680
            cur_pts = TS2Time(ffv.srcFrame.reordered_opaque, ffv);
 
681
            if( !IsTSValid(cur_pts) )
 
682
                cur_pts = next_pts;
 
683
 
 
684
            // pts: граничные случаи
 
685
            double cur_dts = TS2Time(pkt.dts, ffv);
 
686
            if( IsTSValid(cur_dts) )
 
687
            {
 
688
                if( !IsTSValid(cur_pts) )
 
689
                    // первые кадры для avi-контейнеров (не B-кадры) имеют только dts
 
690
                    cur_pts = cur_dts;
 
691
                else if( IsInHurry(dec) )
 
692
                    // помимо не декодирования декодер еще и пропускает B-кадры,
 
693
                    // а значит можно пропустить pts
 
694
                    cur_pts = std::max(cur_pts, cur_dts);
 
695
            }
 
696
 
 
697
            UpdatePTS(ffv, cur_pts);
 
698
        }
 
699
    }
 
700
    else
 
701
        // очередного кадра нет => следующий SetTime()
 
702
        // не должен все обрушить
 
703
        ResetCurPTS(ffv);
 
704
 
 
705
    return res;
 
706
}
 
707
 
 
708
static double Delta(double time, double ts, FFViewer& ffv)
 
709
{
 
710
    ASSERT( IsTSValid(ts) );
 
711
    return (time - ts) * FrameFPS(ffv);
 
712
}
 
713
 
 
714
static double Delta(double time, FFViewer& ffv)
 
715
{
 
716
    return Delta(time, ffv.curPTS, ffv);
 
717
}
 
718
 
 
719
const double NEG_WIN_DELTA = -1.; // в кадрах
 
720
const double MAX_WIN_DELTA = 300.; // 12.;
 
721
// используем вместо нуля из-за погрешности преобразований
 
722
// AV_TIME_BASE(int64_t) <-> секунды(double)
 
723
const double NULL_DELTA    = 0.0001;
 
724
 
 
725
typedef boost::function<bool(FFViewer&)> FFVFunctor;
 
726
 
 
727
static bool DecodeLoop(FFViewer& ffv, const FFVFunctor& condition_fnr)
 
728
{
 
729
    bool res = true;
 
730
    while( !condition_fnr(ffv) )
 
731
        if( !DoDecode(ffv) )
 
732
        {
 
733
            res = false;
 
734
            break;
 
735
        }
 
736
    return res;
 
737
}
 
738
 
 
739
static bool IsFrameFound(double delta)
 
740
{
 
741
    return delta <= NULL_DELTA;
 
742
}
 
743
 
 
744
static bool IsFrameFound(double time, double diff, FFViewer& ffv)
 
745
{
 
746
    return IsFrameFound(Delta(time, ffv) - diff);
 
747
}
 
748
 
 
749
// доводка до разницы <= diff (в кадрах)
 
750
static bool DecodeForDiff(double time, double diff, FFViewer& ffv)
 
751
{
 
752
    return DecodeLoop(ffv, bb::bind(&IsFrameFound, time, diff, _1));
 
753
}
 
754
 
 
755
//static bool IsFrameLate(double delta)
 
756
//{
 
757
//    return delta < NEG_WIN_DELTA - NULL_DELTA;
 
758
//}
 
759
//
 
760
static bool IsFrameLate(double time, FFViewer& ffv)
 
761
{
 
762
    bool res = false;
 
763
    if( IsTSValid(ffv.prevPTS) )
 
764
    {
 
765
        double delta = Delta(time, ffv.prevPTS, ffv);
 
766
        res = IsFrameFound(delta);
 
767
    }
 
768
    else
 
769
        res = Delta(time, ffv) < NEG_WIN_DELTA - NULL_DELTA;
 
770
    return res;
 
771
}
 
772
 
 
773
static bool SeekSetTime(FFViewer& ffv, double time);
 
774
 
 
775
static bool DecodeTill(FFViewer& ffv, double time, bool can_seek)
 
776
{
 
777
    ASSERT( IsCurPTS(ffv) );
 
778
 
 
779
    bool res = false;
 
780
    // * проверка диапазона
 
781
    double orig_delta = Delta(time, ffv);
 
782
    bool wish_seek = IsFrameLate(time, ffv) || (orig_delta > MAX_WIN_DELTA);
 
783
    if( wish_seek && can_seek )
 
784
        res = SeekSetTime(ffv, time);
 
785
    else
 
786
    {
 
787
        if( wish_seek )
 
788
        {
 
789
            LOG_WRN << "Seek delta overflow: " << orig_delta << io::endl;
 
790
            if( orig_delta > 0 )
 
791
                // уменьшаем до приемлемого, явно
 
792
                time = ffv.curPTS + MAX_WIN_DELTA/FrameFPS(ffv);
 
793
        }
 
794
 
 
795
        // * допустимая разница, доводим
 
796
        LOG_INF << "Decoding delta: " << Delta(time, ffv) << io::endl;
 
797
 
 
798
        {
 
799
            HurryModeEnabler hme(GetVideoCtx(ffv));
 
800
            // кадр может длится <= 3 тактов по декодеру, но явный PTS
 
801
            // может и больше
 
802
            res = DecodeForDiff(time, 10, ffv);
 
803
        }
 
804
 
 
805
        // оставшееся в полном режиме
 
806
        res = res && DecodeForDiff(time, 0, ffv);
 
807
    }
 
808
 
 
809
    return res;
 
810
}
 
811
 
 
812
static bool DoSeek(FFViewer& ffv, int64_t ts, bool is_byte_seek)
 
813
{
 
814
    // * перемещение
 
815
    // если перемещение не прошло (индекс частично поломан), то полагаем, что
 
816
    // состояние прежнее (обнуление не требуется)
 
817
    bool res = SeekCall(ffv.iCtx, ts, is_byte_seek);
 
818
    if( res )
 
819
    {
 
820
        // * обнуляем
 
821
        ResetCurPTS(ffv);
 
822
    
 
823
        // сбрасываем буфера декодеров (отдельно от seek)
 
824
        avcodec_flush_buffers(GetVideoCtx(ffv));
 
825
    
 
826
        // * до первого кадра с PTS
 
827
        res = DecodeLoop(ffv, &IsCurPTS);
 
828
    }
 
829
    return res;
 
830
}
 
831
 
 
832
bool TimeSeek(FFViewer& ffv, double seek_time, double time)
 
833
{
 
834
    bool res = DoSeek(ffv, AV_TIME_BASE * seek_time, false);
 
835
 
 
836
    return res && !IsFrameLate(time, ffv);
 
837
}
 
838
 
 
839
static double StartTime(FFViewer& ffv)
 
840
{
 
841
    return StartTime(ffv.iCtx);
 
842
}
 
843
 
 
844
static bool CanByteSeek(AVFormatContext* ic)
 
845
{
 
846
    // переход по позиции не работает для avi, mkv - см. особенности ffmpeg
 
847
    // однако для без-заголовочных демиксеров (MPEG-PS, MPEG-TS) требуется
 
848
 
 
849
    typedef std::map<std::string, AVInputFormat*> Map;
 
850
    static Map map;
 
851
    if( map.empty() )
 
852
    {
 
853
        // для видео < 1 секунды показывает пусто
 
854
        map["mpeg"]   = av_find_input_format("mpeg");
 
855
        // перейти в начало иногда возможно только так,- PanamaCanal_1080p-h264.ts
 
856
        map["mpegts"] = av_find_input_format("mpegts");
 
857
    }
 
858
 
 
859
    bool res = false;
 
860
    boost_foreach( Map::reference ref, map )
 
861
        if( ic->iformat == ref.second )
 
862
        {
 
863
            res = true;
 
864
            break;
 
865
        }
 
866
    return res;
 
867
}
 
868
 
 
869
static bool SeekSetTime(FFViewer& ffv, double time)
 
870
{
 
871
    bool is_begin = false;
 
872
    double start_time = StartTime(ffv);
 
873
    for( int i=0; i<4 && !is_begin; i++ )
 
874
    {
 
875
        int n = (1 << i) - 1; // 0, 1, 3, 7
 
876
        double seek_time = time - n;
 
877
 
 
878
        if( seek_time <= start_time )
 
879
        {
 
880
            is_begin = true;
 
881
            break;
 
882
        }
 
883
 
 
884
        if( TimeSeek(ffv, seek_time, time) )
 
885
            break;
 
886
    }
 
887
 
 
888
    if( is_begin )
 
889
    {
 
890
        if( !TimeSeek(ffv, start_time, time) && CanByteSeek(ffv.iCtx) )
 
891
            // тогда переходим в начало файла
 
892
            DoSeek(ffv, ffv.iCtx->data_offset, true);
 
893
        //TimeSeek(ffv, start_time, time);
 
894
 
 
895
        // некоторое видео глючит в начале (Hellboy), из-за чего
 
896
        // последовательный доступ выполняется с перескоками -
 
897
        // явно ставим пред. кадр
 
898
        if( IsCurPTS(ffv) )
 
899
            // :KLUDGE: -1 уже занят, поэтому -0.5
 
900
            // (система работает, пока start_time не бывает отрицательным)
 
901
            ffv.prevPTS = start_time - 0.5;
 
902
    }
 
903
 
 
904
    return IsCurPTS(ffv) && DecodeTill(ffv, time, false);
 
905
}
 
906
 
 
907
// время без смещения
 
908
bool SetTime(FFViewer& ffv, double time)
 
909
{
 
910
    ASSERT( ffv.IsOpened() );
 
911
 
 
912
    if( (time < 0) || (time > Duration(ffv.iCtx)) )
 
913
        return false;
 
914
    time += StartTime(ffv);
 
915
 
 
916
    // :TODO: рассчитывать только если включено логирование
 
917
    double GetClockTime();
 
918
    double cur_time = GetClockTime();
 
919
 
 
920
    bool res = false;
 
921
    if( !IsCurPTS(ffv) )
 
922
        res = SeekSetTime(ffv, time);
 
923
    else
 
924
        res = DecodeTill(ffv, time, true);
 
925
 
 
926
    if( res )
 
927
    {
 
928
        // декодер должен выдать результат
 
929
        ASSERT( ffv.srcFrame.data[0] );
 
930
 
 
931
        // * перевод в RGB
 
932
        Point sz(ffv.vidSz);
 
933
        // не допускаем смены разрешения в меньшую сторону, чтобы
 
934
        // не вылететь при скалировании; вообще задумано, что разрешение
 
935
        // не меняется
 
936
        // :TODO: по требованию реализовать смену обновлением контекста 
 
937
        // (размер rgb_frame оставить постоянным!) 
 
938
        // Пример смены: ElephDream_720-h264.mov, 405->406; причем 
 
939
        // vlc видит оба разрешения как Resolution/Display Resolution
 
940
        ASSERT_RTL( !(VideoSize(GetVideoCtx(ffv)) < sz) );
 
941
        AVFrame& rgb_frame = ffv.rgbFrame;
 
942
        // не очень понятно как пользовать аргументы 4, 5
 
943
        sws_scale(ffv.rgbCnvCtx, ffv.srcFrame.data, ffv.srcFrame.linesize,
 
944
                  0, sz.y, rgb_frame.data, rgb_frame.linesize);
 
945
        uint8_t* buf = rgb_frame.data[0];
 
946
        uint8_t* ptr = buf;
 
947
        uint8_t tmp;
 
948
        for( int y=0; y<sz.y; y++ )
 
949
            for( int x=0; x<sz.x; x++, ptr += 3 )
 
950
            {
 
951
                // b <-> r
 
952
                tmp = ptr[0];
 
953
                ptr[0] = ptr[2];
 
954
                ptr[2] = tmp;
 
955
            }
 
956
    }
 
957
 
 
958
    LOG_INF << "Time setting: " << time << "; current PTS: " << ffv.curPTS << "; previous PTS: " << ffv.prevPTS << io::endl;
 
959
    LOG_INF << "SetTime() timing: " << GetClockTime() - cur_time << io::endl;
 
960
 
 
961
    return res;
 
962
}
 
963
 
 
964
RefPtr<Gdk::Pixbuf> GetRawFrame(double time, FFViewer& ffv)
 
965
{
 
966
    RefPtr<Gdk::Pixbuf> res_pix;
 
967
    if( ffv.IsOpened() && SetTime(ffv, time) )
 
968
        res_pix = CreateFromData(ffv.rgbFrame.data[0], ffv.vidSz, false);
 
969
    return res_pix;
 
970
}
 
971