FFMPEG 音视频推流

1. 初始化FFmpeg和设备

2. 配置视频和音频编码器

3. 主循环:捕获、编码和推送流

4. 清理资源

//1. 初始化FFmpeg和设备

extern "C" {
#include 
#include 
#include 
#include 
#include 
#include 
}

int main() {
    avdevice_register_all();
    avcodec_register_all();
    av_register_all();
    avformat_network_init();

    const char* out_url = "rtmp://[RTMP服务器地址]/[应用名]/[流名]";
    AVFormatContext* pFormatCtx = avformat_alloc_context();
    AVOutputFormat* fmt = av_guess_format("flv", out_url, NULL);
    pFormatCtx->oformat = fmt;

    // 打开视频设备
    AVFormatContext* video_in_ctx = nullptr;
    AVInputFormat* video_in_fmt = av_find_input_format("v4l2");
    if (avformat_open_input(&video_in_ctx, "/dev/video0", video_in_fmt, NULL) != 0) {
        printf("Couldn't open input video stream.\n");
        return -1;
    }

    // 打开音频设备
    AVFormatContext* audio_in_ctx = nullptr;
    AVInputFormat* audio_in_fmt = av_find_input_format("alsa");
    if (avformat_open_input(&audio_in_ctx, "default", audio_in_fmt, NULL) != 0) {
        printf("Couldn't open input audio stream.\n");
        return -1;
    }
    // ... [接下来的代码]
}

//2. 配置视频和音频编码器
//视频和音频编码器的配置因具体需求而异,这里给出一个基本示例。
    // 查找视频和音频流索引
    int video_stream_index = -1, audio_stream_index = -1;
    for (int i = 0; i < video_in_ctx->nb_streams; i++) {
        if (video_in_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    for (int i = 0; i < audio_in_ctx->nb_streams; i++) {
        if (audio_in_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream_index = i;
            break;
        }
    }

    // 打开视频编码器
    AVCodec* video_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    AVCodecContext* video_codec_ctx = avcodec_alloc_context3(video_codec);
    video_codec_ctx->bit_rate = 400000;
    video_codec_ctx->width = 640;
    video_codec_ctx->height = 480;
    video_codec_ctx->time_base = (AVRational){1, 25};
    video_codec_ctx->framerate = (AVRational){25, 1};
    video_codec_ctx->gop_size = 10;
    video_codec_ctx->max_b_frames = 1;
    video_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    if (avcodec_open2(video_codec_ctx, video_codec, NULL) < 0) {
        printf("Could not open video codec.\n");
        return -1;
    }

    // 打开音频编码器
    AVCodec* audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    AVCodecContext* audio_codec_ctx = avcodec_alloc_context3(audio_codec);
    audio_codec_ctx->bit_rate = 64000;
    audio_codec_ctx->sample_rate = 44100;
    audio_codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
    audio_codec_ctx->channels = 2;
    audio_codec_ctx->sample_fmt = audio_codec->sample_fmts[0];
    audio_codec_ctx->time_base = (AVRational){1, audio_codec_ctx->sample_rate};
    if (avcodec_open2(audio_codec_ctx, audio_codec, NULL) < 0) {
        printf("Could not open audio codec.\n");
        return -1;
    }
//3. 主循环:捕获、编码和推送流
//在主循环中,我们从摄像头和麦克风捕获数据,并将其编码后推送到RTMP服务器。
    // 打开RTMP流
    if (avio_open(&pFormatCtx->pb, out_url, AVIO_FLAG_READ_WRITE) < 0){
        printf("Failed to open output file!\n");
        return -1;
    }

    // 写文件头
    if (avformat_write_header(pFormatCtx, NULL) < 0) {
        printf("Error occurred when opening output file.\n");
        return -1;
    }

    // 初始化转换器
    SwsContext* sws_ctx = sws_getContext(video_codec_ctx->width, video_codec_ctx->height, AV_PIX_FMT_YUV420P,
                                         video_codec_ctx->width, video_codec_ctx->height, AV_PIX_FMT_YUV420P, 
                                         SWS_BILINEAR, NULL, NULL, NULL);
    SwrContext* swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_FLTP, 44100,
                                             AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100, 0, NULL);
    swr_init(swr_ctx);

    // 主循环
    while (1) {
        // 从视频或音频设备读取数据
        // ... [处理视频和音频帧的代码]
    }

    // 清理和写文件尾
    // ... [清理资源的代码]
//主循环:处理音视频帧
//在主循环中,我们需要从视频和音频设备中读取帧,然后对这些帧进行编码,最后将它们发送到RTMP服务器。
//3. 主循环
AVPacket pkt;
while (1) {
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    // 读取视频帧
    if (av_read_frame(video_in_ctx, &pkt) >= 0 && pkt.stream_index == video_stream_index) {
        // 将视频帧发送到编码器
        avcodec_send_packet(video_codec_ctx, &pkt);
        while (avcodec_receive_frame(video_codec_ctx, frame) >= 0) {
            // 这里可以对frame进行处理,比如格式转换
            // 编码视频帧
            avcodec_send_frame(video_codec_ctx, frame);
            while (avcodec_receive_packet(video_codec_ctx, &pkt) >= 0) {
                av_interleaved_write_frame(pFormatCtx, &pkt);
                av_packet_unref(&pkt);
            }
        }
    }

    // 读取音频帧
    if (av_read_frame(audio_in_ctx, &pkt) >= 0 && pkt.stream_index == audio_stream_index) {
        // 将音频帧发送到编码器
        avcodec_send_packet(audio_codec_ctx, &pkt);
        while (avcodec_receive_frame(audio_codec_ctx, frame) >= 0) {
            // 这里可以对frame进行处理,比如格式转换
            // 编码音频帧
            avcodec_send_frame(audio_codec_ctx, frame);
            while (avcodec_receive_packet(audio_codec_ctx, &pkt) >= 0) {
                av_interleaved_write_frame(pFormatCtx, &pkt);
                av_packet_unref(&pkt);
            }
        }
    }

    av_packet_unref(&pkt);

    // 可以在这里添加一个退出循环的条件
}



//4 清理资源
avcodec_close(video_codec_ctx);
avcodec_close(audio_codec_ctx);
avformat_close_input(&video_in_ctx);
avformat_close_input(&audio_in_ctx);
avformat_free_context(pFormatCtx);
sws_freeContext(sws_ctx);
swr_free(&swr_ctx);
avformat_network_deinit();

你可能感兴趣的:(ffmpeg)