searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

ffmpeg 时间基简介

2024-10-31 09:28:56
62
0

在FFmpeg中,时间基(timebase)、PTS(Presentation Time Stamp)、DTS(Decoding Time Stamp)和duration是处理视频和音频流时非常重要的概念。下面我将详细解释这些概念,并提供一个使用FFmpeg库处理这些时间相关信息的C代码示例。

时间基(Time Base)

时间基是FFmpeg中表示时间的基本单位,它是一个有理数(分数),通常由分子和分母组成。时间基定义了时间戳(PTS、DTS)和时长(duration)的精度。

PTS(Presentation Time Stamp)

PTS是数据包(或帧)应该被显示给用户的时间点,它是以时间基为单位的。对于视频流,PTS决定了帧的显示顺序。

DTS(Decoding Time Stamp)

DTS是数据包(或帧)应该被解码的时间点,也是以时间基为单位的。对于视频流,DTS决定了帧的解码顺序。

Duration

Duration是数据包(或帧)的时长,同样是以时间基为单位的。它表示从当前数据包(或帧)的开始到下一个数据包(或帧)的开始之间的时间间隔。

C代码示例

下面是一个使用FFmpeg库读取视频文件并打印每帧的PTS、DTS和duration的C代码示例:

#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <input file>\n", argv[0]);
        return 1;
    }

    const char *input_filename = argv[1];
    AVFormatContext *fmt_ctx = NULL;
    int video_stream_index = -1;
    AVPacket pkt;
    AVFrame *frame = NULL;
    AVRational time_base;

    // Initialize libavformat and register all muxers, demuxers and protocols.
    av_register_all();

    // Open input file and read header.
    if (avformat_open_input(&fmt_ctx, input_filename, NULL, NULL) != 0) {
        fprintf(stderr, "Could not open input file '%s'.\n", input_filename);
        return 1;
    }

    // Retrieve stream information.
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Could not find stream information.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Find the first video stream.
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            time_base = fmt_ctx->streams[i]->time_base;
            break;
        }
    }

    if (video_stream_index == -1) {
        fprintf(stderr, "Could not find a video stream.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Allocate video frame.
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Read frames from the file.
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == video_stream_index) {
            // Decode video frame.
            int response = avcodec_send_packet(fmt_ctx->streams[video_stream_index]->codec, &pkt);
            if (response < 0) {
                fprintf(stderr, "Error sending packet for decoding.\n");
                break;
            }

            while (response >= 0) {
                response = avcodec_receive_frame(fmt_ctx->streams[video_stream_index]->codec, frame);
                if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
                    break;
                } else if (response < 0) {
                    fprintf(stderr, "Error receiving frame from decoder.\n");
                    goto end;
                }

                // Print PTS, DTS, and duration.
                printf("Frame PTS: %"PRId64", DTS: %"PRId64", Duration: %"PRId64"\n",
                       av_rescale_q(frame->pts, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE),
                       av_rescale_q(pkt.dts, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE),
                       av_rescale_q(pkt.duration, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE));
            }
        }

        // Free the packet.
        av_packet_unref(&pkt);
    }

end:
    // Free the frame.
    av_frame_free(&frame);

    // Close the input file.
    avformat_close_input(&fmt_ctx);

    return 0;
}

注意事项

  1. 时间基转换:在打印PTS、DTS和duration时,我使用了av_rescale_q函数将它们从流的时间基转换为AV_TIME_BASE(通常是1/1000000,即微秒)。这是为了更容易理解和比较这些时间值。
  2. 错误处理:代码中的错误处理是简化的,实际使用中可能需要更详细的错误检查和恢复逻辑。
  3. 解码器:这个示例代码假设了视频流有一个可用的解码器,并且解码器能够成功解码帧。在实际应用中,可能需要检查解码器的配置和状态。
  4. 内存管理:FFmpeg使用了自己的内存管理机制,因此请确保在不再需要时释放所有分配的内存和资源。
  5. FFmpeg版本:这个示例代码基于FFmpeg的C API,并且可能需要根据您使用的FFmpeg版本进行调整。特别是,FFmpeg的API在不同版本之间可能会有所变化。
  6. 编译:要编译这个示例代码,您需要链接FFmpeg库。这通常涉及到设置正确的编译器和链接器标志,以及确保FFmpeg的开发文件(头文件和库文件)在您的构建环境中可用。
0条评论
0 / 1000
怪****叔
4文章数
0粉丝数
怪****叔
4 文章 | 0 粉丝
怪****叔
4文章数
0粉丝数
怪****叔
4 文章 | 0 粉丝
原创

ffmpeg 时间基简介

2024-10-31 09:28:56
62
0

在FFmpeg中,时间基(timebase)、PTS(Presentation Time Stamp)、DTS(Decoding Time Stamp)和duration是处理视频和音频流时非常重要的概念。下面我将详细解释这些概念,并提供一个使用FFmpeg库处理这些时间相关信息的C代码示例。

时间基(Time Base)

时间基是FFmpeg中表示时间的基本单位,它是一个有理数(分数),通常由分子和分母组成。时间基定义了时间戳(PTS、DTS)和时长(duration)的精度。

PTS(Presentation Time Stamp)

PTS是数据包(或帧)应该被显示给用户的时间点,它是以时间基为单位的。对于视频流,PTS决定了帧的显示顺序。

DTS(Decoding Time Stamp)

DTS是数据包(或帧)应该被解码的时间点,也是以时间基为单位的。对于视频流,DTS决定了帧的解码顺序。

Duration

Duration是数据包(或帧)的时长,同样是以时间基为单位的。它表示从当前数据包(或帧)的开始到下一个数据包(或帧)的开始之间的时间间隔。

C代码示例

下面是一个使用FFmpeg库读取视频文件并打印每帧的PTS、DTS和duration的C代码示例:

#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <input file>\n", argv[0]);
        return 1;
    }

    const char *input_filename = argv[1];
    AVFormatContext *fmt_ctx = NULL;
    int video_stream_index = -1;
    AVPacket pkt;
    AVFrame *frame = NULL;
    AVRational time_base;

    // Initialize libavformat and register all muxers, demuxers and protocols.
    av_register_all();

    // Open input file and read header.
    if (avformat_open_input(&fmt_ctx, input_filename, NULL, NULL) != 0) {
        fprintf(stderr, "Could not open input file '%s'.\n", input_filename);
        return 1;
    }

    // Retrieve stream information.
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "Could not find stream information.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Find the first video stream.
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            time_base = fmt_ctx->streams[i]->time_base;
            break;
        }
    }

    if (video_stream_index == -1) {
        fprintf(stderr, "Could not find a video stream.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Allocate video frame.
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame.\n");
        avformat_close_input(&fmt_ctx);
        return 1;
    }

    // Read frames from the file.
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        if (pkt.stream_index == video_stream_index) {
            // Decode video frame.
            int response = avcodec_send_packet(fmt_ctx->streams[video_stream_index]->codec, &pkt);
            if (response < 0) {
                fprintf(stderr, "Error sending packet for decoding.\n");
                break;
            }

            while (response >= 0) {
                response = avcodec_receive_frame(fmt_ctx->streams[video_stream_index]->codec, frame);
                if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
                    break;
                } else if (response < 0) {
                    fprintf(stderr, "Error receiving frame from decoder.\n");
                    goto end;
                }

                // Print PTS, DTS, and duration.
                printf("Frame PTS: %"PRId64", DTS: %"PRId64", Duration: %"PRId64"\n",
                       av_rescale_q(frame->pts, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE),
                       av_rescale_q(pkt.dts, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE),
                       av_rescale_q(pkt.duration, fmt_ctx->streams[video_stream_index]->time_base, AV_TIME_BASE));
            }
        }

        // Free the packet.
        av_packet_unref(&pkt);
    }

end:
    // Free the frame.
    av_frame_free(&frame);

    // Close the input file.
    avformat_close_input(&fmt_ctx);

    return 0;
}

注意事项

  1. 时间基转换:在打印PTS、DTS和duration时,我使用了av_rescale_q函数将它们从流的时间基转换为AV_TIME_BASE(通常是1/1000000,即微秒)。这是为了更容易理解和比较这些时间值。
  2. 错误处理:代码中的错误处理是简化的,实际使用中可能需要更详细的错误检查和恢复逻辑。
  3. 解码器:这个示例代码假设了视频流有一个可用的解码器,并且解码器能够成功解码帧。在实际应用中,可能需要检查解码器的配置和状态。
  4. 内存管理:FFmpeg使用了自己的内存管理机制,因此请确保在不再需要时释放所有分配的内存和资源。
  5. FFmpeg版本:这个示例代码基于FFmpeg的C API,并且可能需要根据您使用的FFmpeg版本进行调整。特别是,FFmpeg的API在不同版本之间可能会有所变化。
  6. 编译:要编译这个示例代码,您需要链接FFmpeg库。这通常涉及到设置正确的编译器和链接器标志,以及确保FFmpeg的开发文件(头文件和库文件)在您的构建环境中可用。
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0