34#define MAX_SUPPORTED_WIDTH 1950
35#define MAX_SUPPORTED_HEIGHT 1100
38#include "libavutil/hwcontext_vaapi.h"
40typedef struct VAAPIDecodeContext {
42 VAEntrypoint va_entrypoint;
44 VAContextID va_context;
46#if FF_API_STRUCT_VAAPI_CONTEXT
49 struct vaapi_context *old_context;
50 AVBufferRef *device_ref;
54 AVHWDeviceContext *device;
55 AVVAAPIDeviceContext *hwctx;
57 AVHWFramesContext *frames;
58 AVVAAPIFramesContext *hwfc;
60 enum AVPixelFormat surface_format;
79 : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), NO_PTS_OFFSET(-99999),
80 path(
path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), is_open(false),
81 seek_audio_frame_found(0), seek_video_frame_found(0),is_duration_known(false), largest_frame_processed(0),
82 current_video_frame(0), packet(NULL), duration_strategy(duration_strategy),
83 audio_pts(0), video_pts(0), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
84 pStream(NULL), aStream(NULL), pFrame(NULL), previous_packet_location{-1,0},
92 pts_offset_seconds = NO_PTS_OFFSET;
93 video_pts_seconds = NO_PTS_OFFSET;
94 audio_pts_seconds = NO_PTS_OFFSET;
101 if (inspect_reader) {
123 if (abs(diff) <= amount)
134static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
136 const enum AVPixelFormat *p;
138 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
140#if defined(__linux__)
142 case AV_PIX_FMT_VAAPI:
147 case AV_PIX_FMT_VDPAU:
155 case AV_PIX_FMT_DXVA2_VLD:
160 case AV_PIX_FMT_D3D11:
166#if defined(__APPLE__)
168 case AV_PIX_FMT_VIDEOTOOLBOX:
175 case AV_PIX_FMT_CUDA:
191 return AV_PIX_FMT_NONE;
194int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
198 case AV_CODEC_ID_H264:
199 case AV_CODEC_ID_MPEG2VIDEO:
200 case AV_CODEC_ID_VC1:
201 case AV_CODEC_ID_WMV1:
202 case AV_CODEC_ID_WMV2:
203 case AV_CODEC_ID_WMV3:
218 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
228 if (avformat_open_input(&pFormatCtx,
path.c_str(), NULL, NULL) != 0)
232 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
239 packet_status.
reset(
true);
242 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
244 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
251 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
258 if (videoStream == -1 && audioStream == -1)
262 if (videoStream != -1) {
267 pStream = pFormatCtx->streams[videoStream];
273 const AVCodec *pCodec = avcodec_find_decoder(codecId);
274 AVDictionary *
opts = NULL;
275 int retry_decode_open = 2;
280 if (
hw_de_on && (retry_decode_open==2)) {
282 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
285 retry_decode_open = 0;
290 if (pCodec == NULL) {
291 throw InvalidCodec(
"A valid video codec could not be found for this file.",
path);
295 av_dict_set(&
opts,
"strict",
"experimental", 0);
299 int i_decoder_hw = 0;
301 char *adapter_ptr = NULL;
304 fprintf(stderr,
"Hardware decoding device number: %d\n", adapter_num);
307 pCodecCtx->get_format = get_hw_dec_format;
309 if (adapter_num < 3 && adapter_num >=0) {
310#if defined(__linux__)
311 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
312 adapter_ptr = adapter;
314 switch (i_decoder_hw) {
316 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
319 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
322 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
325 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
328 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
335 switch (i_decoder_hw) {
337 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
340 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
343 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
346 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
349 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
352#elif defined(__APPLE__)
355 switch (i_decoder_hw) {
357 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
360 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
363 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
373#if defined(__linux__)
374 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
376 if( adapter_ptr != NULL ) {
377#elif defined(__APPLE__)
378 if( adapter_ptr != NULL ) {
387 hw_device_ctx = NULL;
389 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
390 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
432 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
436 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
437 if (avcodec_return < 0) {
438 std::stringstream avcodec_error_msg;
439 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
445 AVHWFramesConstraints *constraints = NULL;
446 void *hwconfig = NULL;
447 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
451 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
452 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
455 if (pCodecCtx->coded_width < constraints->min_width ||
456 pCodecCtx->coded_height < constraints->min_height ||
457 pCodecCtx->coded_width > constraints->max_width ||
458 pCodecCtx->coded_height > constraints->max_height) {
461 retry_decode_open = 1;
464 av_buffer_unref(&hw_device_ctx);
465 hw_device_ctx = NULL;
470 ZmqLogger::Instance()->
AppendDebugMethod(
"\nDecode hardware acceleration is used\n",
"Min width :", constraints->min_width,
"Min Height :", constraints->min_height,
"MaxWidth :", constraints->max_width,
"MaxHeight :", constraints->max_height,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
471 retry_decode_open = 0;
473 av_hwframe_constraints_free(&constraints);
486 if (pCodecCtx->coded_width < 0 ||
487 pCodecCtx->coded_height < 0 ||
488 pCodecCtx->coded_width > max_w ||
489 pCodecCtx->coded_height > max_h ) {
490 ZmqLogger::Instance()->
AppendDebugMethod(
"DIMENSIONS ARE TOO LARGE for hardware acceleration\n",
"Max Width :", max_w,
"Max Height :", max_h,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
492 retry_decode_open = 1;
495 av_buffer_unref(&hw_device_ctx);
496 hw_device_ctx = NULL;
500 ZmqLogger::Instance()->
AppendDebugMethod(
"\nDecode hardware acceleration is used\n",
"Max Width :", max_w,
"Max Height :", max_h,
"Frame width :", pCodecCtx->coded_width,
"Frame height :", pCodecCtx->coded_height);
501 retry_decode_open = 0;
509 retry_decode_open = 0;
511 }
while (retry_decode_open);
520 if (audioStream != -1) {
525 aStream = pFormatCtx->streams[audioStream];
531 const AVCodec *aCodec = avcodec_find_decoder(codecId);
537 if (aCodec == NULL) {
538 throw InvalidCodec(
"A valid audio codec could not be found for this file.",
path);
542 AVDictionary *
opts = NULL;
543 av_dict_set(&
opts,
"strict",
"experimental", 0);
546 if (avcodec_open2(aCodecCtx, aCodec, &
opts) < 0)
547 throw InvalidCodec(
"An audio codec was found, but could not be opened.",
path);
557 AVDictionaryEntry *tag = NULL;
558 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
559 QString str_key = tag->key;
560 QString str_value = tag->value;
561 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
565 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
566 AVStream* st = pFormatCtx->streams[i];
567 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
569#if (LIBAVFORMAT_VERSION_MAJOR < 62)
570 for (
int j = 0; j < st->nb_side_data; j++) {
571 AVPacketSideData *sd = &st->side_data[j];
573 for (
int j = 0; j < st->codecpar->nb_coded_side_data; j++) {
574 AVPacketSideData *sd = &st->codecpar->coded_side_data[j];
577 if (sd->type == AV_PKT_DATA_DISPLAYMATRIX &&
578 sd->size >= 9 *
sizeof(int32_t) &&
581 double rotation = -av_display_rotation_get(
582 reinterpret_cast<int32_t *
>(sd->data));
583 if (std::isnan(rotation)) rotation = 0;
587 else if (sd->type == AV_PKT_DATA_SPHERICAL) {
592 const AVSphericalMapping* map =
593 reinterpret_cast<const AVSphericalMapping*
>(sd->data);
596 const char* proj_name = av_spherical_projection_name(map->projection);
602 auto to_deg = [](int32_t v){
603 return (
double)v / 65536.0;
605 info.
metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
606 info.
metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
607 info.
metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
615 previous_packet_location.
frame = -1;
647 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
653 AVPacket *recent_packet = packet;
658 int max_attempts = 128;
663 "attempts", attempts);
675 RemoveAVPacket(recent_packet);
680 if(avcodec_is_open(pCodecCtx)) {
681 avcodec_flush_buffers(pCodecCtx);
687 av_buffer_unref(&hw_device_ctx);
688 hw_device_ctx = NULL;
692 if (img_convert_ctx) {
693 sws_freeContext(img_convert_ctx);
694 img_convert_ctx =
nullptr;
696 if (pFrameRGB_cached) {
703 if(avcodec_is_open(aCodecCtx)) {
704 avcodec_flush_buffers(aCodecCtx);
716 working_cache.
Clear();
719 avformat_close_input(&pFormatCtx);
720 av_freep(&pFormatCtx);
728 largest_frame_processed = 0;
729 seek_audio_frame_found = 0;
730 seek_video_frame_found = 0;
731 current_video_frame = 0;
732 last_video_frame.reset();
736bool FFmpegReader::HasAlbumArt() {
740 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
741 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
744double FFmpegReader::PickDurationSeconds()
const {
745 auto has_value = [](
double value) {
return value > 0.0; };
747 switch (duration_strategy) {
749 if (has_value(video_stream_duration_seconds))
750 return video_stream_duration_seconds;
751 if (has_value(audio_stream_duration_seconds))
752 return audio_stream_duration_seconds;
753 if (has_value(format_duration_seconds))
754 return format_duration_seconds;
757 if (has_value(audio_stream_duration_seconds))
758 return audio_stream_duration_seconds;
759 if (has_value(video_stream_duration_seconds))
760 return video_stream_duration_seconds;
761 if (has_value(format_duration_seconds))
762 return format_duration_seconds;
767 double longest = 0.0;
768 if (has_value(video_stream_duration_seconds))
769 longest = std::max(longest, video_stream_duration_seconds);
770 if (has_value(audio_stream_duration_seconds))
771 longest = std::max(longest, audio_stream_duration_seconds);
772 if (has_value(format_duration_seconds))
773 longest = std::max(longest, format_duration_seconds);
774 if (has_value(longest))
780 if (has_value(format_duration_seconds))
781 return format_duration_seconds;
782 if (has_value(inferred_duration_seconds))
783 return inferred_duration_seconds;
788void FFmpegReader::ApplyDurationStrategy() {
790 const double chosen_seconds = PickDurationSeconds();
792 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
795 is_duration_known =
false;
799 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
803 is_duration_known =
false;
808 info.
duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
809 is_duration_known =
true;
812void FFmpegReader::UpdateAudioInfo() {
827 auto record_duration = [](
double &target,
double seconds) {
829 target = std::max(target, seconds);
834 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
865 if (aStream->duration > 0) {
868 if (pFormatCtx->duration > 0) {
870 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
900 ApplyDurationStrategy();
903 AVDictionaryEntry *tag = NULL;
904 while ((tag = av_dict_get(aStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
905 QString str_key = tag->key;
906 QString str_value = tag->value;
907 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
911void FFmpegReader::UpdateVideoInfo() {
917 auto record_duration = [](
double &target,
double seconds) {
919 target = std::max(target, seconds);
924 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
931 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
943 if (pStream->sample_aspect_ratio.num != 0) {
966 if (!check_interlace) {
967 check_interlace =
true;
969 switch(field_order) {
970 case AV_FIELD_PROGRESSIVE:
983 case AV_FIELD_UNKNOWN:
985 check_interlace =
false;
1000 if (pFormatCtx->duration >= 0) {
1002 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1012 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1013 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1015 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1019 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1020 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1021 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1025 ApplyDurationStrategy();
1028 AVDictionaryEntry *tag = NULL;
1029 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1030 QString str_key = tag->key;
1031 QString str_value = tag->value;
1032 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1037 return this->is_duration_known;
1043 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.",
path);
1046 if (requested_frame < 1)
1047 requested_frame = 1;
1052 throw InvalidFile(
"Could not detect the duration of the video or audio stream.",
path);
1068 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1082 int64_t diff = requested_frame - last_frame;
1083 if (diff >= 1 && diff <= 20) {
1085 frame = ReadStream(requested_frame);
1090 Seek(requested_frame);
1099 frame = ReadStream(requested_frame);
1107std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1109 bool check_seek =
false;
1110 int packet_error = -1;
1120 CheckWorkingFrames(requested_frame);
1125 if (is_cache_found) {
1129 if (!hold_packet || !packet) {
1131 packet_error = GetNextPacket();
1132 if (packet_error < 0 && !packet) {
1143 check_seek = CheckSeek(
false);
1155 if ((
info.
has_video && packet && packet->stream_index == videoStream) ||
1159 ProcessVideoPacket(requested_frame);
1162 if ((
info.
has_audio && packet && packet->stream_index == audioStream) ||
1166 ProcessAudioPacket(requested_frame);
1171 if ((!
info.
has_video && packet && packet->stream_index == videoStream) ||
1172 (!
info.
has_audio && packet && packet->stream_index == audioStream)) {
1174 if (packet->stream_index == videoStream) {
1176 }
else if (packet->stream_index == audioStream) {
1182 RemoveAVPacket(packet);
1192 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ReadStream (force EOF)",
"packets_read", packet_status.
packets_read(),
"packets_decoded", packet_status.
packets_decoded(),
"packets_eof", packet_status.
packets_eof,
"video_eof", packet_status.
video_eof,
"audio_eof", packet_status.
audio_eof,
"end_of_file", packet_status.
end_of_file);
1209 "largest_frame_processed", largest_frame_processed,
1210 "Working Cache Count", working_cache.
Count());
1219 CheckWorkingFrames(requested_frame);
1235 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1238 if (!frame->has_image_data) {
1243 frame->AddAudioSilence(samples_in_frame);
1248 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1250 f->AddAudioSilence(samples_in_frame);
1258int FFmpegReader::GetNextPacket() {
1259 int found_packet = 0;
1260 AVPacket *next_packet;
1261 next_packet =
new AVPacket();
1262 found_packet = av_read_frame(pFormatCtx, next_packet);
1266 RemoveAVPacket(packet);
1269 if (found_packet >= 0) {
1271 packet = next_packet;
1274 if (packet->stream_index == videoStream) {
1276 }
else if (packet->stream_index == audioStream) {
1285 return found_packet;
1289bool FFmpegReader::GetAVFrame() {
1290 int frameFinished = 0;
1296 int send_packet_err = 0;
1297 int64_t send_packet_pts = 0;
1298 if ((packet && packet->stream_index == videoStream) || !packet) {
1299 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1301 if (packet && send_packet_err >= 0) {
1302 send_packet_pts = GetPacketPTS();
1303 hold_packet =
false;
1313 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1314 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: Not sent [" + av_err2string(send_packet_err) +
"])",
"send_packet_err", send_packet_err,
"send_packet_pts", send_packet_pts);
1315 if (send_packet_err == AVERROR(EAGAIN)) {
1317 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1319 if (send_packet_err == AVERROR(EINVAL)) {
1320 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush",
"send_packet_pts", send_packet_pts);
1322 if (send_packet_err == AVERROR(ENOMEM)) {
1323 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(ENOMEM): failed to add packet to internal queue, or legitimate decoding errors",
"send_packet_pts", send_packet_pts);
1330 int receive_frame_err = 0;
1331 AVFrame *next_frame2;
1339 next_frame2 = next_frame;
1342 while (receive_frame_err >= 0) {
1343 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1345 if (receive_frame_err != 0) {
1346 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (receive frame: frame not ready yet from decoder [\" + av_err2string(receive_frame_err) + \"])",
"receive_frame_err", receive_frame_err,
"send_packet_pts", send_packet_pts);
1348 if (receive_frame_err == AVERROR_EOF) {
1350 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1351 avcodec_flush_buffers(pCodecCtx);
1354 if (receive_frame_err == AVERROR(EINVAL)) {
1356 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1357 avcodec_flush_buffers(pCodecCtx);
1359 if (receive_frame_err == AVERROR(EAGAIN)) {
1361 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EAGAIN): output is not available in this state - user must try to send new input)",
"send_packet_pts", send_packet_pts);
1363 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1365 "FFmpegReader::GetAVFrame (receive frame: AVERROR_INPUT_CHANGED: current decoded frame has changed parameters with respect to first decoded frame)",
"send_packet_pts", send_packet_pts);
1376 if (next_frame2->format == hw_de_av_pix_fmt) {
1377 next_frame->format = AV_PIX_FMT_YUV420P;
1378 if ((err = av_hwframe_transfer_data(next_frame,next_frame2,0)) < 0) {
1381 if ((err = av_frame_copy_props(next_frame,next_frame2)) < 0) {
1389 next_frame = next_frame2;
1401 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)next_frame->data, next_frame->linesize,
1408 if (next_frame->pts != AV_NOPTS_VALUE) {
1411 video_pts = next_frame->pts;
1412 }
else if (next_frame->pkt_dts != AV_NOPTS_VALUE) {
1414 video_pts = next_frame->pkt_dts;
1418 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1429 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1435 if (frameFinished) {
1439 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.
width,
1448 return frameFinished;
1452bool FFmpegReader::CheckSeek(
bool is_video) {
1457 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1465 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1468 if (max_seeked_frame >= seeking_frame) {
1471 "is_video_seek", is_video_seek,
1472 "max_seeked_frame", max_seeked_frame,
1473 "seeking_frame", seeking_frame,
1474 "seeking_pts", seeking_pts,
1475 "seek_video_frame_found", seek_video_frame_found,
1476 "seek_audio_frame_found", seek_audio_frame_found);
1479 Seek(seeking_frame - (10 * seek_count * seek_count));
1483 "is_video_seek", is_video_seek,
1484 "packet->pts", GetPacketPTS(),
1485 "seeking_pts", seeking_pts,
1486 "seeking_frame", seeking_frame,
1487 "seek_video_frame_found", seek_video_frame_found,
1488 "seek_audio_frame_found", seek_audio_frame_found);
1502void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1505 int frame_finished = GetAVFrame();
1508 if (!frame_finished) {
1511 RemoveAVFrame(pFrame);
1517 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1520 if (!seek_video_frame_found && is_seeking)
1521 seek_video_frame_found = current_frame;
1527 working_cache.
Add(CreateFrame(requested_frame));
1539 AVFrame *pFrameRGB = pFrameRGB_cached;
1542 if (pFrameRGB ==
nullptr)
1544 pFrameRGB_cached = pFrameRGB;
1547 uint8_t *buffer =
nullptr;
1568 max_width = std::max(
float(max_width), max_width * max_scale_x);
1569 max_height = std::max(
float(max_height), max_height * max_scale_y);
1575 QSize width_size(max_width * max_scale_x,
1578 max_height * max_scale_y);
1580 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1581 max_width = std::max(max_width, width_size.width());
1582 max_height = std::max(max_height, width_size.height());
1584 max_width = std::max(max_width, height_size.width());
1585 max_height = std::max(max_height, height_size.height());
1592 float preview_ratio = 1.0;
1599 max_width =
info.
width * max_scale_x * preview_ratio;
1600 max_height =
info.
height * max_scale_y * preview_ratio;
1608 int original_height = height;
1609 if (max_width != 0 && max_height != 0 && max_width < width && max_height < height) {
1611 float ratio = float(width) / float(height);
1612 int possible_width = round(max_height * ratio);
1613 int possible_height = round(max_width / ratio);
1615 if (possible_width <= max_width) {
1617 width = possible_width;
1618 height = max_height;
1622 height = possible_height;
1627 const int bytes_per_pixel = 4;
1628 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
1631 constexpr size_t ALIGNMENT = 32;
1632 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
1633 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
1638 int scale_mode = SWS_FAST_BILINEAR;
1640 scale_mode = SWS_BICUBIC;
1642 img_convert_ctx = sws_getCachedContext(img_convert_ctx,
info.
width,
info.
height,
AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), width, height,
PIX_FMT_RGBA, scale_mode, NULL, NULL, NULL);
1643 if (!img_convert_ctx)
1647 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
1648 original_height, pFrameRGB->data, pFrameRGB->linesize);
1651 std::shared_ptr<Frame> f = CreateFrame(current_frame);
1656 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
1659 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
1663 working_cache.
Add(f);
1666 last_video_frame = f;
1672 RemoveAVFrame(pFrame);
1678 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
1682void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
1685 if (packet && packet->pts != AV_NOPTS_VALUE) {
1687 location = GetAudioPTSLocation(packet->pts);
1690 if (!seek_audio_frame_found && is_seeking)
1691 seek_audio_frame_found = location.
frame;
1698 working_cache.
Add(CreateFrame(requested_frame));
1702 "requested_frame", requested_frame,
1703 "target_frame", location.
frame,
1707 int frame_finished = 0;
1711 int packet_samples = 0;
1715 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
1716 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1720 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
1721 if (receive_frame_err >= 0) {
1724 if (receive_frame_err == AVERROR_EOF) {
1728 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
1730 avcodec_flush_buffers(aCodecCtx);
1732 if (receive_frame_err != 0) {
1737 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
1740 if (frame_finished) {
1746 audio_pts = audio_frame->pts;
1749 location = GetAudioPTSLocation(audio_pts);
1752 int plane_size = -1;
1758 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
1762 packet_samples = audio_frame->nb_samples * nb_channels;
1771 int pts_remaining_samples = packet_samples /
info.
channels;
1774 if (pts_remaining_samples == 0) {
1776 "packet_samples", packet_samples,
1778 "pts_remaining_samples", pts_remaining_samples);
1782 while (pts_remaining_samples) {
1787 int samples = samples_per_frame - previous_packet_location.
sample_start;
1788 if (samples > pts_remaining_samples)
1789 samples = pts_remaining_samples;
1792 pts_remaining_samples -= samples;
1794 if (pts_remaining_samples > 0) {
1796 previous_packet_location.
frame++;
1805 "packet_samples", packet_samples,
1813 audio_converted->nb_samples = audio_frame->nb_samples;
1814 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
1830 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
1839 audio_converted->data,
1840 audio_converted->linesize[0],
1841 audio_converted->nb_samples,
1843 audio_frame->linesize[0],
1844 audio_frame->nb_samples);
1847 int64_t starting_frame_number = -1;
1848 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++) {
1850 starting_frame_number = location.
frame;
1851 int channel_buffer_size = nb_samples;
1852 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
1856 int remaining_samples = channel_buffer_size;
1857 while (remaining_samples > 0) {
1862 int samples = std::fmin(samples_per_frame - start, remaining_samples);
1865 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
1868 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
1872 "frame", starting_frame_number,
1875 "channel", channel_filter,
1876 "samples_per_frame", samples_per_frame);
1879 working_cache.
Add(f);
1882 remaining_samples -= samples;
1885 if (remaining_samples > 0)
1886 channel_buffer += samples;
1889 starting_frame_number++;
1897 av_free(audio_converted->data[0]);
1906 "requested_frame", requested_frame,
1907 "starting_frame", location.
frame,
1908 "end_frame", starting_frame_number - 1,
1909 "audio_pts_seconds", audio_pts_seconds);
1915void FFmpegReader::Seek(int64_t requested_frame) {
1917 if (requested_frame < 1)
1918 requested_frame = 1;
1921 if (requested_frame > largest_frame_processed && packet_status.
end_of_file) {
1928 "requested_frame", requested_frame,
1929 "seek_count", seek_count,
1930 "last_frame", last_frame);
1933 working_cache.
Clear();
1937 video_pts_seconds = NO_PTS_OFFSET;
1939 audio_pts_seconds = NO_PTS_OFFSET;
1940 hold_packet =
false;
1942 current_video_frame = 0;
1943 largest_frame_processed = 0;
1948 packet_status.
reset(
false);
1954 int buffer_amount = 12;
1955 if (requested_frame - buffer_amount < 20) {
1969 if (seek_count == 1) {
1972 seeking_pts = ConvertFrameToVideoPTS(1);
1974 seek_audio_frame_found = 0;
1975 seek_video_frame_found = 0;
1979 bool seek_worked =
false;
1980 int64_t seek_target = 0;
1984 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
1986 fprintf(stderr,
"%s: error while seeking video stream\n", pFormatCtx->AV_FILENAME);
1989 is_video_seek =
true;
1996 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
1998 fprintf(stderr,
"%s: error while seeking audio stream\n", pFormatCtx->AV_FILENAME);
2001 is_video_seek =
false;
2010 avcodec_flush_buffers(aCodecCtx);
2014 avcodec_flush_buffers(pCodecCtx);
2017 previous_packet_location.
frame = -1;
2022 if (seek_count == 1) {
2024 seeking_pts = seek_target;
2025 seeking_frame = requested_frame;
2027 seek_audio_frame_found = 0;
2028 seek_video_frame_found = 0;
2056int64_t FFmpegReader::GetPacketPTS() {
2058 int64_t current_pts = packet->pts;
2059 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2060 current_pts = packet->dts;
2066 return AV_NOPTS_VALUE;
2071void FFmpegReader::UpdatePTSOffset() {
2072 if (pts_offset_seconds != NO_PTS_OFFSET) {
2076 pts_offset_seconds = 0.0;
2077 double video_pts_offset_seconds = 0.0;
2078 double audio_pts_offset_seconds = 0.0;
2080 bool has_video_pts =
false;
2083 has_video_pts =
true;
2085 bool has_audio_pts =
false;
2088 has_audio_pts =
true;
2092 while (!has_video_pts || !has_audio_pts) {
2094 if (GetNextPacket() < 0)
2099 int64_t pts = GetPacketPTS();
2102 if (!has_video_pts && packet->stream_index == videoStream) {
2108 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2109 has_video_pts =
true;
2112 else if (!has_audio_pts && packet->stream_index == audioStream) {
2118 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2119 has_audio_pts =
true;
2125 if (has_video_pts && has_audio_pts) {
2137 pts_offset_seconds = std::max(video_pts_offset_seconds, audio_pts_offset_seconds);
2142int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2144 int64_t previous_video_frame = current_video_frame;
2153 if (current_video_frame == 0)
2154 current_video_frame = frame;
2158 if (frame == previous_video_frame) {
2163 current_video_frame++;
2172int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2174 double seconds = (double(frame_number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2184int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2186 double seconds = (double(frame_number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2196AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2204 int64_t whole_frame = int64_t(frame);
2207 double sample_start_percentage = frame - double(whole_frame);
2213 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2216 if (whole_frame < 1)
2218 if (sample_start < 0)
2225 if (previous_packet_location.
frame != -1) {
2226 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2227 int64_t orig_frame = location.
frame;
2232 location.
frame = previous_packet_location.
frame;
2235 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAudioPTSLocation (Audio Gap Detected)",
"Source Frame", orig_frame,
"Source Audio Sample", orig_start,
"Target Frame", location.
frame,
"Target Audio Sample", location.
sample_start,
"pts", pts);
2244 previous_packet_location = location;
2251std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2253 std::shared_ptr<Frame> output = working_cache.
GetFrame(requested_frame);
2257 output = working_cache.
GetFrame(requested_frame);
2258 if(output)
return output;
2266 working_cache.
Add(output);
2269 if (requested_frame > largest_frame_processed)
2270 largest_frame_processed = requested_frame;
2277bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2280 bool seek_trash =
false;
2281 int64_t max_seeked_frame = seek_audio_frame_found;
2282 if (seek_video_frame_found > max_seeked_frame) {
2283 max_seeked_frame = seek_video_frame_found;
2285 if ((
info.
has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2286 (
info.
has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2294void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2297 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2300 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.
GetFrames();
2301 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2304 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2307 std::shared_ptr<Frame> f = *working_itr;
2310 if (!f || f->number > requested_frame) {
2316 double frame_pts_seconds = (double(f->number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2317 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2320 bool is_video_ready =
false;
2321 bool is_audio_ready =
false;
2322 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2323 if ((frame_pts_seconds <= video_pts_seconds)
2324 || (recent_pts_diff > 1.5)
2328 is_video_ready =
true;
2330 "frame_number", f->number,
2331 "frame_pts_seconds", frame_pts_seconds,
2332 "video_pts_seconds", video_pts_seconds,
2333 "recent_pts_diff", recent_pts_diff);
2337 for (int64_t previous_frame = requested_frame - 1; previous_frame > 0; previous_frame--) {
2339 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2341 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2346 if (last_video_frame && !f->has_image_data) {
2348 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2349 }
else if (!f->has_image_data) {
2350 f->AddColor(
"#000000");
2355 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2356 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2357 || (recent_pts_diff > 1.5)
2362 is_audio_ready =
true;
2364 "frame_number", f->number,
2365 "frame_pts_seconds", frame_pts_seconds,
2366 "audio_pts_seconds", audio_pts_seconds,
2367 "audio_pts_diff", audio_pts_diff,
2368 "recent_pts_diff", recent_pts_diff);
2370 bool is_seek_trash = IsPartialFrame(f->number);
2378 "frame_number", f->number,
2379 "is_video_ready", is_video_ready,
2380 "is_audio_ready", is_audio_ready,
2386 if ((!packet_status.
end_of_file && is_video_ready && is_audio_ready) || packet_status.
end_of_file || is_seek_trash) {
2389 "requested_frame", requested_frame,
2390 "f->number", f->number,
2391 "is_seek_trash", is_seek_trash,
2392 "Working Cache Count", working_cache.
Count(),
2396 if (!is_seek_trash) {
2401 working_cache.
Remove(f->number);
2404 last_frame = f->number;
2407 working_cache.
Remove(f->number);
2414 working_frames.clear();
2415 working_frames.shrink_to_fit();
2419void FFmpegReader::CheckFPS() {
2427 int frames_per_second[3] = {0,0,0};
2428 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
2431 int all_frames_detected = 0;
2432 int starting_frames_detected = 0;
2437 if (GetNextPacket() < 0)
2442 if (packet->stream_index == videoStream) {
2445 fps_index = int(video_seconds);
2448 if (fps_index >= 0 && fps_index < max_fps_index) {
2450 starting_frames_detected++;
2451 frames_per_second[fps_index]++;
2455 all_frames_detected++;
2460 float avg_fps = 30.0;
2461 if (starting_frames_detected > 0 && fps_index > 0) {
2462 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
2466 if (avg_fps < 8.0) {
2475 if (all_frames_detected > 0) {
2489void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
2493 av_freep(&remove_frame->data[0]);
2501void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
2506 delete remove_packet;
2521 root[
"type"] =
"FFmpegReader";
2522 root[
"path"] =
path;
2523 switch (duration_strategy) {
2525 root[
"duration_strategy"] =
"VideoPreferred";
2528 root[
"duration_strategy"] =
"AudioPreferred";
2532 root[
"duration_strategy"] =
"LongestStream";
2549 catch (
const std::exception& e) {
2551 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
2562 if (!root[
"path"].isNull())
2563 path = root[
"path"].asString();
2564 if (!root[
"duration_strategy"].isNull()) {
2565 const std::string strategy = root[
"duration_strategy"].asString();
2566 if (strategy ==
"VideoPreferred") {
2568 }
else if (strategy ==
"AudioPreferred") {
Shared helpers for Crop effect scaling logic.
Header file for all Exception classes.
AVPixelFormat hw_de_av_pix_fmt_global
AVHWDeviceType hw_de_av_device_type_global
Header file for FFmpegReader class.
Header file for FFmpegUtilities.
#define AV_FREE_CONTEXT(av_context)
#define AV_FREE_FRAME(av_frame)
#define SWR_CONVERT(ctx, out, linesize, out_count, in, linesize2, in_count)
#define AV_GET_CODEC_TYPE(av_stream)
#define AV_GET_CODEC_PIXEL_FORMAT(av_stream, av_context)
#define AV_GET_CODEC_CONTEXT(av_stream, av_codec)
#define AV_FIND_DECODER_CODEC_ID(av_stream)
#define AV_ALLOCATE_FRAME()
#define AV_COPY_PICTURE_DATA(av_frame, buffer, pix_fmt, width, height)
#define AV_FREE_PACKET(av_packet)
#define AVCODEC_REGISTER_ALL
#define AV_GET_CODEC_ATTRIBUTES(av_stream, av_context)
#define AV_ALLOCATE_IMAGE(av_frame, pix_fmt, width, height)
#define AV_GET_SAMPLE_FORMAT(av_stream, av_context)
#define AV_RESET_FRAME(av_frame)
Cross-platform helper to encourage returning freed memory to the OS.
#define FF_VIDEO_NUM_PROCESSORS
#define FF_AUDIO_NUM_PROCESSORS
Header file for Timeline class.
Header file for ZeroMQ-based Logger class.
void SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels)
Set maximum bytes to a different amount based on a ReaderInfo struct.
int64_t Count()
Count the frames in the queue.
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
std::vector< std::shared_ptr< openshot::Frame > > GetFrames()
Get an array of all Frames.
void Remove(int64_t frame_number)
Remove a specific frame.
void Clear()
Clear the cache of all frames.
This class represents a clip (used to arrange readers on the timeline)
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
openshot::TimelineBase * ParentTimeline() override
Get the associated Timeline pointer (if any)
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
void Open() override
Open File - which is called by the constructor automatically.
FFmpegReader(const std::string &path, bool inspect_reader=true)
Constructor for FFmpegReader.
Json::Value JsonValue() const override
Generate Json::Value for this object.
bool GetIsDurationKnown()
Return true if frame can be read with GetFrame()
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
CacheMemory final_cache
Final cache object used to hold final frames.
virtual ~FFmpegReader()
Destructor.
std::string Json() const override
Generate JSON string of this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t requested_frame) override
void Close() override
Close File.
void SetJson(const std::string value) override
Load JSON string into this object.
This class represents a fraction.
int num
Numerator for the fraction.
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
int den
Denominator for the fraction.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Exception when no valid codec is found for a file.
Exception for files that can not be found or opened.
Exception for invalid JSON.
Point GetMaxPoint() const
Get max point (by Y coordinate)
Exception when no streams are found in the file.
Exception when memory could not be allocated.
Coordinate co
This is the primary coordinate.
openshot::ReaderInfo info
Information about the current media file.
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Exception when a reader is closed, and a frame is requested.
int DE_LIMIT_WIDTH_MAX
Maximum columns that hardware decode can handle.
int HW_DE_DEVICE_SET
Which GPU to use to decode (0 is the first)
int DE_LIMIT_HEIGHT_MAX
Maximum rows that hardware decode can handle.
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
int HARDWARE_DECODER
Use video codec for faster video decoding (if supported)
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
This class represents a timeline.
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Append debug information.
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
This namespace is the default namespace for all code in the openshot library.
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
DurationStrategy
This enumeration determines which duration source to favor.
@ VideoPreferred
Prefer the video stream's duration, fallback to audio then container.
@ LongestStream
Use the longest value from video, audio, or container.
@ AudioPreferred
Prefer the audio stream's duration, fallback to video then container.
bool TrimMemoryToOS(bool force) noexcept
Attempt to return unused heap memory to the operating system.
void ApplyCropResizeScale(Clip *clip, int source_width, int source_height, int &max_width, int &max_height)
Scale the requested max_width / max_height based on the Crop resize amount, capped by source size.
const Json::Value stringToJson(const std::string value)
This struct holds the associated video frame and starting sample # for an audio packet.
bool is_near(AudioLocation location, int samples_per_frame, int64_t amount)
int64_t packets_decoded()
int audio_bit_rate
The bit rate of the audio stream (in bytes)
int video_bit_rate
The bit rate of the video stream (in bytes)
bool has_single_image
Determines if this file only contains a single image.
float duration
Length of time (in seconds)
openshot::Fraction audio_timebase
The audio timebase determines how long each audio packet should be played.
int width
The width of the video (in pixesl)
int channels
The number of audio channels used in the audio stream.
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
openshot::Fraction display_ratio
The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3)
int height
The height of the video (in pixels)
int pixel_format
The pixel format (i.e. YUV420P, RGB24, etc...)
int64_t video_length
The number of frames in the video stream.
std::string acodec
The name of the audio codec used to encode / decode the video stream.
std::map< std::string, std::string > metadata
An optional map/dictionary of metadata for this reader.
std::string vcodec
The name of the video codec used to encode / decode the video stream.
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square)
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
bool has_video
Determines if this file has a video stream.
bool has_audio
Determines if this file has an audio stream.
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
int video_stream_index
The index of the video stream.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
int audio_stream_index
The index of the audio stream.
int64_t file_size
Size of file (in bytes)