32 #define ENABLE_VAAPI 0
35 #define MAX_SUPPORTED_WIDTH 1950
36 #define MAX_SUPPORTED_HEIGHT 1100
39 #include "libavutil/hwcontext_vaapi.h"
41 typedef struct VAAPIDecodeContext {
43 VAEntrypoint va_entrypoint;
45 VAContextID va_context;
47 #if FF_API_STRUCT_VAAPI_CONTEXT
50 struct vaapi_context *old_context;
51 AVBufferRef *device_ref;
55 AVHWDeviceContext *device;
56 AVVAAPIDeviceContext *hwctx;
58 AVHWFramesContext *frames;
59 AVVAAPIFramesContext *hwfc;
61 enum AVPixelFormat surface_format;
80 : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), NO_PTS_OFFSET(-99999),
81 path(
path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), is_open(false),
82 seek_audio_frame_found(0), seek_video_frame_found(0),is_duration_known(false), largest_frame_processed(0),
83 current_video_frame(0), packet(NULL), duration_strategy(duration_strategy),
84 audio_pts(0), video_pts(0), pFormatCtx(NULL), videoStream(-1), audioStream(-1), pCodecCtx(NULL), aCodecCtx(NULL),
85 pStream(NULL), aStream(NULL), pFrame(NULL), previous_packet_location{-1,0},
93 pts_offset_seconds = NO_PTS_OFFSET;
94 video_pts_seconds = NO_PTS_OFFSET;
95 audio_pts_seconds = NO_PTS_OFFSET;
102 if (inspect_reader) {
124 if (abs(diff) <= amount)
135 static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx,
const enum AVPixelFormat *pix_fmts)
137 const enum AVPixelFormat *p;
139 for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
141 #if defined(__linux__)
143 case AV_PIX_FMT_VAAPI:
148 case AV_PIX_FMT_VDPAU:
156 case AV_PIX_FMT_DXVA2_VLD:
161 case AV_PIX_FMT_D3D11:
167 #if defined(__APPLE__)
169 case AV_PIX_FMT_VIDEOTOOLBOX:
176 case AV_PIX_FMT_CUDA:
192 return AV_PIX_FMT_NONE;
195 int FFmpegReader::IsHardwareDecodeSupported(
int codecid)
199 case AV_CODEC_ID_H264:
200 case AV_CODEC_ID_MPEG2VIDEO:
201 case AV_CODEC_ID_VC1:
202 case AV_CODEC_ID_WMV1:
203 case AV_CODEC_ID_WMV2:
204 case AV_CODEC_ID_WMV3:
219 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
229 if (avformat_open_input(&pFormatCtx,
path.c_str(), NULL, NULL) != 0)
233 if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
240 packet_status.
reset(
true);
243 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
245 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_VIDEO && videoStream < 0) {
252 if (
AV_GET_CODEC_TYPE(pFormatCtx->streams[i]) == AVMEDIA_TYPE_AUDIO && audioStream < 0) {
259 if (videoStream == -1 && audioStream == -1)
263 if (videoStream != -1) {
268 pStream = pFormatCtx->streams[videoStream];
274 const AVCodec *pCodec = avcodec_find_decoder(codecId);
275 AVDictionary *
opts = NULL;
276 int retry_decode_open = 2;
281 if (
hw_de_on && (retry_decode_open==2)) {
283 hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id);
286 retry_decode_open = 0;
291 if (pCodec == NULL) {
292 throw InvalidCodec(
"A valid video codec could not be found for this file.",
path);
296 av_dict_set(&
opts,
"strict",
"experimental", 0);
300 int i_decoder_hw = 0;
302 char *adapter_ptr = NULL;
305 fprintf(stderr,
"Hardware decoding device number: %d\n", adapter_num);
308 pCodecCtx->get_format = get_hw_dec_format;
310 if (adapter_num < 3 && adapter_num >=0) {
311 #if defined(__linux__)
312 snprintf(adapter,
sizeof(adapter),
"/dev/dri/renderD%d", adapter_num+128);
313 adapter_ptr = adapter;
315 switch (i_decoder_hw) {
317 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
320 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
323 hw_de_av_device_type = AV_HWDEVICE_TYPE_VDPAU;
326 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
329 hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI;
333 #elif defined(_WIN32)
336 switch (i_decoder_hw) {
338 hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA;
341 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
344 hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA;
347 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
350 hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2;
353 #elif defined(__APPLE__)
356 switch (i_decoder_hw) {
358 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
361 hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV;
364 hw_de_av_device_type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
374 #if defined(__linux__)
375 if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
376 #elif defined(_WIN32)
377 if( adapter_ptr != NULL ) {
378 #elif defined(__APPLE__)
379 if( adapter_ptr != NULL ) {
388 hw_device_ctx = NULL;
390 if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, adapter_ptr, NULL, 0) >= 0) {
391 if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) {
433 pCodecCtx->thread_type &= ~FF_THREAD_FRAME;
437 int avcodec_return = avcodec_open2(pCodecCtx, pCodec, &
opts);
438 if (avcodec_return < 0) {
439 std::stringstream avcodec_error_msg;
440 avcodec_error_msg <<
"A video codec was found, but could not be opened. Error: " << av_err2string(avcodec_return);
446 AVHWFramesConstraints *constraints = NULL;
447 void *hwconfig = NULL;
448 hwconfig = av_hwdevice_hwconfig_alloc(hw_device_ctx);
452 ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config;
453 constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig);
456 if (pCodecCtx->coded_width < constraints->min_width ||
457 pCodecCtx->coded_height < constraints->min_height ||
458 pCodecCtx->coded_width > constraints->max_width ||
459 pCodecCtx->coded_height > constraints->max_height) {
462 retry_decode_open = 1;
465 av_buffer_unref(&hw_device_ctx);
466 hw_device_ctx = NULL;
471 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);
472 retry_decode_open = 0;
474 av_hwframe_constraints_free(&constraints);
487 if (pCodecCtx->coded_width < 0 ||
488 pCodecCtx->coded_height < 0 ||
489 pCodecCtx->coded_width > max_w ||
490 pCodecCtx->coded_height > max_h ) {
491 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);
493 retry_decode_open = 1;
496 av_buffer_unref(&hw_device_ctx);
497 hw_device_ctx = NULL;
501 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);
502 retry_decode_open = 0;
510 retry_decode_open = 0;
512 }
while (retry_decode_open);
521 if (audioStream != -1) {
526 aStream = pFormatCtx->streams[audioStream];
532 const AVCodec *aCodec = avcodec_find_decoder(codecId);
538 if (aCodec == NULL) {
539 throw InvalidCodec(
"A valid audio codec could not be found for this file.",
path);
543 AVDictionary *
opts = NULL;
544 av_dict_set(&
opts,
"strict",
"experimental", 0);
547 if (avcodec_open2(aCodecCtx, aCodec, &
opts) < 0)
548 throw InvalidCodec(
"An audio codec was found, but could not be opened.",
path);
558 AVDictionaryEntry *tag = NULL;
559 while ((tag = av_dict_get(pFormatCtx->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
560 QString str_key = tag->key;
561 QString str_value = tag->value;
562 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
566 for (
unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
567 AVStream* st = pFormatCtx->streams[i];
568 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
570 for (
int j = 0; j < st->nb_side_data; j++) {
571 AVPacketSideData *sd = &st->side_data[j];
574 if (sd->type == AV_PKT_DATA_DISPLAYMATRIX &&
575 sd->size >= 9 *
sizeof(int32_t) &&
578 double rotation = -av_display_rotation_get(
579 reinterpret_cast<int32_t *
>(sd->data));
580 if (std::isnan(rotation)) rotation = 0;
584 else if (sd->type == AV_PKT_DATA_SPHERICAL) {
589 const AVSphericalMapping* map =
590 reinterpret_cast<const AVSphericalMapping*
>(sd->data);
593 const char* proj_name = av_spherical_projection_name(map->projection);
599 auto to_deg = [](int32_t v){
600 return (
double)v / 65536.0;
602 info.
metadata[
"spherical_yaw"] = std::to_string(to_deg(map->yaw));
603 info.
metadata[
"spherical_pitch"] = std::to_string(to_deg(map->pitch));
604 info.
metadata[
"spherical_roll"] = std::to_string(to_deg(map->roll));
612 previous_packet_location.
frame = -1;
644 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
650 AVPacket *recent_packet = packet;
655 int max_attempts = 128;
660 "attempts", attempts);
672 RemoveAVPacket(recent_packet);
677 if(avcodec_is_open(pCodecCtx)) {
678 avcodec_flush_buffers(pCodecCtx);
684 av_buffer_unref(&hw_device_ctx);
685 hw_device_ctx = NULL;
689 if (img_convert_ctx) {
690 sws_freeContext(img_convert_ctx);
691 img_convert_ctx =
nullptr;
693 if (pFrameRGB_cached) {
700 if(avcodec_is_open(aCodecCtx)) {
701 avcodec_flush_buffers(aCodecCtx);
713 working_cache.
Clear();
716 avformat_close_input(&pFormatCtx);
717 av_freep(&pFormatCtx);
725 largest_frame_processed = 0;
726 seek_audio_frame_found = 0;
727 seek_video_frame_found = 0;
728 current_video_frame = 0;
729 last_video_frame.reset();
733 bool FFmpegReader::HasAlbumArt() {
737 return pFormatCtx && videoStream >= 0 && pFormatCtx->streams[videoStream]
738 && (pFormatCtx->streams[videoStream]->disposition & AV_DISPOSITION_ATTACHED_PIC);
741 double FFmpegReader::PickDurationSeconds()
const {
742 auto has_value = [](
double value) {
return value > 0.0; };
744 switch (duration_strategy) {
746 if (has_value(video_stream_duration_seconds))
747 return video_stream_duration_seconds;
748 if (has_value(audio_stream_duration_seconds))
749 return audio_stream_duration_seconds;
750 if (has_value(format_duration_seconds))
751 return format_duration_seconds;
754 if (has_value(audio_stream_duration_seconds))
755 return audio_stream_duration_seconds;
756 if (has_value(video_stream_duration_seconds))
757 return video_stream_duration_seconds;
758 if (has_value(format_duration_seconds))
759 return format_duration_seconds;
764 double longest = 0.0;
765 if (has_value(video_stream_duration_seconds))
766 longest = std::max(longest, video_stream_duration_seconds);
767 if (has_value(audio_stream_duration_seconds))
768 longest = std::max(longest, audio_stream_duration_seconds);
769 if (has_value(format_duration_seconds))
770 longest = std::max(longest, format_duration_seconds);
771 if (has_value(longest))
777 if (has_value(format_duration_seconds))
778 return format_duration_seconds;
779 if (has_value(inferred_duration_seconds))
780 return inferred_duration_seconds;
785 void FFmpegReader::ApplyDurationStrategy() {
787 const double chosen_seconds = PickDurationSeconds();
789 if (chosen_seconds <= 0.0 || fps_value <= 0.0) {
792 is_duration_known =
false;
796 const int64_t frames =
static_cast<int64_t
>(std::llround(chosen_seconds * fps_value));
800 is_duration_known =
false;
805 info.
duration =
static_cast<float>(
static_cast<double>(frames) / fps_value);
806 is_duration_known =
true;
809 void FFmpegReader::UpdateAudioInfo() {
824 auto record_duration = [](
double &target,
double seconds) {
826 target = std::max(target, seconds);
831 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
862 if (aStream->duration > 0) {
865 if (pFormatCtx->duration > 0) {
867 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
897 ApplyDurationStrategy();
900 AVDictionaryEntry *tag = NULL;
901 while ((tag = av_dict_get(aStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
902 QString str_key = tag->key;
903 QString str_value = tag->value;
904 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
908 void FFmpegReader::UpdateVideoInfo() {
914 auto record_duration = [](
double &target,
double seconds) {
916 target = std::max(target, seconds);
921 info.
file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
928 AVRational framerate = av_guess_frame_rate(pFormatCtx, pStream, NULL);
940 if (pStream->sample_aspect_ratio.num != 0) {
963 if (!check_interlace) {
964 check_interlace =
true;
966 switch(field_order) {
967 case AV_FIELD_PROGRESSIVE:
980 case AV_FIELD_UNKNOWN:
982 check_interlace =
false;
997 if (pFormatCtx->duration >= 0) {
999 record_duration(format_duration_seconds,
static_cast<double>(pFormatCtx->duration) / AV_TIME_BASE);
1009 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1010 pStream->duration == AV_NOPTS_VALUE && pFormatCtx->duration == AV_NOPTS_VALUE) {
1012 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1016 if (video_stream_duration_seconds <= 0.0 && format_duration_seconds <= 0.0 &&
1017 pFormatCtx && pFormatCtx->iformat && strcmp(pFormatCtx->iformat->name,
"gif") == 0) {
1018 record_duration(video_stream_duration_seconds, 60 * 60 * 1);
1022 ApplyDurationStrategy();
1025 AVDictionaryEntry *tag = NULL;
1026 while ((tag = av_dict_get(pStream->metadata,
"", tag, AV_DICT_IGNORE_SUFFIX))) {
1027 QString str_key = tag->key;
1028 QString str_value = tag->value;
1029 info.
metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
1034 return this->is_duration_known;
1040 throw ReaderClosed(
"The FFmpegReader is closed. Call Open() before calling this method.",
path);
1043 if (requested_frame < 1)
1044 requested_frame = 1;
1049 throw InvalidFile(
"Could not detect the duration of the video or audio stream.",
path);
1065 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1079 int64_t diff = requested_frame - last_frame;
1080 if (diff >= 1 && diff <= 20) {
1082 frame = ReadStream(requested_frame);
1087 Seek(requested_frame);
1096 frame = ReadStream(requested_frame);
1104 std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {
1106 bool check_seek =
false;
1107 int packet_error = -1;
1117 CheckWorkingFrames(requested_frame);
1122 if (is_cache_found) {
1126 if (!hold_packet || !packet) {
1128 packet_error = GetNextPacket();
1129 if (packet_error < 0 && !packet) {
1140 check_seek = CheckSeek(
false);
1152 if ((
info.
has_video && packet && packet->stream_index == videoStream) ||
1156 ProcessVideoPacket(requested_frame);
1159 if ((
info.
has_audio && packet && packet->stream_index == audioStream) ||
1163 ProcessAudioPacket(requested_frame);
1168 if ((!
info.
has_video && packet && packet->stream_index == videoStream) ||
1169 (!
info.
has_audio && packet && packet->stream_index == audioStream)) {
1171 if (packet->stream_index == videoStream) {
1173 }
else if (packet->stream_index == audioStream) {
1179 RemoveAVPacket(packet);
1189 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);
1206 "largest_frame_processed", largest_frame_processed,
1207 "Working Cache Count", working_cache.
Count());
1216 CheckWorkingFrames(requested_frame);
1232 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1235 if (!frame->has_image_data) {
1240 frame->AddAudioSilence(samples_in_frame);
1245 std::shared_ptr<Frame> f = CreateFrame(largest_frame_processed);
1247 f->AddAudioSilence(samples_in_frame);
1255 int FFmpegReader::GetNextPacket() {
1256 int found_packet = 0;
1257 AVPacket *next_packet;
1258 next_packet =
new AVPacket();
1259 found_packet = av_read_frame(pFormatCtx, next_packet);
1263 RemoveAVPacket(packet);
1266 if (found_packet >= 0) {
1268 packet = next_packet;
1271 if (packet->stream_index == videoStream) {
1273 }
else if (packet->stream_index == audioStream) {
1282 return found_packet;
1286 bool FFmpegReader::GetAVFrame() {
1287 int frameFinished = 0;
1293 int send_packet_err = 0;
1294 int64_t send_packet_pts = 0;
1295 if ((packet && packet->stream_index == videoStream) || !packet) {
1296 send_packet_err = avcodec_send_packet(pCodecCtx, packet);
1298 if (packet && send_packet_err >= 0) {
1299 send_packet_pts = GetPacketPTS();
1300 hold_packet =
false;
1310 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1311 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);
1312 if (send_packet_err == AVERROR(EAGAIN)) {
1314 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::GetAVFrame (send packet: AVERROR(EAGAIN): user must read output with avcodec_receive_frame()",
"send_packet_pts", send_packet_pts);
1316 if (send_packet_err == AVERROR(EINVAL)) {
1317 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);
1319 if (send_packet_err == AVERROR(ENOMEM)) {
1320 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);
1327 int receive_frame_err = 0;
1328 AVFrame *next_frame2;
1336 next_frame2 = next_frame;
1339 while (receive_frame_err >= 0) {
1340 receive_frame_err = avcodec_receive_frame(pCodecCtx, next_frame2);
1342 if (receive_frame_err != 0) {
1343 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);
1345 if (receive_frame_err == AVERROR_EOF) {
1347 "FFmpegReader::GetAVFrame (receive frame: AVERROR_EOF: EOF detected from decoder, flushing buffers)",
"send_packet_pts", send_packet_pts);
1348 avcodec_flush_buffers(pCodecCtx);
1351 if (receive_frame_err == AVERROR(EINVAL)) {
1353 "FFmpegReader::GetAVFrame (receive frame: AVERROR(EINVAL): invalid frame received, flushing buffers)",
"send_packet_pts", send_packet_pts);
1354 avcodec_flush_buffers(pCodecCtx);
1356 if (receive_frame_err == AVERROR(EAGAIN)) {
1358 "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);
1360 if (receive_frame_err == AVERROR_INPUT_CHANGED) {
1362 "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);
1373 if (next_frame2->format == hw_de_av_pix_fmt) {
1374 next_frame->format = AV_PIX_FMT_YUV420P;
1375 if ((err = av_hwframe_transfer_data(next_frame,next_frame2,0)) < 0) {
1378 if ((err = av_frame_copy_props(next_frame,next_frame2)) < 0) {
1386 next_frame = next_frame2;
1398 av_image_copy(pFrame->data, pFrame->linesize, (
const uint8_t**)next_frame->data, next_frame->linesize,
1405 if (next_frame->pts != AV_NOPTS_VALUE) {
1408 video_pts = next_frame->pts;
1409 }
else if (next_frame->pkt_dts != AV_NOPTS_VALUE) {
1411 video_pts = next_frame->pkt_dts;
1415 "FFmpegReader::GetAVFrame (Successful frame received)",
"video_pts", video_pts,
"send_packet_pts", send_packet_pts);
1426 avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);
1432 if (frameFinished) {
1436 av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt,
info.
width,
1445 return frameFinished;
1449 bool FFmpegReader::CheckSeek(
bool is_video) {
1454 if ((is_video_seek && !seek_video_frame_found) || (!is_video_seek && !seek_audio_frame_found))
1462 int64_t max_seeked_frame = std::max(seek_audio_frame_found, seek_video_frame_found);
1465 if (max_seeked_frame >= seeking_frame) {
1468 "is_video_seek", is_video_seek,
1469 "max_seeked_frame", max_seeked_frame,
1470 "seeking_frame", seeking_frame,
1471 "seeking_pts", seeking_pts,
1472 "seek_video_frame_found", seek_video_frame_found,
1473 "seek_audio_frame_found", seek_audio_frame_found);
1476 Seek(seeking_frame - (10 * seek_count * seek_count));
1480 "is_video_seek", is_video_seek,
1481 "packet->pts", GetPacketPTS(),
1482 "seeking_pts", seeking_pts,
1483 "seeking_frame", seeking_frame,
1484 "seek_video_frame_found", seek_video_frame_found,
1485 "seek_audio_frame_found", seek_audio_frame_found);
1499 void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
1502 int frame_finished = GetAVFrame();
1505 if (!frame_finished) {
1508 RemoveAVFrame(pFrame);
1514 int64_t current_frame = ConvertVideoPTStoFrame(video_pts);
1517 if (!seek_video_frame_found && is_seeking)
1518 seek_video_frame_found = current_frame;
1524 working_cache.
Add(CreateFrame(requested_frame));
1536 AVFrame *pFrameRGB = pFrameRGB_cached;
1539 if (pFrameRGB ==
nullptr)
1541 pFrameRGB_cached = pFrameRGB;
1544 uint8_t *buffer =
nullptr;
1565 max_width = std::max(
float(max_width), max_width * max_scale_x);
1566 max_height = std::max(
float(max_height), max_height * max_scale_y);
1572 QSize width_size(max_width * max_scale_x,
1575 max_height * max_scale_y);
1577 if (width_size.width() >= max_width && width_size.height() >= max_height) {
1578 max_width = std::max(max_width, width_size.width());
1579 max_height = std::max(max_height, width_size.height());
1581 max_width = std::max(max_width, height_size.width());
1582 max_height = std::max(max_height, height_size.height());
1589 float preview_ratio = 1.0;
1596 max_width =
info.
width * max_scale_x * preview_ratio;
1597 max_height =
info.
height * max_scale_y * preview_ratio;
1605 int original_height = height;
1606 if (max_width != 0 && max_height != 0 && max_width < width && max_height < height) {
1608 float ratio = float(width) / float(height);
1609 int possible_width = round(max_height * ratio);
1610 int possible_height = round(max_width / ratio);
1612 if (possible_width <= max_width) {
1614 width = possible_width;
1615 height = max_height;
1619 height = possible_height;
1624 const int bytes_per_pixel = 4;
1625 int raw_buffer_size = (width * height * bytes_per_pixel) + 128;
1628 constexpr
size_t ALIGNMENT = 32;
1629 int buffer_size = ((raw_buffer_size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
1630 buffer = (
unsigned char*) aligned_malloc(buffer_size, ALIGNMENT);
1635 int scale_mode = SWS_FAST_BILINEAR;
1637 scale_mode = SWS_BICUBIC;
1639 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);
1640 if (!img_convert_ctx)
1644 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
1645 original_height, pFrameRGB->data, pFrameRGB->linesize);
1648 std::shared_ptr<Frame> f = CreateFrame(current_frame);
1653 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888_Premultiplied, buffer);
1656 f->AddImage(width, height, bytes_per_pixel, QImage::Format_RGBA8888, buffer);
1660 working_cache.
Add(f);
1663 last_video_frame = f;
1669 RemoveAVFrame(pFrame);
1675 ZmqLogger::Instance()->
AppendDebugMethod(
"FFmpegReader::ProcessVideoPacket (After)",
"requested_frame", requested_frame,
"current_frame", current_frame,
"f->number", f->number,
"video_pts_seconds", video_pts_seconds);
1679 void FFmpegReader::ProcessAudioPacket(int64_t requested_frame) {
1682 if (packet && packet->pts != AV_NOPTS_VALUE) {
1684 location = GetAudioPTSLocation(packet->pts);
1687 if (!seek_audio_frame_found && is_seeking)
1688 seek_audio_frame_found = location.
frame;
1695 working_cache.
Add(CreateFrame(requested_frame));
1699 "requested_frame", requested_frame,
1700 "target_frame", location.
frame,
1704 int frame_finished = 0;
1708 int packet_samples = 0;
1712 int send_packet_err = avcodec_send_packet(aCodecCtx, packet);
1713 if (send_packet_err < 0 && send_packet_err != AVERROR_EOF) {
1717 int receive_frame_err = avcodec_receive_frame(aCodecCtx, audio_frame);
1718 if (receive_frame_err >= 0) {
1721 if (receive_frame_err == AVERROR_EOF) {
1725 if (receive_frame_err == AVERROR(EINVAL) || receive_frame_err == AVERROR_EOF) {
1727 avcodec_flush_buffers(aCodecCtx);
1729 if (receive_frame_err != 0) {
1734 int used = avcodec_decode_audio4(aCodecCtx, audio_frame, &frame_finished, packet);
1737 if (frame_finished) {
1743 audio_pts = audio_frame->pts;
1746 location = GetAudioPTSLocation(audio_pts);
1749 int plane_size = -1;
1755 data_size = av_samples_get_buffer_size(&plane_size, nb_channels,
1759 packet_samples = audio_frame->nb_samples * nb_channels;
1768 int pts_remaining_samples = packet_samples /
info.
channels;
1771 if (pts_remaining_samples == 0) {
1773 "packet_samples", packet_samples,
1775 "pts_remaining_samples", pts_remaining_samples);
1779 while (pts_remaining_samples) {
1784 int samples = samples_per_frame - previous_packet_location.
sample_start;
1785 if (samples > pts_remaining_samples)
1786 samples = pts_remaining_samples;
1789 pts_remaining_samples -= samples;
1791 if (pts_remaining_samples > 0) {
1793 previous_packet_location.
frame++;
1802 "packet_samples", packet_samples,
1810 audio_converted->nb_samples = audio_frame->nb_samples;
1811 av_samples_alloc(audio_converted->data, audio_converted->linesize,
info.
channels, audio_frame->nb_samples, AV_SAMPLE_FMT_FLTP, 0);
1827 av_opt_set_int(avr,
"out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
1836 audio_converted->data,
1837 audio_converted->linesize[0],
1838 audio_converted->nb_samples,
1840 audio_frame->linesize[0],
1841 audio_frame->nb_samples);
1844 int64_t starting_frame_number = -1;
1845 for (
int channel_filter = 0; channel_filter <
info.
channels; channel_filter++) {
1847 starting_frame_number = location.
frame;
1848 int channel_buffer_size = nb_samples;
1849 auto *channel_buffer = (
float *) (audio_converted->data[channel_filter]);
1853 int remaining_samples = channel_buffer_size;
1854 while (remaining_samples > 0) {
1859 int samples = std::fmin(samples_per_frame - start, remaining_samples);
1862 std::shared_ptr<Frame> f = CreateFrame(starting_frame_number);
1865 f->AddAudio(
true, channel_filter, start, channel_buffer, samples, 1.0f);
1869 "frame", starting_frame_number,
1872 "channel", channel_filter,
1873 "samples_per_frame", samples_per_frame);
1876 working_cache.
Add(f);
1879 remaining_samples -= samples;
1882 if (remaining_samples > 0)
1883 channel_buffer += samples;
1886 starting_frame_number++;
1894 av_free(audio_converted->data[0]);
1903 "requested_frame", requested_frame,
1904 "starting_frame", location.
frame,
1905 "end_frame", starting_frame_number - 1,
1906 "audio_pts_seconds", audio_pts_seconds);
1912 void FFmpegReader::Seek(int64_t requested_frame) {
1914 if (requested_frame < 1)
1915 requested_frame = 1;
1918 if (requested_frame > largest_frame_processed && packet_status.
end_of_file) {
1925 "requested_frame", requested_frame,
1926 "seek_count", seek_count,
1927 "last_frame", last_frame);
1930 working_cache.
Clear();
1934 video_pts_seconds = NO_PTS_OFFSET;
1936 audio_pts_seconds = NO_PTS_OFFSET;
1937 hold_packet =
false;
1939 current_video_frame = 0;
1940 largest_frame_processed = 0;
1945 packet_status.
reset(
false);
1951 int buffer_amount = 12;
1952 if (requested_frame - buffer_amount < 20) {
1966 if (seek_count == 1) {
1969 seeking_pts = ConvertFrameToVideoPTS(1);
1971 seek_audio_frame_found = 0;
1972 seek_video_frame_found = 0;
1976 bool seek_worked =
false;
1977 int64_t seek_target = 0;
1981 seek_target = ConvertFrameToVideoPTS(requested_frame - buffer_amount);
1983 fprintf(stderr,
"%s: error while seeking video stream\n", pFormatCtx->AV_FILENAME);
1986 is_video_seek =
true;
1993 seek_target = ConvertFrameToAudioPTS(requested_frame - buffer_amount);
1995 fprintf(stderr,
"%s: error while seeking audio stream\n", pFormatCtx->AV_FILENAME);
1998 is_video_seek =
false;
2007 avcodec_flush_buffers(aCodecCtx);
2011 avcodec_flush_buffers(pCodecCtx);
2014 previous_packet_location.
frame = -1;
2019 if (seek_count == 1) {
2021 seeking_pts = seek_target;
2022 seeking_frame = requested_frame;
2024 seek_audio_frame_found = 0;
2025 seek_video_frame_found = 0;
2053 int64_t FFmpegReader::GetPacketPTS() {
2055 int64_t current_pts = packet->pts;
2056 if (current_pts == AV_NOPTS_VALUE && packet->dts != AV_NOPTS_VALUE)
2057 current_pts = packet->dts;
2063 return AV_NOPTS_VALUE;
2068 void FFmpegReader::UpdatePTSOffset() {
2069 if (pts_offset_seconds != NO_PTS_OFFSET) {
2073 pts_offset_seconds = 0.0;
2074 double video_pts_offset_seconds = 0.0;
2075 double audio_pts_offset_seconds = 0.0;
2077 bool has_video_pts =
false;
2080 has_video_pts =
true;
2082 bool has_audio_pts =
false;
2085 has_audio_pts =
true;
2089 while (!has_video_pts || !has_audio_pts) {
2091 if (GetNextPacket() < 0)
2096 int64_t pts = GetPacketPTS();
2099 if (!has_video_pts && packet->stream_index == videoStream) {
2105 if (std::abs(video_pts_offset_seconds) <= 10.0) {
2106 has_video_pts =
true;
2109 else if (!has_audio_pts && packet->stream_index == audioStream) {
2115 if (std::abs(audio_pts_offset_seconds) <= 10.0) {
2116 has_audio_pts =
true;
2122 if (has_video_pts && has_audio_pts) {
2134 pts_offset_seconds = std::max(video_pts_offset_seconds, audio_pts_offset_seconds);
2139 int64_t FFmpegReader::ConvertVideoPTStoFrame(int64_t pts) {
2141 int64_t previous_video_frame = current_video_frame;
2150 if (current_video_frame == 0)
2151 current_video_frame = frame;
2155 if (frame == previous_video_frame) {
2160 current_video_frame++;
2169 int64_t FFmpegReader::ConvertFrameToVideoPTS(int64_t frame_number) {
2171 double seconds = (double(frame_number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2181 int64_t FFmpegReader::ConvertFrameToAudioPTS(int64_t frame_number) {
2183 double seconds = (double(frame_number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2193 AudioLocation FFmpegReader::GetAudioPTSLocation(int64_t pts) {
2201 int64_t whole_frame = int64_t(frame);
2204 double sample_start_percentage = frame - double(whole_frame);
2210 int sample_start = round(
double(samples_per_frame) * sample_start_percentage);
2213 if (whole_frame < 1)
2215 if (sample_start < 0)
2222 if (previous_packet_location.
frame != -1) {
2223 if (location.
is_near(previous_packet_location, samples_per_frame, samples_per_frame)) {
2224 int64_t orig_frame = location.
frame;
2229 location.
frame = previous_packet_location.
frame;
2232 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);
2241 previous_packet_location = location;
2248 std::shared_ptr<Frame> FFmpegReader::CreateFrame(int64_t requested_frame) {
2250 std::shared_ptr<Frame> output = working_cache.
GetFrame(requested_frame);
2254 output = working_cache.
GetFrame(requested_frame);
2255 if(output)
return output;
2263 working_cache.
Add(output);
2266 if (requested_frame > largest_frame_processed)
2267 largest_frame_processed = requested_frame;
2274 bool FFmpegReader::IsPartialFrame(int64_t requested_frame) {
2277 bool seek_trash =
false;
2278 int64_t max_seeked_frame = seek_audio_frame_found;
2279 if (seek_video_frame_found > max_seeked_frame) {
2280 max_seeked_frame = seek_video_frame_found;
2282 if ((
info.
has_audio && seek_audio_frame_found && max_seeked_frame >= requested_frame) ||
2283 (
info.
has_video && seek_video_frame_found && max_seeked_frame >= requested_frame)) {
2291 void FFmpegReader::CheckWorkingFrames(int64_t requested_frame) {
2294 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
2297 std::vector<std::shared_ptr<openshot::Frame>> working_frames = working_cache.
GetFrames();
2298 std::vector<std::shared_ptr<openshot::Frame>>::iterator working_itr;
2301 for(working_itr = working_frames.begin(); working_itr != working_frames.end(); ++working_itr)
2304 std::shared_ptr<Frame> f = *working_itr;
2307 if (!f || f->number > requested_frame) {
2313 double frame_pts_seconds = (double(f->number - 1) /
info.
fps.
ToDouble()) + pts_offset_seconds;
2314 double recent_pts_seconds = std::max(video_pts_seconds, audio_pts_seconds);
2317 bool is_video_ready =
false;
2318 bool is_audio_ready =
false;
2319 double recent_pts_diff = recent_pts_seconds - frame_pts_seconds;
2320 if ((frame_pts_seconds <= video_pts_seconds)
2321 || (recent_pts_diff > 1.5)
2325 is_video_ready =
true;
2327 "frame_number", f->number,
2328 "frame_pts_seconds", frame_pts_seconds,
2329 "video_pts_seconds", video_pts_seconds,
2330 "recent_pts_diff", recent_pts_diff);
2334 for (int64_t previous_frame = requested_frame - 1; previous_frame > 0; previous_frame--) {
2336 if (previous_frame_instance && previous_frame_instance->has_image_data) {
2338 f->AddImage(std::make_shared<QImage>(previous_frame_instance->GetImage()->copy()));
2343 if (last_video_frame && !f->has_image_data) {
2345 f->AddImage(std::make_shared<QImage>(last_video_frame->GetImage()->copy()));
2346 }
else if (!f->has_image_data) {
2347 f->AddColor(
"#000000");
2352 double audio_pts_diff = audio_pts_seconds - frame_pts_seconds;
2353 if ((frame_pts_seconds < audio_pts_seconds && audio_pts_diff > 1.0)
2354 || (recent_pts_diff > 1.5)
2359 is_audio_ready =
true;
2361 "frame_number", f->number,
2362 "frame_pts_seconds", frame_pts_seconds,
2363 "audio_pts_seconds", audio_pts_seconds,
2364 "audio_pts_diff", audio_pts_diff,
2365 "recent_pts_diff", recent_pts_diff);
2367 bool is_seek_trash = IsPartialFrame(f->number);
2375 "frame_number", f->number,
2376 "is_video_ready", is_video_ready,
2377 "is_audio_ready", is_audio_ready,
2383 if ((!packet_status.
end_of_file && is_video_ready && is_audio_ready) || packet_status.
end_of_file || is_seek_trash) {
2386 "requested_frame", requested_frame,
2387 "f->number", f->number,
2388 "is_seek_trash", is_seek_trash,
2389 "Working Cache Count", working_cache.
Count(),
2393 if (!is_seek_trash) {
2398 working_cache.
Remove(f->number);
2401 last_frame = f->number;
2404 working_cache.
Remove(f->number);
2411 working_frames.clear();
2412 working_frames.shrink_to_fit();
2416 void FFmpegReader::CheckFPS() {
2424 int frames_per_second[3] = {0,0,0};
2425 int max_fps_index =
sizeof(frames_per_second) /
sizeof(frames_per_second[0]);
2428 int all_frames_detected = 0;
2429 int starting_frames_detected = 0;
2434 if (GetNextPacket() < 0)
2439 if (packet->stream_index == videoStream) {
2442 fps_index = int(video_seconds);
2445 if (fps_index >= 0 && fps_index < max_fps_index) {
2447 starting_frames_detected++;
2448 frames_per_second[fps_index]++;
2452 all_frames_detected++;
2457 float avg_fps = 30.0;
2458 if (starting_frames_detected > 0 && fps_index > 0) {
2459 avg_fps = float(starting_frames_detected) / std::min(fps_index, max_fps_index);
2463 if (avg_fps < 8.0) {
2472 if (all_frames_detected > 0) {
2486 void FFmpegReader::RemoveAVFrame(AVFrame *remove_frame) {
2490 av_freep(&remove_frame->data[0]);
2498 void FFmpegReader::RemoveAVPacket(AVPacket *remove_packet) {
2503 delete remove_packet;
2518 root[
"type"] =
"FFmpegReader";
2519 root[
"path"] =
path;
2520 switch (duration_strategy) {
2522 root[
"duration_strategy"] =
"VideoPreferred";
2525 root[
"duration_strategy"] =
"AudioPreferred";
2529 root[
"duration_strategy"] =
"LongestStream";
2546 catch (
const std::exception& e) {
2548 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
2559 if (!root[
"path"].isNull())
2560 path = root[
"path"].asString();
2561 if (!root[
"duration_strategy"].isNull()) {
2562 const std::string strategy = root[
"duration_strategy"].asString();
2563 if (strategy ==
"VideoPreferred") {
2565 }
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)