最简单的基于FFmpeg的AVDevice例子(读取摄像头)–Libavdevice支持??设备作为输入端/输出端
FFmpeg获取DirectShow设备数据(摄像头,录屏)
①首先要ffmpeg安装到windows:static版本放到某一个目录下并添加到系统环境变量path中
查看版本:ffmpeg-version
②命令:列出设备: ffmpeg -list_devices true -f dshow -i dummy
问题①:无法列出设备:没有安装screen-capture-recorder和virtual-audio-capturer
ffmpeg 录屏 screen capture recorder
问题②:设备名显示为中文乱码
//列表显示设备的名称很重要,输入的时候都是使用“-f dshow -i video="{设备名}"”的方式。
下面是正确显示:
[dshow @0388f5e0] DirectShow video devices
[dshow @0388f5e0] “Integrated Camera”
[dshow @0388f5e0] “screen-capture-recorder”
[dshow @0388f5e0] DirectShow audio devices
[dshow @0388f5e0] “鍐呰楹﹀厠椋?(Conexant20672 SmartAudi”
[dshow @0388f5e0] “virtual-audio-capturer”
//screen-capture-recorder和virtual-audio-capturer需要安装 Integrated Camera例如联想笔记本上的一体化摄像头
2. 获取摄像头数据(保存为本地文件或者发送实时流)
2.1. 编码为H.264,保存为本地文件
下面这条命令,实现了从摄像头读取数据并编码为H.264,最后保存成mycamera.mkv。
ffmpeg -f dshow -i video="Integrated Camera" -vcodec libx264 mycamera.mkv
最简单的基于Libavdevice的摄像头数据读取一帧YUV数据,并保存成output.yuv文件:
①普通的文件输入:
AVFormatContext *pFormatCtx = avformat_alloc_context();
avformat_open_input(&pFormatCtx, “test.h265”,NULL,NULL);
②利用设备输入:
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVInputFormat *ifmt=av_find_input_format(“vfwcap”);//设备名(如vfwcap video4linux2)→确定fmt
avformat_open_input(&pFormatCtx, 0, ifmt,NULL);//0表示在URL中指定打开第0个设备(在我自己计算机上即是摄像头设备)。
在Windows平台上除了使用vfw设备作为输入设备之外,还可以使用DirectShow作为输入设备:
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVInputFormat *ifmt=av_find_input_format(“dshow”);
avformat_open_input(&pFormatCtx,“video=Integrated Camera”,ifmt,NULL) ;
avformat_open_input也可以不输入指定的AVInputFormat,而是缺省,通过第二个传参url自行判断
YUV420P(planar格式)在ffmpeg中存储是在struct AVFrame的data[]数组中
data[0]——-Y分量
data[1]——-U分量
data[2]——-V分量 ->故需要对data[012]依次存放才能是YUVp
linesize[]数组中保存的是对应通道的数据宽度
linesize[0]——-Y分量的宽度
linesize[1]——-U分量的宽度
linesize[2]——-V分量的宽度
注意: linesize[0]的值并不一定等于图片的宽度。我将一张1366*768的图片编码后,linesize[0]的值为1376,大概是为了内存对齐的缘故,我就没深究了。
读摄像头->pkt解码转换->目标frame->pFrameYUV->data[012]依次存放到目标文件(应该不算标准yuv文件,不可只有像素?)
#include "libavdevice/avdevice.h"
int main(int argc, char* argv[])
{
//1、prepare
AVFormatContext *pFormatCtx;
int i, videoindex;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
av_register_all();
avformat_network_init();
avdevice_register_all();
pFormatCtx = avformat_alloc_context();
AVInputFormat *ifmt=av_find_input_format("video4linux2");
if(avformat_open_input(&pFormatCtx,"/dev/video0",ifmt,NULL)!=0) {
printf("Couldn't open input stream./dev/video0\n"); return -1; }
if(avformat_find_stream_info(pFormatCtx,NULL)<0) {
printf("Couldn't find stream information.\n"); return -1; }
videoindex=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoindex=i; break; }
if(videoindex==-1) {
printf("Couldn't find a video stream.\n"); return -1; }
pCodecCtx=pFormatCtx->streams[videoindex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id); //if(pCodec==NULL) { printf("Codec not found.\n"); return -1; }
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0) {
printf("Could not open codec.\n"); return -1; }
AVFrame *pFrame,*pFrameYUV;
pFrame=av_frame_alloc();
pFrameYUV=av_frame_alloc();
unsigned char *out_buffer=(unsigned char *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //关联pFrameYUV和out_buffer
//根据获取摄像头的宽高和指定的像素格式420,分配空间
//printf("camera width=%d height=%d \n",pCodecCtx->width, pCodecCtx->height);
int ret, got_picture;
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
#if OUTPUT_YUV420P
FILE *fp_yuv=fopen("output.yuv","wb+"); //打开目标输出文件
#endif
struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext( pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
//2、读取--解码--转换-保存【YUV3个分量依次存放pFrameYUV->data[012]】--保存成yuv图片
if(av_read_frame(pFormatCtx, packet)>=0){
if(packet->stream_index==videoindex){
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if(ret < 0){
printf("Decode Error.\n"); return -1; }
if(got_picture){
sws_scale( img_convert_ctx,
(const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
#if OUTPUT_YUV420P
int y_size=pCodecCtx->width*pCodecCtx->height;
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
#endif
}
}
av_free_packet(packet);
}
//3、end
sws_freeContext(img_convert_ctx);
#if OUTPUT_YUV420P
fclose(fp_yuv);
#endif
av_free(out_buffer);
av_free(pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
下面则较为严谨的版本:
//FFMPEG抓取摄像头数据保存为一张图片11yuv420.yuv(转换成)
//编译指令: gcc usb_baocun_yuv.c -o video-frame.out -I/root/ffmpeg/include/ -L/root/ffmpeg/lib -lavcodec -lavformat -lavdevice -lavutil -lswscale -lSDL
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
char* input_name= "video4linux2"; //没有用到啊
char* file_name = "/dev/video0"; //当做文件读取
char* out_file = "11yuv420.yuv";//使用专用软件打开时如果打开时的格式与实际不符合会出错(按照错误的方式解读)
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
typedef struct StreamContext {
//准备好?个码流解码/编码所需的AVCodecContext
AVCodecContext *dec_ctx;
AVCodecContext *enc_ctx;
} StreamContext;
static StreamContext *stream_ctx = NULL;
const char* jpeg_file = "cuc_view_encode.jpg";
unsigned int g_stream_index = -1; //视频流的序号
void release_frame(AVFrame *frame)
{
if(frame){
av_frame_unref(frame);
av_frame_free(&frame);
}
}
void release_packet(AVPacket *packet)
{
if(packet){
av_packet_unref(packet);
av_packet_free(&packet);
}
}
//打开输入文件并获取其中各个码流的stream_ctx[?]->dec_ctx
int open_input_file()
{
int i = 0;
int ret = -1;
int videoindex = -1;
ifmt_ctx = NULL;//里面有?个码流--对应stream_ctx[?]
if((ret = avformat_open_input (&ifmt_ctx, file_name, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");return ret;}
//av_dump_format(ifmt_ctx,0,file_name,0);
if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");return;}
stream_ctx = av_mallocz_array(ifmt_ctx->nb_streams, sizeof(*stream_ctx))