FFmpeg AVFormatContext codecpar segfaults while it works in C

Hello, I am trying to capture the screen with FFmpeg and as a reference I am using Low-Latency-Live-Streaming/capture_screen.c at master · JingyZhu/Low-Latency-Live-Streaming · GitHub.
But now I got the problem that I AVFormatContext is null while execing it while the correspond C code runs fine, and I can not find the source of the problem.

Zig code:

const std = @import("std");
const ffmpeg = @cImport({
    @cInclude("ffmpeg4.4/libavformat/avformat.h");
    @cInclude("ffmpeg4.4/libavdevice/avdevice.h");
    @cInclude("ffmpeg4.4/libavcodec/avcodec.h");
});

pub fn main() !void {
    var c_error: c_int = 0;

    //  const codec_ctx: ?*ffmpeg.AVCodecContext = null;
    //    const codec: ?*ffmpeg.AVCodec = null;
    var options: ?*ffmpeg.AVDictionary = null;

    var AVFormatContext = ffmpeg.avformat_alloc_context();
    _ = ffmpeg.avformat_network_init();
    ffmpeg.avdevice_register_all();

    c_error = ffmpeg.av_dict_set(&options, "framerate", "30", 0);
    std.debug.print("{any}\n", .{c_error});

    //c_error = ffmpeg.av_dict_set(&options, "preset", "medium", 0);
    //std.debug.print("{any}\n", .{c_error});

    c_error = ffmpeg.av_dict_set(&options, "video_size", "1920*1080", 0);
    std.debug.print("size:{any}\n", .{c_error});

    const input_format = ffmpeg.av_find_input_format("x11grab");
    c_error = ffmpeg.avformat_open_input(&AVFormatContext, ":0.0", input_format, &options);
    std.debug.print("{any}\n", .{c_error});

    c_error = ffmpeg.avformat_find_stream_info(AVFormatContext, &options);
    std.debug.print("info:{any}\n", .{c_error});

    std.debug.print("{any}\n", .{AVFormatContext.*.streams[0].*.codecpar.*});
}

C code:

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
 
int main(int argc, char* argv[])
{
 
	AVFormatContext	*pFormatCtx;
	int				i, videoindex;
	AVCodecContext	*pCodecCtx;
	AVCodec			*pCodec;

	char* video_size = "1920*1080";
	char* framerate = "30";
	
	// Deprecated
	// av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
 
	//Register Device Deprecated
	avdevice_register_all();

	AVDictionary* options = NULL;
	//Set some options
	//grabbing frame rate
	av_dict_set(&options,"framerate", framerate, 0);
	//Make the grabbed area follow the mouse
	//av_dict_set(&options,"follow_mouse","centered",0);
	//Video frame size. The default is to capture the full screen
	av_dict_set(&options, "video_size", video_size, 0);
	const AVInputFormat *ifmt=av_find_input_format("x11grab");
	//Grab at position 10,20

	if(avformat_open_input(&pFormatCtx,":0.0",ifmt,&options)!=0){
		fprintf(stderr, "Couldn't open input stream.\n");
		return -1;
	}
	fprintf(stdout, "test %d\n", pFormatCtx->streams[0]->codecpar->codec_type);

	if(avformat_find_stream_info(pFormatCtx,NULL)<0){
		fprintf(stderr, "Couldn't find stream information.\n");
		return -1;
	}
}

Hello @Michal0x Welcome to ziggit :slight_smile:

Are you getting stack trace on segfault?

2 Likes

I solved it myself; I had used the header files of the wrong version. After removing "ffmpeg4.4/“ it works.

By the way, I have an FFmpeg zig package that is updated to 7.0.1: GitHub - andrewrk/ffmpeg: FFmpeg Zig package

You add it to your project like this:

build.zig.zon

        .ffmpeg = .{
            .url = "https://github.com/andrewrk/ffmpeg/archive/refs/tags/7.0.1-4.tar.gz",
            .hash = "1220b5d387335cb76a0eda4ab3e3c9ed153cbc3d1b5bb63c4899b5dfab3f826f4413",
        },

build.zig

    const ffmpeg_dep = b.dependency("ffmpeg", .{
        .target = target,
        .optimize = dep_optimize,
    });
// ...
    player.addImport("av", ffmpeg_dep.module("av"));

and then use it like this:

pub const av = @import("av");
// ...
    pub fn open(dir: std.fs.Dir, sub_path: []const u8, filename_hint: [*:0]const u8) !*File {
        const file = try std.heap.raw_c_allocator.create(File);
        errdefer std.heap.raw_c_allocator.destroy(file);

        // Call to readPacket inside this function depends on only this field.
        file.fs_file = try dir.openFile(sub_path, .{});
        errdefer file.fs_file.close();

        const ioc = ioc: {
            const buf = try av.malloc(4 * 1024);
            errdefer av.free(buf.ptr);
            break :ioc try av.IOContext.alloc(buf, .read_only, file, readPacket, writePacket, seek);
        };
        errdefer {
            av.free(ioc.buffer);
            ioc.free();
        }

        const fc = try av.FormatContext.open_input(filename_hint, null, null, ioc);
        errdefer fc.close_input();

        try fc.find_stream_info(null);

        // Set all streams to discard other than the main audio stream.
        if (fc.nb_streams > std.math.maxInt(c_int)) return error.TooManyStreams;
        for (0..fc.nb_streams) |i| {
            fc.streams[i].discard = .ALL;
        }

        const audio_stream_index, const decoder = try fc.find_best_stream(.AUDIO, -1, -1);
        const audio_stream = fc.streams[audio_stream_index];
        audio_stream.discard = .DEFAULT;

        const decode_context = try av.CodecContext.alloc(decoder);
        file.decode = decode_context;
        errdefer decode_context.free();

        try decode_context.parameters_to_context(audio_stream.codecpar);
        try decode_context.open(decoder, null);

        if (decode_context.ch_layout.nb_channels == 0) return error.InvalidChannelLayout;

        file.* = .{
            .fs_file = file.fs_file,
            .fc = fc,
            .decode = decode_context,
            .stream_index = audio_stream_index,
        };
        return file;
    }

full working example

I didn’t add API bindings for 100% of everything but happy to merge patches.

6 Likes