linux 摄像头v4l2 推流 rtmp 服务器 c语言完整代码
时间: 2023-09-23 16:09:23 浏览: 738
以下是一个使用C语言实现在Linux上通过v4l2摄像头推流到RTMP服务器的完整代码示例:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libavformat/avformat.h>
#define WIDTH 640
#define HEIGHT 480
#define FRAME_RATE 30
#define BIT_RATE 400000
int main(int argc, char* argv[]) {
int fd;
struct v4l2_capability cap;
struct v4l2_format format;
struct v4l2_requestbuffers reqbuf;
struct v4l2_buffer buf;
void* buffer;
AVFormatContext* avformat_ctx = NULL;
AVCodecContext* avcodec_ctx = NULL;
AVStream* stream = NULL;
AVCodec* codec = NULL;
AVPacket packet;
int frame_count = 0;
// 打开摄像头设备
fd = open("/dev/video0", O_RDWR);
if (fd < 0) {
perror("Failed to open device");
return -1;
}
// 查询摄像头设备能力
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
perror("Failed to query device capabilities");
close(fd);
return -1;
}
// 设置视频格式
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = WIDTH;
format.fmt.pix.height = HEIGHT;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &format) < 0) {
perror("Failed to set video format");
close(fd);
return -1;
}
// 分配并映射内存用于存储视频帧数据
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 1;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
perror("Failed to request buffers");
close(fd);
return -1;
}
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
perror("Failed to query buffer");
close(fd);
return -1;
}
buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffer == MAP_FAILED) {
perror("Failed to map buffer");
close(fd);
return -1;
}
// 初始化FFmpeg
av_register_all();
avformat_network_init();
// 创建AVFormatContext
avformat_ctx = avformat_alloc_context();
if (!avformat_ctx) {
fprintf(stderr, "Failed to allocate AVFormatContext\n");
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 配置输出格式
avformat_ctx->oformat = av_guess_format("flv", NULL, NULL);
if (!avformat_ctx->oformat) {
fprintf(stderr, "Failed to guess output format\n");
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 打开输出URL
if (avio_open2(&avformat_ctx->pb, "rtmp://your-rtmp-server-url", AVIO_FLAG_WRITE, NULL, NULL) < 0) {
fprintf(stderr, "Failed to open output URL\n");
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 创建视频流
stream = avformat_new_stream(avformat_ctx, codec);
if (!stream) {
fprintf(stderr, "Failed to allocate stream\n");
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
avcodec_ctx = stream->codec;
avcodec_ctx->codec_id = avformat_ctx->oformat->video_codec;
avcodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
avcodec_ctx->width = WIDTH;
avcodec_ctx->height = HEIGHT;
avcodec_ctx->bit_rate = BIT_RATE;
avcodec_ctx->time_base.num = 1;
avcodec_ctx->time_base.den = FRAME_RATE;
avcodec_ctx->gop_size = 10;
avcodec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
// 初始化视频编码器
if (avcodec_open2(avcodec_ctx, codec, NULL) < 0) {
fprintf(stderr, "Failed to open video encoder\n");
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 写入文件头
if (avformat_write_header(avformat_ctx, NULL) < 0) {
fprintf(stderr, "Failed to write header\n");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 开始视频采集
if (ioctl(fd, VIDIOC_STREAMON, &buf.type) < 0) {
perror("Failed to start capture");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
while (frame_count < 100) { // 采集100帧
// 将视频帧数据读取到缓冲区
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;
if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
perror("Failed to enqueue buffer");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
perror("Failed to dequeue buffer");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 将YUYV格式的视频帧转换为YUV420P格式
uint8_t* yuyv = (uint8_t*)buffer;
uint8_t* yuv420p = (uint8_t*)av_malloc(WIDTH * HEIGHT * 3 / 2);
for (int i = 0; i < WIDTH * HEIGHT; i++) {
int y = yuyv[i * 2];
int u = yuyv[i * 2 + 1];
int v = yuyv[i * 2 + 3];
int r = y + (int)1.402 * (v - 128);
int g = y - (int)0.344136 * (u - 128) - (int)0.714136 * (v - 128);
int b = y + (int)1.772 * (u - 128);
yuv420p[i] = r > 255 ? 255 : (r < 0 ? 0 : r);
yuv420p[i + WIDTH * HEIGHT] = g > 255 ? 255 : (g < 0 ? 0 : g);
yuv420p[i + WIDTH * HEIGHT + WIDTH * HEIGHT / 4] = b > 255 ? 255 : (b < 0 ? 0 : b);
}
// 编码并写入视频帧
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
AVFrame* frame = av_frame_alloc();
frame->format = avcodec_ctx->pix_fmt;
frame->width = avcodec_ctx->width;
frame->height = avcodec_ctx->height;
av_frame_get_buffer(frame, 0);
av_image_fill_arrays(frame->data, frame->linesize, yuv420p,
avcodec_ctx->pix_fmt, avcodec_ctx->width, avcodec_ctx->height, 1);
if (avcodec_encode_video2(avcodec_ctx, &packet, frame, NULL) < 0) {
fprintf(stderr, "Failed to encode video frame\n");
av_frame_free(&frame);
av_free(yuv420p);
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
av_frame_free(&frame);
av_free(yuv420p);
packet.stream_index = stream->index;
packet.pts = packet.dts = frame_count * (avcodec_ctx->time_base.den) / ((avcodec_ctx->time_base.num) * FRAME_RATE);
packet.duration = (avcodec_ctx->time_base.den) / ((avcodec_ctx->time_base.num) * FRAME_RATE);
if (av_interleaved_write_frame(avformat_ctx, &packet) < 0) {
fprintf(stderr, "Failed to write video frame\n");
av_packet_unref(&packet);
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
av_packet_unref(&packet);
frame_count++;
}
// 结束视频采集
if (ioctl(fd, VIDIOC_STREAMOFF, &buf.type) < 0) {
perror("Failed to stop capture");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 写入文件尾
if (av_write_trailer(avformat_ctx) < 0) {
fprintf(stderr, "Failed to write trailer\n");
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
munmap(buffer, buf.length);
close(fd);
return -1;
}
// 释放资源
munmap(buffer, buf.length);
close(fd);
avcodec_close(avcodec_ctx);
avio_close(avformat_ctx->pb);
avformat_free_context(avformat_ctx);
return 0;
}
```
请注意,上述代码只是一个示例,并且可能需要根据您的需求进行适当的修改和调整。您需要将`"rtmp://your-rtmp-server-url"`替换为实际的RTMP服务器URL。此外,还需要确保已经安装了libavformat和libavcodec库,并将编译命令中添加相应的链接选项。
编译和运行此代码可能需要一些额外的设置和依赖项,因此请根据您的环境和需求进行适当的调整。
阅读全文