FFmpeg的视频解码详解

FFmpeg的视频解码详解,第1张

第一步:组册组件

av_register_all()

例如:编码器、解码器等等…

第二步:打开封装格式->打开文件

例如:mp4、mov、wmv文件等等

avformat_open_input();

第三步:查找视频

如果是视频解码,那么查找视频流,如果是音频解码,那么就查找音频流

avformat_find_stream_info();

第四步:查找视频解码器

1、查找视频流索引位置

2、根据视频流索引,获取解码器上下文

3、根据解码器上下文,获得解码器ID,然后查找解码器

第五步:打开解码器

avcodec_open2();

第六步:读取视频压缩数据->循环读取

没读取一帧数据,立马解码一帧数据

第七步:视频解码->播放视频->得到视频像素数据

第八步:关闭解码器->解码完成

//第一步:组册组件

    av_register_all();

    //第二步:打开封装格式->打开文件

    //参数一:封装格式上下文

    //作用:保存整个视频信息(解码器、编码器等等)

    //信息:码率、帧率等

    AVFormatContext avformat_context = avformat_alloc_context();

    //参数二:视频路径

    const char url = [jinFilePath UTF8String]

    //参数三:指定输入的格式

    //参数四:设置默认参数

    int avformat_open_input_result = avformat_open_input(&avformat_context, url,NULL, NULL);

    if (avformat_open_input_result !=0){

        NSLog("打开文件失败");

        //不同的平台替换不同平台log日志

        return;

    }

    //第三步:查找视频流->拿到视频信息

    //参数一:封装格式上下文

    //参数二:指定默认配置

    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context,NULL);

    if (avformat_find_stream_info_result <0){

        NSLog(" 查找失败");

        return;

    }

    //第四步:查找视频解码器

    //1、查找视频流索引位置

    int av_stream_index = -1;

    for (int i =0; i < avformat_context->nb_streams; ++i) {

        //判断流类型:视频流、音频流、字母流等等

        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){

            av_stream_index = i;

            break;

        }

    }

    //2、根据视频流索引,获取解码器上下文

    AVCodecContext avcodec_context = avformat_context->streams[av_stream_index]->codec;

    //3、根据解码器上下文,获得解码器ID,然后查找解码器

    AVCodec avcodec = avcodec_find_decoder(avcodec_context->codec_id);

    //第五步:打开解码器

    int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec,NULL);

    if (avcodec_open2_result !=0){

        NSLog("打开解码器失败");

        return;

    }

    //第六步:读取视频压缩数据->循环读取

    //1、分析av_read_frame参数

    //参数一:封装格式上下文

    //参数二:一帧压缩数据 = 一张

    //av_read_frame()

    //结构体大小计算:字节对齐原则

    AVPacket packet = (AVPacket)av_malloc(sizeof(AVPacket));

    //32 解码一帧视频压缩数据->进行解码(作用:用于解码 *** 作)

    //开辟一块内存空间

    AVFrame avframe_in = av_frame_alloc();

    int decode_result =0;

    //4、注意:在这里我们不能够保证解码出来的一帧视频像素数据格式是yuv格式

    //参数一:源文件->原始视频像素数据格式宽

    //参数二:源文件->原始视频像素数据格式高

    //参数三:源文件->原始视频像素数据格式类型

    //参数四:目标文件->目标视频像素数据格式宽

    //参数五:目标文件->目标视频像素数据格式高

    //参数六:目标文件->目标视频像素数据格式类型

    SwsContext swscontext = sws_getContext(avcodec_context->width,

                   avcodec_context->height,

                   avcodec_context->pix_fmt,

                   avcodec_context->width,

                   avcodec_context->height,

                   AV_PIX_FMT_YUV420P,

                   SWS_BICUBIC,

                   NULL,

                   NULL,

                   NULL);

    //创建一个yuv420视频像素数据格式缓冲区(一帧数据)

    AVFrame avframe_yuv420p = av_frame_alloc();

    //给缓冲区设置类型->yuv420类型

    //得到YUV420P缓冲区大小

    //参数一:视频像素数据格式类型->YUV420P格式

    //参数二:一帧视频像素数据宽 = 视频宽

    //参数三:一帧视频像素数据高 = 视频高

    //参数四:字节对齐方式->默认是1

    int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,

                             avcodec_context->width,

                             avcodec_context->height,

                             1);

    //开辟一块内存空间

    uint8_t out_buffer = (uint8_t )av_malloc(buffer_size);

    //向avframe_yuv420p->填充数据

    //参数一:目标->填充数据(avframe_yuv420p)

    //参数二:目标->每一行大小

    //参数三:原始数据

    //参数四:目标->格式类型

    //参数五:宽

    //参数六:高

    //参数七:字节对齐方式

    av_image_fill_arrays(avframe_yuv420p->data,

                         avframe_yuv420p->linesize,

                         out_buffer,

                         AV_PIX_FMT_YUV420P,

                         avcodec_context->width,

                         avcodec_context->height,

                         1);

    int y_size, u_size, v_size;

    //52 将yuv420p数据写入yuv文件中

    //打开写入文件

    const char outfile = [joutFilePath UTF8String];

    FILE file_yuv420p = fopen(outfile,"wb+");

    if (file_yuv420p ==NULL){

       NSLog("输出文件打开失败");

        return;

    }

    int current_index =0;

    while (av_read_frame(avformat_context, packet) >=0){

        //>=:读取到了

        //<0:读取错误或者读取完毕

        //2、是否是我们的视频流

        if (packet->stream_index == av_stream_index){

            //第七步:解码

            //学习一下C基础,结构体

            //3、解码一帧压缩数据->得到视频像素数据->yuv格式

            //采用新的API

            //31 发送一帧视频压缩数据

            avcodec_send_packet(avcodec_context, packet);

            //32 解码一帧视频压缩数据->进行解码(作用:用于解码 *** 作)

            decode_result = avcodec_receive_frame(avcodec_context, avframe_in);

            if (decode_result ==0){

                //解码成功

                //4、注意:在这里我们不能够保证解码出来的一帧视频像素数据格式是yuv格式

                //视频像素数据格式很多种类型: yuv420P、yuv422p、yuv444p等等

                //保证:我的解码后的视频像素数据格式统一为yuv420P->通用的格式

                //进行类型转换: 将解码出来的视频像素点数据格式->统一转类型为yuv420P

                //sws_scale作用:进行类型转换的

                //参数一:视频像素数据格式上下文

                //参数二:原来的视频像素数据格式->输入数据

                //参数三:原来的视频像素数据格式->输入画面每一行大小

                //参数四:原来的视频像素数据格式->输入画面每一行开始位置(填写:0->表示从原点开始读取)

                //参数五:原来的视频像素数据格式->输入数据行数

                //参数六:转换类型后视频像素数据格式->输出数据

                //参数七:转换类型后视频像素数据格式->输出画面每一行大小

                sws_scale(swscontext,

                          (const uint8_t const )avframe_in->data,

                          avframe_in->linesize,

                          0,

                          avcodec_context->height,

                          avframe_yuv420p->data,

                          avframe_yuv420p->linesize);

                //方式一:直接显示视频上面去

                //方式二:写入yuv文件格式

                //5、将yuv420p数据写入yuv文件中

                //51 计算YUV大小

                //分析一下原理

                //Y表示:亮度

                //UV表示:色度

                //有规律

                //YUV420P格式规范一:Y结构表示一个像素(一个像素对应一个Y)

                //YUV420P格式规范二:4个像素点对应一个(U和V: 4Y = U = V)

                y_size = avcodec_context->width avcodec_context->height;

                u_size = y_size /4;

                v_size = y_size /4;

                //52 写入yuv文件

                //首先->Y数据

                fwrite(avframe_yuv420p->data[0], 1, y_size, file_yuv420p);

                //其次->U数据

                fwrite(avframe_yuv420p->data[1], 1, u_size, file_yuv420p);

                //再其次->V数据

                fwrite(avframe_yuv420p->data[2], 1, v_size, file_yuv420p);

                current_index++;

                NSLog("当前解码第%d帧", current_index);

            }

        }

    }

    //第八步:释放内存资源,关闭解码器

    av_packet_free(&packet);

    fclose(file_yuv420p);

    av_frame_free(&avframe_in);

    av_frame_free(&avframe_yuv420p);

    free(out_buffer);

    avcodec_close(avcodec_context);

    avformat_free_context(avformat_context);

;   一 引言

    我们知道视频聊天软件的关键技术在于采集视频 并实时传输给聊天软件在线的人 对于视频的采集 这里采用微软公司的关于数字视频的一个软件包VFW(Video for Windows) 相信很多人对它都很熟习 VFW能使应用程序通过数字化设备从传统的模拟视频源得到数字化的视频剪辑 VFW的一个关键思想是播放时不需要专用硬件 为了解决数字视频数据量大的问题 需要对数据进行压缩 而VFW引进了AVI的文件标准 该标准未规定如何对视频进行捕捉 压缩及播放 仅规定视频和音频该如何存储在硬盘上及在AVI文件中交替存储视频帧和与之相匹配的音频数据 通过VFW 开发人员通过发送消息或设置属性来捕捉 播放和编辑视频剪辑 当用户在安装VFW时 安装程序会自动地安装配置视频所需要的组件 如设备驱动程序 视频压缩程序等 VFW主要由 个模块组成 VFW功能模块

    AVICAP DLL 包含执行视频捕捉的函数 它给AVI文件的I/O处理和视频 音频设备驱动程序提供一个高级接口

    MSVIDEO DLL 包含一套特殊的DrawDib函数 用来处理屏幕上的视频 *** 作

    MCIAVI DRV 包括对VFW的MCI命令解释器的驱动程序

    AVIFILE DLL 包含由标准多媒体I/O(mmio)函数提供的更高的命令 用来访问 AVI文件

    ICM 压缩管理器 用于管理的视频压缩/解压缩的编译码器(Codec)

    ACM 音频压缩管理器 提供与ICM相似的服务 适用于波形音频

    对于视频的传输 我们使用UDP来传 因为UDP传输速度快 TCP是面向连接的 建立连接时双方需经过三次握手 数据传输可靠 FTP telnet等就是基于TCP的 UDP是面向非连接的 发出信息不需对方确认 但这样速度比TCP快 但有可能丢失数据 象SMTP tftp等就是基于UDP的 另外UDP还支持广播 UDP广播两种 一种是directed broadcast 比如你的网段是 X 你就往 发就可以了 另一种是limited broadcast 广播地址是

    二 视频聊天软件的开发步骤

     创建捕捉窗口 采集视频

    在进行视频捕捉之前必需要先创建一个捕捉窗口 并应以此为基础进行所有的捕捉及设置 *** 作 捕捉窗口可用AVICap窗口类的 CapCreateCaptureWindow 函数来创建 其窗口风格可设置为WSCHILD和WS_VISIBLE参数

    有了捕捉窗口 我们就可以将视频流和音频流捕捉到一个AVI文件中 动态地同视频和音频输入器件连接或断开 用Overlay或Preview模式对输入的视频流进行实时显示 设置捕捉速率 显示控制视频源 视频格式及视频压缩的对话框 创建 保存或载入调色板 将图像和相关的调色板拷贝到剪贴板 将捕捉的单帧图像保存到BMP格式文件中

     捕捉窗口和驱动程序的关联

    仅仅一个捕捉窗口是不能工作起来的 它必须要与一个设备相关联才能取得视频信号 用函数CapDriverConnect可使捕捉窗与其设备驱动程序相关联

     设置视频设备的属性

    通过设置TcaptureParms结构变量的各个成员变量 可以控制设备的采样频率 中断采样按键 状态行为 设置好TcaptureParms结构变量后 可以用函CapCaptureSetSetup使设置生效 之后还可以用CapPreviewScale CapPreviewRate设置预览的比例与速度 也可以直接使用设备的默认值

     打开预览

    利用函数CapOverlay可选择是否采用叠加模式预览 以使系统资源占用小 视频显示速度加快 然后用CapPreview启动预览功能 这时就可以在屏幕上看到来自摄像头的图像了

     使用捕捉窗回调函数

    前的四个步骤就可以建立一个基本的视频捕捉程序了 如果想自己处理从设备捕捉到的视频数据 则要使用捕捉窗回调函数来处理 比如一帧一帧地获得视频数据 也可以以流的方式获得视频数据等等

     传输视频流

    使用回调函数可以取得第一帧的数据 我们使用网络技术将数据发给其它机器 其它机品将接收的数据显示出来

     接收视频

    接收UDP数据 同时将接收到的数据回显出来 这样就可以看到远处传来的视频了

    三 用Delphi编写程序代码

    微软的VFW SDK只有VC和VB版 并没有Delphi版 不过在网上可以找到VFW PAS文件 FW PAS文件声明了调用DLL中的各个函数和变量 (注 源代码中提供了VFW PAS文件)

    下面就以Delphi 开发一个网络视频聊天软件 聊天软件分两个程序 一个是视频采集程序并进行UDP广播的视频聊天软件服务器 另一个是接收UDP广播程序显示传来的视频数据的视频聊天软件客户端

     建立视频聊天软件服务器

     )新建一个工程 命名为Project dpr 并把VFW PAS加到USE中

     )在Form 上放置一个Tpanel控件 该控件用于显示视频 之后再放置两个Tbutton控件 一个caption为 开始 另一个Name为 停止 放置一个UDP组件 这里用indy的IdUDPClient用来传输视频 如图示

    用Delphi开发视频聊天软件(二)

lishixinzhi/Article/program/Delphi/201311/24745

摄像头获取到的数据其实都是一帧一帧的,任何语言不仅仅是java都可以对这些数据进行处理,但是Java不是最佳的选择。

针对这些你首先要知道摄像头是否对视频流使用了压缩技术,例如:H263\H264或者是没有压缩过的。

如果是压缩过的,你需要先对每一帧进行解编码(DECODE),然后就是一张图了,你进行你要做的编辑,然后再编码(ENCODE),再放入视频流中。

C语言开发的视频处理软件是cstream,你可以看一看,这个是通道式处理,添加插件,处理每一个buffer,一个buffer就是一帧,同时也可以处理多种格式的音频。

对视频的编辑不是简简单单就能实现的,需要对算法有很好的了解。

叫做FreeVideoToJPGConverter的软件

1、首先,先去FreeVideoToJPGConverter的官网把这个软件下载下来,它的界面如图所示。

2、点击添加文件按钮。

3、这是软件支持的视频格式,如果打开没有看到想要的分解的视频,记得选中展开所有文件。

4、选中视频。

5、点击箭头指向的按钮选定要保存到哪一个文件夹,不然会找不到。

6、根据自己的需要来调节数量输出的密度,如果不清楚它们的差异可以自己多试几次,但是测试参数时记得清除输出文件夹,不然会很混乱。

7、点击转换按钮就可以开始等待了,时间随视频的长度和质量而不同。通常需要一些时间,5-10分钟很正常。可以把软件最小化,但不要退出。

8、转换完成后,点击箭头指向的按钮,可以看到转换后的文件夹。

以上就是关于FFmpeg的视频解码详解全部的内容,包括:FFmpeg的视频解码详解、用Delphi开发视频聊天软件(一)、java怎么写摄像头获取的视频流等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9682876.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-30
下一篇2023-04-30

发表评论

登录后才能评论

评论列表(0条)

    保存