How to Read Video Packets with FFmpeg libavformat
This article provides a step-by-step guide on how to use the FFmpeg
libavformat C API to open a video file, retrieve stream
information, and read individual packet headers. You will learn the
essential functions required to initialize the format context, locate
the video stream, and loop through packets to extract metadata such as
presentation timestamps (PTS), decoding timestamps (DTS), packet size,
and stream indices.
Step 1: Initialize Context and Open the Video File
To read a media file, you must first allocate an
AVFormatContext and open the file using
avformat_open_input. This function reads the file header
and populates the context with information about the container
format.
#include <libavformat/avformat.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <input_file>\n", argv[0]);
return -1;
}
const char *filename = argv[1];
AVFormatContext *format_ctx = NULL;
// Open the input file and read the header
if (avformat_open_input(&format_ctx, filename, NULL, NULL) < 0) {
fprintf(stderr, "Could not open source file %s\n", filename);
return -1;
}Step 2: Retrieve Stream Information
Opening the file only reads the header. To access detailed stream
information (such as codecs, frame rates, and durations), you need to
call avformat_find_stream_info.
// Retrieve stream information
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
avformat_close_input(&format_ctx);
return -1;
}
// Dump information about the file onto standard error
av_dump_format(format_ctx, 0, filename, 0);Step 3: Find the Video Stream Index
A media file can contain multiple streams (video, audio, subtitles). You must iterate through the streams to find the one containing video data.
int video_stream_index = -1;
for (unsigned int i = 0; i < format_ctx->nb_streams; i++) {
if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_index = i;
break;
}
}
if (video_stream_index == -1) {
fprintf(stderr, "Could not find a video stream\n");
avformat_close_input(&format_ctx);
return -1;
}Step 4: Allocate the Packet and Read Packet Headers
FFmpeg uses the AVPacket structure to store compressed
data. You allocate a packet using av_packet_alloc() and
read data from the stream using a loop with
av_read_frame(). Despite its name,
av_read_frame reads a packet (compressed data), not a
decoded raw frame.
AVPacket *packet = av_packet_alloc();
if (!packet) {
fprintf(stderr, "Could not allocate packet\n");
avformat_close_input(&format_ctx);
return -1;
}
printf("Reading packets:\n");
// Read packets from the context
while (av_read_frame(format_ctx, packet) >= 0) {
// Check if the packet belongs to the video stream
if (packet->stream_index == video_stream_index) {
printf("Video Packet: PTS=%lld, DTS=%lld, Size=%d, Keyframe=%s\n",
packet->pts,
packet->dts,
packet->size,
(packet->flags & AV_PKT_FLAG_KEY) ? "Yes" : "No");
}
// Wipe the packet buffer to prepare it for the next iteration
av_packet_unref(packet);
}Step 5: Clean Up Resources
Once processing is complete, free the allocated packet memory and close the input format context to prevent memory leaks.
av_packet_free(&packet);
avformat_close_input(&format_ctx);
return 0;
}