QT打开摄像头录制,保存为yuv格式文件并打开

 打开摄像头录制,保存为yuv格式文件


#include <stdio.h>

#define __STDC_CONSTANT_MACROS

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
#include "libavdevice/avdevice.h"
#include "SDL.h"
#undef main
};

int main(int argc, char* argv[])
{
    //参数:1双指针定义一颗星*,2要打开流的路径(文件名)
    //AVFormatContext保存视频(视频流)相关信息的结构体
    AVFormatContext	*pFormatCtx;

    int				i, videoindex;
    AVCodecContext	*pCodecCtx;//AVCodecContext 结构表示程序运行的当前 Codec 使用的上下文,着重于所有 Codec 共有的属性(并且是在程
    //序运行时才能确定其值)和关联其他结构的字段
    AVCodec			*pCodec;//AVCodec是存储编解码器信息的结构体, 每种视频(音频)编解码器对应一个该结构体
    AVFrame	*pFrame,*pFrameYUV;//在视频编解码的时候,存储视频和音频的原始数据,
    unsigned char *out_buffer;
    AVPacket *packet;//保存了解复用之后,解码之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加信息,如显示时间戳(pts)、解码时间戳(dts)、数据时长,所在媒体流的索引等。
    int y_size;
    int ret, got_picture;
    struct SwsContext *img_convert_ctx;//主要用于处理图片像素数据, 可以完成图片像素格式的转换, 图片的拉伸等工作



    char filepath[]="bigbuckbunny_480x272.h265";
    //SDL---------------------------
    int screen_w=0,screen_h=0;
    SDL_Window *screen;
    SDL_Renderer* sdlRenderer;
    SDL_Texture* sdlTexture;
    SDL_Rect sdlRect;

    FILE *fp_yuv;

    av_register_all(); // 过时
    avdevice_register_all();
    //avformat_network_init();//需要用到网络功能
    pFormatCtx = avformat_alloc_context();

    AVInputFormat *ifmt=av_find_input_format("dshow");
    /*2、打开视频文件*/
    if( avformat_open_input(&pFormatCtx, "video=Integrated Webcam" ,ifmt,NULL)!=0){//打开多媒体数据并且获得一些相关的信
        printf("Couldn't open input stream.\n");
        return -1;
    }
    /*3、打开成功之后相关的结构体信息放在了AVFormatContext里面,接下来获取视频文件信息*/
    //3.1先看有没有视频流信息(avformat_find_stream_info),进行判断的原因是有可能打开普通文件
    if(avformat_find_stream_info(pFormatCtx,NULL)<0){//读取媒体文件的音视频包去获取流信息
        printf("Couldn't find stream information.\n");
        return -1;
    }
    //AVFormatContext(含有解码器的id,去streams的流数组里面找视频流)->AVStream->AVCodecContext
    //->codec(有解码器的AVCodec)->AVCodec(含有编解码器的id、类型)
    //AVCodecContext 保存视频音频编解码相关的信息
    videoindex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(videoindex==-1){
        printf("Didn't find a video stream.\n");
        return -1;
    }

    //对视频的编码只有编码器是不够的还要有宽高格式等
    //3.3编码器对应的上下文对象结构体,存储编码器以及宽高格式等
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;

    /*4、有视频流,则查找对应视频流的解码器*/
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id); //根据id查找解码器
    if(pCodec==NULL){
        printf("Codec not found.\n");
        return -1;
    }

    /*5、找到解码器后打开解码器*/
    //参数:1.初始化的上下文对象 2.打开的解码器 3.类似目录的东西(没有)
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
        //将编码器上下文和编码器进行关联
        //该函数用于 初始化 一个视音频编解码器的AVCodecContext。
        printf("Could not open codec.\n");
        return -1;
    }

    /*6、获取到的每一帧码流(视频流)数据写到文件中*/
    /*6.1写到文件用FILE结构体*/


    FILE *fp = fopen("saveH264.h264","wb+");
    FILE *fpYUV = fopen("saveYUV.yuv","wb+");


    //码流数据是存到buffer里面,也需要我们动态开空间(AVBufferRef *buf;)
    //开空间不知道一帧的码流数据是多少?其实编解码器告诉了宽高,以此可以计算出给码流数据开多大空间
    pFrame=av_frame_alloc();  //分配空间
    pFrameYUV=av_frame_alloc();

    int bufSize=av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  pCodecCtx->width, pCodecCtx->height,1);

    //开的空间用来保存像素数据的大小
    out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  pCodecCtx->width, pCodecCtx->height,1));//通过指定像素格式、图像宽、图像高来计算所需的内存大小

    //像素数据填充到AVFrame的pFrameYUV里
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
        AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);//瓜分上一步分配到的buffer

    //pkt这时没有指向,要我们给他分配内存空间,希望把读出来的数据放到这块内存去
    packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    //Output Info-----------------------------
    printf("--------------- File Information ----------------\n");
    //av_dump_format(pFormatCtx,0,filepath,0);
    printf("-------------------------------------------------\n");

    //因为解码之后要伸展,所以先进行转换规则的设置
    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);


    while(av_read_frame(pFormatCtx, packet) == 0)
    {
        if(packet->stream_index == videoindex)
        {
           //是视频流则写到文件中
           fwrite(packet->data,packet->size,1,fp);//每次写一个结构体
           //读到一帧是视频流就进行解码的动作
           /*7、解码——得到YUV 保存在AVFrame结构体里*/
           //参数:1编解码器上下文对象的结构体 2存放解码后的像素数据(AVFrame类型)
           //3判断有没有数据可以进行解码:指针类型的变量 4对谁进行解码:一帧的码流数据


           avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
           if(got_picture)
           {
               //把解码得到的损坏的像素数据剔除,存到pictureYUV中
               //data是数组,把Y U V 三份数据分开存储
                  sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                  pFrameYUV->data, pFrameYUV->linesize);

                  fwrite(pFrameYUV->data[0],bufSize,1,fpYUV);
                  fwrite(pFrameYUV->data[1],bufSize/4,1,fpYUV);
                  fwrite(pFrameYUV->data[2],bufSize/4,1,fpYUV);
           }
         }
            av_packet_unref(packet);
     }

    fclose(fp);
    fclose(fpYUV);

    return 0;
}

打开yuv格式视频


#include <stdio.h>

#define __STDC_CONSTANT_MACROS

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
#include "libavdevice/avdevice.h"
#include "SDL.h"
#undef main
};

int main(int argc, char* argv[])
{
    //参数:1双指针定义一颗星*,2要打开流的路径(文件名)
    //AVFormatContext保存视频(视频流)相关信息的结构体
    AVFormatContext	*pFormatCtx;

    int				i, videoindex;
    AVCodecContext	*pCodecCtx;//AVCodecContext 结构表示程序运行的当前 Codec 使用的上下文,着重于所有 Codec 共有的属性(并且是在程
    //序运行时才能确定其值)和关联其他结构的字段
    AVCodec			*pCodec;//AVCodec是存储编解码器信息的结构体, 每种视频(音频)编解码器对应一个该结构体
    AVFrame	*pFrame,*pFrameYUV;//在视频编解码的时候,存储视频和音频的原始数据,
    unsigned char *out_buffer;
    AVPacket *packet;//保存了解复用之后,解码之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加信息,如显示时间戳(pts)、解码时间戳(dts)、数据时长,所在媒体流的索引等。
    int y_size;
    int ret, got_picture;
    struct SwsContext *img_convert_ctx;//主要用于处理图片像素数据, 可以完成图片像素格式的转换, 图片的拉伸等工作



    char filepath[]="bigbuckbunny_480x272.h265";
    char fp[]="saveH264.h264";
    //SDL---------------------------
    int screen_w=0,screen_h=0;
    SDL_Window *screen;
    SDL_Renderer* sdlRenderer;
    SDL_Texture* sdlTexture;
    SDL_Rect sdlRect;

    FILE *fp_yuv;

    av_register_all(); // 过时
    avdevice_register_all();
    //avformat_network_init();//需要用到网络功能
    pFormatCtx = avformat_alloc_context();

    AVInputFormat *ifmt=av_find_input_format("dshow");
    /*2、打开视频文件*/
    if( avformat_open_input(&pFormatCtx, fp ,NULL,NULL)!=0){//打开多媒体数据并且获得一些相关的信
        printf("Couldn't open input stream.\n");
        return -1;
    }
    /*3、打开成功之后相关的结构体信息放在了AVFormatContext里面,接下来获取视频文件信息*/
    //3.1先看有没有视频流信息(avformat_find_stream_info),进行判断的原因是有可能打开普通文件
    if(avformat_find_stream_info(pFormatCtx,NULL)<0){//读取媒体文件的音视频包去获取流信息
        printf("Couldn't find stream information.\n");
        return -1;
    }
    //AVFormatContext(含有解码器的id,去streams的流数组里面找视频流)->AVStream->AVCodecContext
    //->codec(有解码器的AVCodec)->AVCodec(含有编解码器的id、类型)
    //AVCodecContext 保存视频音频编解码相关的信息
    videoindex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
//    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("Didn't find a video stream.\n");
        return -1;
    }

    //对视频的编码只有编码器是不够的还要有宽高格式等
    //3.3编码器对应的上下文对象结构体,存储编码器以及宽高格式等
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;

    /*4、有视频流,则查找对应视频流的解码器*/
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id); //根据id查找解码器
    if(pCodec==NULL){
        printf("Codec not found.\n");
        return -1;
    }

    /*5、找到解码器后打开解码器*/
    //参数:1.初始化的上下文对象 2.打开的解码器 3.类似目录的东西(没有)
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
        //将编码器上下文和编码器进行关联
        //该函数用于 初始化 一个视音频编解码器的AVCodecContext。
        printf("Could not open codec.\n");
        return -1;
    }

    /*6、获取到的每一帧码流(视频流)数据写到文件中*/
    /*6.1写到文件用FILE结构体*/


//    FILE *fp = fopen("saveH264.h264","wb+");
//        FILE *fpYUV = fopen("saveYUV.yuv","wb+");


    //码流数据是存到buffer里面,也需要我们动态开空间(AVBufferRef *buf;)
    //开空间不知道一帧的码流数据是多少?其实编解码器告诉了宽高,以此可以计算出给码流数据开多大空间
    pFrame=av_frame_alloc();  //分配空间
    pFrameYUV=av_frame_alloc();

    //开的空间用来保存像素数据的大小
    out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  pCodecCtx->width, pCodecCtx->height,1));//通过指定像素格式、图像宽、图像高来计算所需的内存大小

    //像素数据填充到AVFrame的pFrameYUV里
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
        AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);//瓜分上一步分配到的buffer

    //pkt这时没有指向,要我们给他分配内存空间,希望把读出来的数据放到这块内存去
    packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    //Output Info-----------------------------
    printf("--------------- File Information ----------------\n");
    //av_dump_format(pFormatCtx,0,filepath,0);
    printf("-------------------------------------------------\n");

    //因为解码之后要伸展,所以先进行转换规则的设置
    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);
    //转换源的格式

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        printf( "Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }

    screen_w = pCodecCtx->width;
    screen_h = pCodecCtx->height;
    //SDL 2.0 Support for multiple windows
    screen = SDL_CreateWindow("carema", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        screen_w, screen_h,
        SDL_WINDOW_OPENGL);

    if(!screen) {
        printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
        return -1;
    }

    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    //IYUV: Y + U + V  (3 planes)
    //YV12: Y + V + U  (3 planes)
    sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);

    sdlRect.x=0;
    sdlRect.y=0;
    sdlRect.w=screen_w;
    sdlRect.h=screen_h;

    //SDL End----------------------
//    while(av_read_frame(pFormatCtx, packet)>=0){//读取码流中的音频若干帧或者视频一帧
//        if(packet->stream_index==videoindex){
//            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame
//            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);
//                //SDL---------------------------
//#if 0
//                SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );
//#else
//                SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
//                pFrameYUV->data[0], pFrameYUV->linesize[0],
//                pFrameYUV->data[1], pFrameYUV->linesize[1],
//                pFrameYUV->data[2], pFrameYUV->linesize[2]);
//#endif

//                SDL_RenderClear( sdlRenderer );
//                SDL_RenderCopy( sdlRenderer, sdlTexture,  NULL, &sdlRect);
//                SDL_RenderPresent( sdlRenderer );
//                //SDL End-----------------------
//                //Delay 40ms
//                SDL_Delay(40);

//            }
//        }
          //每次都存在同一块内存空间里,要清空上一次的操作
//        av_packet_unref(packet);
//    }

        while(av_read_frame(pFormatCtx, packet)>=0 && packet->stream_index==videoindex)
        {
            avcodec_send_packet(pCodecCtx, packet);
            if(avcodec_receive_frame(pCodecCtx, pFrame) == 0)
            {
                sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                                    pFrameYUV->data, pFrameYUV->linesize);
                SDL_UpdateYUVTexture(sdlTexture, NULL,
                     pFrameYUV->data[0], pFrameYUV->linesize[0],
                     pFrameYUV->data[1], pFrameYUV->linesize[1],
                     pFrameYUV->data[2], pFrameYUV->linesize[2]);

                sdlRect.x = 0;
                sdlRect.y = 0;
                sdlRect.w = pCodecCtx->width;
                sdlRect.h = pCodecCtx->height;

                SDL_RenderClear( sdlRenderer );
                SDL_RenderCopy( sdlRenderer, sdlTexture,  NULL, &sdlRect);
                SDL_RenderPresent( sdlRenderer );
                SDL_Delay(50);
            }
             av_packet_unref(packet);
             av_free_packet(packet);


        }



 //   flush decoder
 //   FIX: Flush Frames remained in Codec
    while (1) {
        ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
        if (ret < 0)
            break;
        if (!got_picture)
            break;
        sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
            pFrameYUV->data, pFrameYUV->linesize);

        //SDL---------------------------
        SDL_UpdateTexture( sdlTexture, &sdlRect, pFrameYUV->data[0], pFrameYUV->linesize[0] );
        SDL_RenderClear( sdlRenderer );
        SDL_RenderCopy( sdlRenderer, sdlTexture,  NULL, &sdlRect);
        SDL_RenderPresent( sdlRenderer );
        //SDL End-----------------------
        //Delay 40ms
        SDL_Delay(40);

    }

    sws_freeContext(img_convert_ctx);

    SDL_Quit();

    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用OpenCV来调用摄像头并获取视频流,然后将视频流保存YUV格式的视频。下面是一个示例代码: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 打开摄像头 VideoCapture cap(0); if (!cap.isOpened()) { cerr << "Failed to open camera!" << endl; return -1; } // 获取摄像头参数 int width = cap.get(CAP_PROP_FRAME_WIDTH); int height = cap.get(CAP_PROP_FRAME_HEIGHT); int fps = cap.get(CAP_PROP_FPS); // 创建输出YUV文件 VideoWriter writer("output.yuv", cv::VideoWriter::fourcc('I', '4', '2', '0'), fps, Size(width, height), false); // 逐帧读取并保存视频流 Mat frame; while (cap.read(frame)) { cv::cvtColor(frame, frame, cv::COLOR_BGR2YUV_I420); writer.write(frame); imshow("frame", frame); if (waitKey(1) >= 0) break; } // 释放资源 cap.release(); writer.release(); return 0; } ``` 在上面的代码中,我们使用cv::VideoCapture类来打开摄像头并获取视频流。我们使用cv::VideoCapture::get函数来获取摄像头的参数,包括宽度、高度和帧率。然后,我们使用cv::VideoWriter类来创建输出YUV文件,并使用cv::VideoWriter::fourcc函数来指定输出文件的编码格式为I420。 在读取每一帧视频流时,我们使用cv::cvtColor函数将BGR格式的帧转换为YUV格式,并使用cv::VideoWriter::write函数将每一帧数据写入输出YUV文件。最后,我们使用cv::VideoCapture::release和cv::VideoWriter::release函数来释放资源。 注意,在保存YUV格式的视频时,每一帧数据的大小为width * height * 3 / 2,其中Y占据width * height字节,U和V各占据width * height / 4字节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值