Repository: opencv/opencv
File: modules/videoio/src/cap_ffmpeg_impl.hpp (C++)

Code before:
}

double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
{
    return (double)(dts - ic->streams[video_stream]->start_time) *
        r2d(ic->streams[video_stream]->time_base);
}

void CvCapture_FFMPEG::get_rotation_angle()
{
    rotation_angle = 0;
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(57, 68, 100)
    const uint8_t *data = 0;
    data = av_stream_get_side_data(video_st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
    if (data)
    {
        rotation_angle = -cvRound(av_display_rotation_get((const int32_t*)data));
        if (rotation_angle < 0)
            rotation_angle += 360;
    }
#elif LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
    AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
    if (rotate_tag != NULL)
        rotation_angle = atoi(rotate_tag->value);
#endif
}

void CvCapture_FFMPEG::seek(int64_t _frame_number)
{
    if (!rawMode) {
        CV_Assert(context);
    }
    _frame_number = std::min(_frame_number, get_total_frames());
    int delta = !rawMode ? 16 : 0;

    // if we have not grabbed a single frame before first seek, let's read the first frame
    // and get some valuable information during the process
    if( first_frame_number < 0 && get_total_frames() > 1 )
        grabFrame();

    for(;;)
    {
        int64_t _frame_number_temp = std::max(_frame_number-delta, (int64_t)0);
        double sec = (double)_frame_number_temp / get_fps();
        int64_t time_stamp = ic->streams[video_stream]->start_time;
        double  time_base  = r2d(ic->streams[video_stream]->time_base);
        time_stamp += (int64_t)(sec / time_base + 0.5);
        if (get_total_frames() > 1) av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_BACKWARD);
        if(!rawMode)
            avcodec_flush_buffers(context);
        if( _frame_number > 0 )

Review comment (bug):
This segfaults if `av_packet_side_data_get` returns NULL. Which is does if there is no side data present. See https://ffmpeg.org/doxygen/8.0/group__lavc__packet__side__data.html#ga61a3a0fba92a308208c8ab957472d23c.

This showed up in `videoio_encapsulate.write/7` of `opencv_test_videoio`.

Code after:
{
    double sec = dts_to_sec(dts);
    return (int64_t)(get_fps() * sec + 0.5);
}

double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
{
    return (double)(dts - ic->streams[video_stream]->start_time) *
        r2d(ic->streams[video_stream]->time_base);
}

void CvCapture_FFMPEG::get_rotation_angle()
{
    rotation_angle = 0;
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(57, 68, 100)
    const uint8_t *data = 0;
    // av_stream_get_side_data removed in FFmpeg release 8.0
# if (LIBAVCODEC_BUILD < CALC_FFMPEG_VERSION(62, 11, 100))
    data = av_stream_get_side_data(video_st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
# else
    AVPacketSideData* sd = video_st->codecpar->coded_side_data;
    int nb_sd = video_st->codecpar->nb_coded_side_data;
    if (sd && nb_sd > 0)
    {
        const AVPacketSideData* mtx = av_packet_side_data_get(sd,  nb_sd, AV_PKT_DATA_DISPLAYMATRIX);
        data = mtx->data;
    }
# endif
    if (data)
    {
        rotation_angle = -cvRound(av_display_rotation_get((const int32_t*)data));
        if (rotation_angle < 0)
            rotation_angle += 360;
    }
#elif LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
    AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
    if (rotate_tag != NULL)
        rotation_angle = atoi(rotate_tag->value);
#endif
}

void CvCapture_FFMPEG::seek(int64_t _frame_number)
{
    if (!rawMode) {
        CV_Assert(context);
    }
    _frame_number = std::min(_frame_number, get_total_frames());
    int delta = !rawMode ? 16 : 0;

    // if we have not grabbed a single frame before first seek, let's read the first frame
    // and get some valuable information during the process