2
// mgui/mgui/ffviewer.cpp
3
// This file is part of Bombono DVD project.
5
// Copyright (c) 2010 Ilya Murav'jov
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.
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.
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
22
#include <mgui/_pc_.h>
25
#include "img_utils.h"
26
#include "render/common.h" // FillEmpty()
28
#include <mlib/gettext.h>
30
/////////////////////////////////////////
31
// :KLUDGE: потому что riff.h не копируют
34
typedef struct AVCodecTag {
35
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,39,00)
43
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,34,00)
44
static uint FFCodecID2Tag(CodecID codec_id)
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);
51
static uint FFCodecID2Tag(CodecID codec_id)
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);
60
/////////////////////////////////////////
62
static AVStream* VideoStream(FFData& ffv)
64
return ffv.iCtx->streams[ffv.videoIdx];
67
AVCodecContext* GetVideoCtx(FFData& ffv)
69
return VideoStream(ffv)->codec;
72
Point DAspectRatio(FFData& ffv)
78
AVRational sample_r = VideoStream(ffv)->sample_aspect_ratio;
80
sample_r = GetVideoCtx(ffv)->sample_aspect_ratio;
81
if( !sample_r.num || !sample_r.den )
87
Point res(sample_r.num*ffv.vidSz.x, sample_r.den*ffv.vidSz.y);
92
static double VideoFrameLength(AVCodecContext* dec, int ticks)
94
return av_q2d(dec->time_base) * ticks;
97
double FrameFPS(FFData& ffv)
99
double res = Mpeg::PAL_SECAM_FRAME_FPS;
102
// не работает для mpegts (Панама, Плавание)
103
// хоть и пишется, что r_frame_rate "just a guess", но
104
// все применяют его (mplayer, ffmpeg.c)
105
res = av_q2d(VideoStream(ffv)->r_frame_rate);
107
// не всегда работает для MPEG4+AVI (Пацаны)
108
//AVCodecContext* dec = GetVideoCtx(ffv);
109
//res = 1.0/VideoFrameLength(dec, dec->ticks_per_frame);
114
bool IsFTSValid(int64_t ts)
116
return ts != (int64_t)AV_NOPTS_VALUE;
119
static double AVTime2Sec(int64_t val)
121
ASSERT( IsFTSValid(val) );
122
return val / (double)AV_TIME_BASE;
125
static double Duration(AVFormatContext* ic)
127
return AVTime2Sec(ic->duration);
130
static double StartTime(AVFormatContext* ic)
132
return AVTime2Sec(ic->start_time);
135
void CheckOpen(VideoViewer& vwr, const std::string& fname)
137
bool is_open = vwr.Open(fname.c_str());
138
ASSERT_OR_UNUSED( is_open );
141
void RGBOpen(VideoViewer& vwr, const std::string& fname)
143
//SetOutputFormat(plyr, fofRGB);
145
CheckOpen(vwr, fname);
148
double FrameTime(VideoViewer& ffv, int fram_pos)
150
return fram_pos / FrameFPS(ffv);
153
bool TryGetFrame(RefPtr<Gdk::Pixbuf>& pix, double time, FFViewer& ffv)
156
RefPtr<Gdk::Pixbuf> img_pix = GetRawFrame(time, ffv);
162
//RGBA::Scale(pix, img_pix);
163
RGBA::CopyOrScale(pix, img_pix);
165
pix = img_pix->copy();
170
RefPtr<Gdk::Pixbuf> GetFrame(RefPtr<Gdk::Pixbuf>& pix, double time, FFViewer& ffv)
172
if( !TryGetFrame(pix, time, ffv) )
177
double Duration(FFData& ffv)
181
res = Duration(ffv.iCtx);
185
// длина медиа в кадрах
186
double FramesLength(FFViewer& ffv)
188
return Duration(ffv) * FrameFPS(ffv);
193
FFData::FFData(): iCtx(0), videoIdx(-1) {}
195
bool FFData::IsOpened()
200
void CloseInfo(FFData& ffi)
204
// контекст кодека закрывается отдельно
205
if( ffi.videoIdx != -1 )
206
avcodec_close(GetVideoCtx(ffi));
209
// судя по тому как, например, поле ctx_flags нигде не обнуляется
210
// (кроме как при инициализации), то повторно использовать структуру
211
// не принято -> все заново создаем при переоткрытии
212
av_close_input_file(ffi.iCtx);
217
static void ResetCurPTS(FFViewer& ffv);
219
FFViewer::FFViewer(): rgbBuf(0), rgbCnvCtx(0)
224
FFViewer::~FFViewer()
229
static bool IsCurPTS(FFViewer& ffv)
231
return IsTSValid(ffv.curPTS);
234
void FFViewer::Close()
242
sws_freeContext(rgbCnvCtx);
249
static void DumpIFile(AVFormatContext* ic, int idx = 0, const std::string& fname = std::string())
252
// Инфо о всем контейнере как ее показывает ffmpeg
254
// idx - идентификатор файла (для клиента)
255
//const char* fname = "n/a";
256
// входной/выходной файл
258
dump_format(ic, idx, fname.c_str(), is_output);
261
Point VideoSize(AVCodecContext* dec)
263
return Point(dec->width, dec->height);
266
static bool SeekCall(AVFormatContext* ic, int64_t ts, bool is_byte_seek)
268
int flags = is_byte_seek ? AVSEEK_FLAG_BYTE
269
: AVSEEK_FLAG_BACKWARD; // чтоб раньше времени пришли
271
// вполне подойдет поиск по умолчальному потоку (все равно видео выберут)
272
int av_res = av_seek_frame(ic, -1, ts, flags);
276
static bool IsFFError(int av_res)
281
static unsigned char GetChar(uint tag, int bit_begin)
283
return (tag>>bit_begin) & 0xFF;
286
static bool SetIndex(int& idx, int i, bool b)
288
bool res = (idx == -1) && b;
294
bool OpenInfo(FFData& ffi, const char* fname, std::string& err_str)
298
ASSERT( !ffi.IsOpened() );
301
// AVInputFormat* для форсирования формата контейнера
302
// создается из av_find_input_format(str), где str из опции -f для ffmpeg
304
AVInputFormat* file_iformat = 0;
305
// для уточнения параметров входного потока; используется в случаях, когда
306
// по самому потоку невозможно определить их (не для контейнеров, а для
307
// элементарных потоков
308
AVFormatParameters* ap = 0;
309
// всегда нуль (ffmpeg, ffplay)
312
AVFormatContext* ic = 0;
313
int av_res = av_open_input_file(&ic, fname, file_iformat, buf_size, ap);
314
if( IsFFError(av_res) ) // ошибка
319
// :TODO: решить, ставить в конце точки или нет (сообщения пользователю
320
// показывается не HIG-ого)
321
err_str = _("No such file");
324
err_str = _("Unknown file format");
326
case AVERROR_UNKNOWN:
328
err_str = boost::format("FFmpeg unknown error: %1%") % av_res % bf::stop;
339
av_res = av_find_stream_info(ic);
340
if( IsFFError(av_res) )
342
// например .webm для FFmpeg <= 0.5
343
err_str = BF_("Can't find stream information: %1%") % av_res % bf::stop;
346
if( LogFilter->IsEnabled(::Log::Info) )
349
int video_idx = -1, audio_idx = -1;
350
for( int i=0; i < (int)ic->nb_streams; i++ )
352
AVStream* strm = ic->streams[i];
353
AVCodecContext* avctx = strm->codec;
354
if( SetIndex(video_idx, i, avctx->codec_type == CODEC_TYPE_VIDEO) )
357
// для демиксера имеет значение только NONE и ALL
358
strm->discard = AVDISCARD_ALL;
360
SetIndex(audio_idx, i, avctx->codec_type == CODEC_TYPE_AUDIO);
363
if( video_idx == -1 )
365
err_str = _("No video stream found");
368
// включить по требованию (и поправить flower.mpg)
369
//if( audio_idx == -1 )
371
// err_str = _("No audio stream found");
375
if( !IsFTSValid(ic->duration) )
377
err_str = _("Can't find the file duration");
381
if( !IsFTSValid(ic->start_time) )
383
// в 99% отсутствие нач. времени - элементарный поток = без контейнера;
384
// см. особенности ffmpeg, update_initial_timestamps()
385
err_str = _("Start time of the file is unknown");
389
if( !SeekCall(ic, ic->start_time, false) )
391
// проверка индекса/возможности перемещения
392
err_str = _("Can't seek through the file");
397
AVCodecContext* dec = ic->streams[video_idx]->codec;
398
// для H.264 и плохих TS
399
dec->strict_std_compliance = FF_COMPLIANCE_STRICT;
401
// Chromium зачем-то выставляет явно, но такие значения уже по умолчанию
402
//dec->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
403
//dec->error_recognition = FF_ER_CAREFUL;
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;
413
// AVCodec - это одиночка, а AVCodecContext - состояние для него
414
// в соответ. потоке контейнера
415
AVCodec* codec = avcodec_find_decoder(dec->codec_id);
418
err_str = BF_("No decoder found for the stream: %1%") % tag_str % bf::stop;
422
if( IsFFError(avcodec_open(dec, codec)) )
424
err_str = boost::format("Can't open codec: %1%") % tag_str % bf::stop;
429
// * декодер настроен
431
ffi.videoIdx = video_idx;
433
Point sz(VideoSize(dec));
436
err_str = "Video has null size";
448
FFInfo::FFInfo(const std::string& fname)
451
bool res = OpenInfo(*this, fname.c_str(), err_str);
452
ASSERT_OR_UNUSED( res );
460
bool CanOpenAsFFmpegVideo(const char* fname, std::string& err_str)
463
return OpenInfo(ffi, fname, err_str);
466
bool FFViewer::Open(const char* fname, std::string& err_str)
468
// * закрываем открытое ранее
471
bool res = OpenInfo(*this, fname, err_str);
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;
487
// :TRICKY: почему-то ffmpeg'у "нравится" BGR24 и не нравиться RGB24 в плане использования
488
// MMX (ускорения); цена по времени неизвестна,- используем только ради того, чтобы не было
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);
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);
504
// защита от неполных открытий
509
bool FFViewer::Open(const char* fname)
512
return Open(fname, err);
515
static double TS2Time(int64_t ts, FFViewer& ffv)
519
tm = ts * av_q2d(VideoStream(ffv)->time_base);
523
static bool IsInHurry(AVCodecContext* dec)
525
return dec->hurry_up != 0;
528
struct HurryModeEnabler
532
HurryModeEnabler(AVCodecContext* dec_): dec(dec_)
534
// как признак (хоть и устаревший)
536
// Прирост скорости (h264):
537
// - AVDISCARD_NONREF: 2x
538
// - AVDISCARD_BIDIR: для h264 (и других современных кодеков?) разница в скорости
539
// с AVDISCARD_NONREF небольшая, зато корректно все необходимые кадры распакуем
540
// - AVDISCARD_NONKEY: неотличим от AVDISCARD_ALL, так как I-кадры очень
542
// - AVDISCARD_ALL: 3,6-4 (но тогда декодер вообще перестанет выдавать
543
// кадры, например для mpeg4 - без переделки нельзя использовать)
546
dec->skip_frame = AVDISCARD_NONREF;
548
// незначительный прирост дают
549
//dec->skip_loop_filter = AVDISCARD_ALL;
550
//dec->skip_idct = AVDISCARD_ALL;
556
dec->skip_frame = AVDISCARD_DEFAULT;
557
//dec->skip_idct = AVDISCARD_DEFAULT;
558
//dec->skip_loop_filter = AVDISCARD_DEFAULT;
563
// для включения логов (де)кодера на время,
564
// чтобы другое консоль не забивало
565
struct CodecDebugEnabler
569
CodecDebugEnabler(AVCodecContext* dec, int flags)
571
oldLvl = av_log_get_level();
572
av_log_set_level(AV_LOG_DEBUG);
577
av_log_set_level(oldLvl);
581
static void DoVideoDecode(FFViewer& ffv, int& got_picture, AVPacket* pkt)
583
// FF_DEBUG_PICT_INFO - вывод типов декодируемых картинок
584
// FF_DEBUG_MMCO - (h.264) управление зависимыми кадрами + порядок кадров (poc)
585
//CodecDebugEnabler cde(GetVideoCtx(ffv), FF_DEBUG_PICT_INFO);
587
AVFrame& picture = ffv.srcFrame;
588
avcodec_get_frame_defaults(&picture); // ffmpeg.c очищает каждый раз
589
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,00)
592
// никогда бы не использовал alloca(), но не хочется создавать
593
// на стеке лишние байты для исключительных случаев
594
pkt = (AVPacket*)alloca(sizeof(AVPacket));
599
int av_res = avcodec_decode_video2(GetVideoCtx(ffv), &picture, &got_picture, pkt);
601
const uint8_t* buf = 0;
608
int av_res = avcodec_decode_video(GetVideoCtx(ffv), &picture, &got_picture, buf, buf_sz);
611
// ничего не требуется делать в случае ошибок
612
LOG_WRN << "Error while decoding frame!" << io::endl;
615
static void ResetCurPTS(FFViewer& ffv)
618
ffv.prevPTS = INV_TS;
621
static void UpdatePTS(FFViewer& ffv, double new_pts)
623
ffv.prevPTS = ffv.curPTS;
624
ffv.curPTS = new_pts;
627
static bool DoDecode(FFViewer& ffv)
629
double cur_pts = ffv.curPTS;
630
AVCodecContext* dec = GetVideoCtx(ffv);
635
// предположительное время начала следующего кадра (если не будет явно
636
// установлено) - расчет до следующего av_read_frame()
637
double next_pts = cur_pts;
638
if( IsTSValid(cur_pts) )
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);
648
int av_res = av_read_frame(ffv.iCtx, &pkt);
651
// хотя только одно видео фильтруем, по ходу работы
652
// может найтись новый поток -
653
// samples.mplayerhq.hu/MPEG2/dothack2.mpg (субтитры на 8й секунде)
654
if( pkt.stream_index == ffv.videoIdx )
656
dec->reordered_opaque = pkt.pts;
658
DoVideoDecode(ffv, got_picture, &pkt);
660
av_free_packet(&pkt);
662
else if( av_res == AVERROR_EOF ) // для mpegts также -EIO приходит
664
// остатки в декодере забираем
665
DoVideoDecode(ffv, got_picture, 0);
668
// больше ничего нет, даже того что было
672
// остальные проблемы демиксера
679
// * PTS текущего кадра
680
cur_pts = TS2Time(ffv.srcFrame.reordered_opaque, ffv);
681
if( !IsTSValid(cur_pts) )
684
// pts: граничные случаи
685
double cur_dts = TS2Time(pkt.dts, ffv);
686
if( IsTSValid(cur_dts) )
688
if( !IsTSValid(cur_pts) )
689
// первые кадры для avi-контейнеров (не B-кадры) имеют только dts
691
else if( IsInHurry(dec) )
692
// помимо не декодирования декодер еще и пропускает B-кадры,
693
// а значит можно пропустить pts
694
cur_pts = std::max(cur_pts, cur_dts);
697
UpdatePTS(ffv, cur_pts);
701
// очередного кадра нет => следующий SetTime()
702
// не должен все обрушить
708
static double Delta(double time, double ts, FFViewer& ffv)
710
ASSERT( IsTSValid(ts) );
711
return (time - ts) * FrameFPS(ffv);
714
static double Delta(double time, FFViewer& ffv)
716
return Delta(time, ffv.curPTS, ffv);
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;
725
typedef boost::function<bool(FFViewer&)> FFVFunctor;
727
static bool DecodeLoop(FFViewer& ffv, const FFVFunctor& condition_fnr)
730
while( !condition_fnr(ffv) )
739
static bool IsFrameFound(double delta)
741
return delta <= NULL_DELTA;
744
static bool IsFrameFound(double time, double diff, FFViewer& ffv)
746
return IsFrameFound(Delta(time, ffv) - diff);
749
// доводка до разницы <= diff (в кадрах)
750
static bool DecodeForDiff(double time, double diff, FFViewer& ffv)
752
return DecodeLoop(ffv, bb::bind(&IsFrameFound, time, diff, _1));
755
//static bool IsFrameLate(double delta)
757
// return delta < NEG_WIN_DELTA - NULL_DELTA;
760
static bool IsFrameLate(double time, FFViewer& ffv)
763
if( IsTSValid(ffv.prevPTS) )
765
double delta = Delta(time, ffv.prevPTS, ffv);
766
res = IsFrameFound(delta);
769
res = Delta(time, ffv) < NEG_WIN_DELTA - NULL_DELTA;
773
static bool SeekSetTime(FFViewer& ffv, double time);
775
static bool DecodeTill(FFViewer& ffv, double time, bool can_seek)
777
ASSERT( IsCurPTS(ffv) );
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);
789
LOG_WRN << "Seek delta overflow: " << orig_delta << io::endl;
791
// уменьшаем до приемлемого, явно
792
time = ffv.curPTS + MAX_WIN_DELTA/FrameFPS(ffv);
795
// * допустимая разница, доводим
796
LOG_INF << "Decoding delta: " << Delta(time, ffv) << io::endl;
799
HurryModeEnabler hme(GetVideoCtx(ffv));
800
// кадр может длится <= 3 тактов по декодеру, но явный PTS
802
res = DecodeForDiff(time, 10, ffv);
805
// оставшееся в полном режиме
806
res = res && DecodeForDiff(time, 0, ffv);
812
static bool DoSeek(FFViewer& ffv, int64_t ts, bool is_byte_seek)
815
// если перемещение не прошло (индекс частично поломан), то полагаем, что
816
// состояние прежнее (обнуление не требуется)
817
bool res = SeekCall(ffv.iCtx, ts, is_byte_seek);
823
// сбрасываем буфера декодеров (отдельно от seek)
824
avcodec_flush_buffers(GetVideoCtx(ffv));
826
// * до первого кадра с PTS
827
res = DecodeLoop(ffv, &IsCurPTS);
832
bool TimeSeek(FFViewer& ffv, double seek_time, double time)
834
bool res = DoSeek(ffv, AV_TIME_BASE * seek_time, false);
836
return res && !IsFrameLate(time, ffv);
839
static double StartTime(FFViewer& ffv)
841
return StartTime(ffv.iCtx);
844
static bool CanByteSeek(AVFormatContext* ic)
846
// переход по позиции не работает для avi, mkv - см. особенности ffmpeg
847
// однако для без-заголовочных демиксеров (MPEG-PS, MPEG-TS) требуется
849
typedef std::map<std::string, AVInputFormat*> Map;
853
// для видео < 1 секунды показывает пусто
854
map["mpeg"] = av_find_input_format("mpeg");
855
// перейти в начало иногда возможно только так,- PanamaCanal_1080p-h264.ts
856
map["mpegts"] = av_find_input_format("mpegts");
860
boost_foreach( Map::reference ref, map )
861
if( ic->iformat == ref.second )
869
static bool SeekSetTime(FFViewer& ffv, double time)
871
bool is_begin = false;
872
double start_time = StartTime(ffv);
873
for( int i=0; i<4 && !is_begin; i++ )
875
int n = (1 << i) - 1; // 0, 1, 3, 7
876
double seek_time = time - n;
878
if( seek_time <= start_time )
884
if( TimeSeek(ffv, seek_time, time) )
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);
895
// некоторое видео глючит в начале (Hellboy), из-за чего
896
// последовательный доступ выполняется с перескоками -
897
// явно ставим пред. кадр
899
// :KLUDGE: -1 уже занят, поэтому -0.5
900
// (система работает, пока start_time не бывает отрицательным)
901
ffv.prevPTS = start_time - 0.5;
904
return IsCurPTS(ffv) && DecodeTill(ffv, time, false);
907
// время без смещения
908
bool SetTime(FFViewer& ffv, double time)
910
ASSERT( ffv.IsOpened() );
912
if( (time < 0) || (time > Duration(ffv.iCtx)) )
914
time += StartTime(ffv);
916
// :TODO: рассчитывать только если включено логирование
917
double GetClockTime();
918
double cur_time = GetClockTime();
922
res = SeekSetTime(ffv, time);
924
res = DecodeTill(ffv, time, true);
928
// декодер должен выдать результат
929
ASSERT( ffv.srcFrame.data[0] );
933
// не допускаем смены разрешения в меньшую сторону, чтобы
934
// не вылететь при скалировании; вообще задумано, что разрешение
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];
948
for( int y=0; y<sz.y; y++ )
949
for( int x=0; x<sz.x; x++, ptr += 3 )
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;
964
RefPtr<Gdk::Pixbuf> GetRawFrame(double time, FFViewer& ffv)
966
RefPtr<Gdk::Pixbuf> res_pix;
967
if( ffv.IsOpened() && SetTime(ffv, time) )
968
res_pix = CreateFromData(ffv.rgbFrame.data[0], ffv.vidSz, false);