当前位置: 首页 > news >正文

网站名称意义/服务器域名怎么注册

网站名称意义,服务器域名怎么注册,做门面商铺比较好的网站,石家庄seo公司最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)_雷霄骅的博客-CSDN博客_ffmpeg 编码器 初学音视频、ffmpeg。根据雷神的例子跑起来,调用libavcodec将YUV像素数据(YUV420P)编码为H.264码流,H.265为…

最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)_雷霄骅的博客-CSDN博客_ffmpeg 编码器

初学音视频、ffmpeg。根据雷神的例子跑起来,调用libavcodec将YUV像素数据(YUV420P)编码为H.264码流,H.265为(HEVC)。

视频编码:

视频编码方式就是指通过特定的压缩技术,将某个视频格式的文件转换成另一种视频格式文件的方式。视频编码格式常见到的有:MPEG-2 TS、Divx、Xvid、H.264、WMV-HD和VC-1。

原始的图像和声音是需要占用很大的存储空间和带宽的,不适合运输和传送(例如例子的yuv数据),所以我们需要对原始图像和声音加工,压缩得更小。就是图像和声音的压缩方法。

 

编码流程:

  • 首先要有未压缩的 YUV 原始数据。
  • 其次要根据想要编码的格式选择特定的编码器。
  • 最后编码器的输出即为编码后的视频帧。

示例里编码完的ds.h264 文件在vlc 里播放可以看到分辨率为 480x272

现在研究把480x272 的输分辨率改为 320x270,需要使用以下函数:

FFmpeg中的 sws_scale() 函数主要是用来做视频像素格式和分辨率的转换。

// 初始化
struct SwsContext *sws_ctx(int srcW, /* 输入图像的宽度 */int srcH, /* 输入图像的宽度 */enum AVPixelFormat srcFormat, /* 输入图像的像素格式 */int dstW, /* 输出图像的宽度 */int dstH, /* 输出图像的高度 */enum AVPixelFormat dstFormat, /* 输出图像的像素格式 */int flags,/* 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEAR */SwsFilter *srcFilter, /* 输入图像的滤波器信息, 若不需要传NULL */SwsFilter *dstFilter, /* 输出图像的滤波器信息, 若不需要传NULL */const double *param /* 特定缩放算法需要的参数(?),默认为NULL */);// 转换的函数  
sws_scale(sws_ctx, src_frame->data, src_frame->linesize,  0, height, //源图像的高  dst_frame->data, dst_frame->linesize); 1.参数 SwsContext *c, 转换格式的上下文。也就是 sws_getContext 函数返回的结果。2.参数 const uint8_t *const srcSlice[], 输入图像的每个颜色通道的数据指针。其实就是解码后的        AVFrame中的data[]数组。因为不同像素的存储格式不同,所以srcSlice[]维数也有可能不同。
以YUV420P为例,它是planar格式,它的内存中的排布如下:YYYYYYYY UUUU VVVV使用FFmpeg解码后存储在AVFrame的data[]数组中时:data[0]——-Y分量, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8……data[1]——-U分量, U1, U2, U3, U4……data[2]——-V分量, V1, V2, V3, V4……linesize[]数组中保存的是对应通道的数据宽度 ,linesize[0]——-Y分量的宽度linesize[1]——-U分量的宽度linesize[2]——-V分量的宽度而RGB24,它是packed格式,它在data[]数组中则只有一维,它在存储方式如下:data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……这里要特别注意,linesize[0]的值并不一定等于图片的宽度,有时候为了对齐各解码器的CPU,实际尺寸会大于图片的宽度,这点在我们编程时(比如OpengGL硬件转换/渲染)要特别注意,否则解码出来的图像会异常。3.参数const int srcStride[],输入图像的每个颜色通道的跨度。.也就是每个通道的行字节数,对应的是解码后的AVFrame中的linesize[]数组。根据它可以确立下一行的起始位置,不过stride和width不一定相同,这是因为:
a.由于数据帧存储的对齐,有可能会向每行后面增加一些填充字节这样 stride = width + N;
b.packet色彩空间下,每个像素几个通道数据混合在一起,例如RGB24,每个像素3字节连续存放,因此下一行的位置需要跳过3*width字节。4.参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域,srcSliceY是起始位置,srcSliceH是处理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。这种设置是为了多线程并行,例如可以创建两个线程,第一个线程处理 [0, h/2-1]行,第二个线程处理 [h/2, h-1]行。并行处理加快速度。
5.参数uint8_t *const dst[], const int dstStride[]定义输出图像信息(输出的每个颜色通道数据指针,每个颜色通道行字节数)// 释放sws_scale
sws_freeContext(sws_ctx); 

在雷神代码基础上修改主要加了以上函数,加了一些不懂函数的注释。现在把代码贴出来,主要要区分开输入数据和输出数据:

#include <QApplication>#include <stdio.h>#define __STDC_CONSTANT_MACROS#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#ifdef __cplusplus
};
#endif
#endif//test different codec
#define TEST_H264  1
#define TEST_HEVC  0// H265int main(int argc, char* argv[])
{AVCodec *pCodec;AVCodecContext *pCodecCtx= NULL;int i, ret, got_output;FILE *fp_in;FILE *fp_out;//    AVFrame *src_frame;//存储一帧未编码的像素数据。AVFrame *src_frame, *dst_frame;// 输入和输出AVPacket pkt;//存储一帧压缩编码数据。int y_size;int framecnt=0;char filename_in[]="../ds_480x272.yuv";static struct SwsContext *img_convert_ctx;#if TEST_HEVCAVCodecID codec_id=AV_CODEC_ID_HEVC;char filename_out[]="ds.hevc";
#elseAVCodecID codec_id=AV_CODEC_ID_H264;char filename_out[]="ds.h264";
#endifint src_w = 480, src_h = 272;int dst_w = 320, dst_h = 270;int framenum = 100;avcodec_register_all();//注册所有编解码器相关的组件。av_register_all():注册所有的编解码器,复用/解复用器等等组件pCodec = avcodec_find_encoder(codec_id);//查找ID值为AV_CODEC_ID_H264的AAC编码器if (!pCodec) {printf("Codec not found\n");return -1;}pCodecCtx = avcodec_alloc_context3(pCodec); // 为AVCodecContext分配内存 创建AVCodecContext结构体。AVCodecContext中很多的参数是编码的时候使用if (!pCodecCtx) {printf("Could not allocate video codec context\n");return -1;}pCodecCtx->bit_rate = 400000;//平均比特率 目标的码率,即采样的码率;显然,采样码率越大,视频大小越大pCodecCtx->width = dst_w;//如果是视频的话,代表宽和高 编码目标的视频帧大小,以像素为单位pCodecCtx->height = dst_h;//帧率的基本单位,我们用分数来表示,//用分数来表示的原因是,有很多视频的帧率是带小数的eg:NTSC 使用的帧率是29.97pCodecCtx->time_base.num=1; //根据该参数,可以把PTS转化为实际的时间(单位为秒s)pCodecCtx->time_base.den=25;pCodecCtx->gop_size = 10;//一组图片中的图片数//两个非B帧之间允许出现多少个B帧数//设置0表示不使用B帧//b 帧越多,图片越小pCodecCtx->max_b_frames = 1;pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;//像素格式 也就是说采用什么样的色彩空间来表明一个像素点//设置显示的率pCodecCtx->framerate = {10, 1};//量化参数设置(影响视频清晰度) 越小越清晰pCodecCtx->qmax = 51;pCodecCtx->qmin = 10;if (codec_id == AV_CODEC_ID_H264)av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打开编码器。printf("Could not open codec\n");return -1;}src_frame = av_frame_alloc();//分配AVFrame AVFrame结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCMif (!src_frame) {printf("Could not allocate video frame\n");return -1;}src_frame->format = pCodecCtx->pix_fmt; //帧的格式src_frame->width  = src_w;src_frame->height = src_h;//视频帧宽和高(1920x1080,1280x720...)dst_frame = av_frame_alloc();dst_frame->width  = dst_w;dst_frame->height = dst_h;  //初始化一个SwsContextimg_convert_ctx = sws_getContext(src_frame->width, src_frame->height, \pCodecCtx->pix_fmt, dst_frame->width, dst_frame->height, \pCodecCtx->pix_fmt, 0, nullptr, nullptr, nullptr);//    int dst_bytes_num = avpicture_get_size(pCodecCtx->pix_fmt, dst_frame->width, dst_frame->height);//计算这个格式的图片,需要多少字节来存储
//    uint8_t* dst_buff = (uint8_t *)av_malloc(dst_bytes_num * sizeof(uint8_t));//申请空间来存放图片数据。包含源数据和目标数据//    前面的av_frame_alloc函数,只是为这个AVFrame结构体分配了内存,//    而该类型的指针指向的内存还没分配。这里把av_malloc得到的内存和AVFrame关联起来。//    当然,其还会设置AVFrame的其他成员
//    avpicture_fill((AVPicture *)dst_frame, dst_buff, pCodecCtx->pix_fmt, dst_frame->width, dst_frame->height);//ffmpeg4.2.2中就已经被抛弃了, 取而代之的是av_image_fill_arrays()//以上注释的代码也能实现 av_image_allocav_image_alloc(dst_frame->data, dst_frame->linesize, dst_frame->width, dst_frame->height,pCodecCtx->pix_fmt, 16);ret = av_image_alloc(src_frame->data, src_frame->linesize, src_frame->width, src_frame->height,pCodecCtx->pix_fmt, 16);//按照指定的宽、高、像素格式来分析图像内存//    pointers[4]:保存图像通道的地址。如果是RGB,则前三个指针分别指向R,G,B的内存地址。第四个指针保留不用//    linesizes[4]:保存图像每个通道的内存对齐的步长,即一行的对齐内存的宽度,此值大小等于图像宽度。//     w:                 要申请内存的图像宽度。//     h:                  要申请内存的图像高度。//     pix_fmt:        要申请内存的图像的像素格式。//     align:            用于内存对齐的值。//     返回值:所申请的内存空间的总大小。如果是负值,表示申请失败。if (ret < 0) {printf("Could not allocate raw picture buffer\n");return -1;}//Input raw datafp_in = fopen(filename_in, "rb");//使用给定的模式 mode 打开 filename 所指向的文件。 rb+读写打开一个二进制文件,允许读数据。if (!fp_in) {printf("Could not open %s\n", filename_in);return -1;}//Output bitstreamfp_out = fopen(filename_out, "wb");//wb 只写打开或新建一个二进制文件;只允许写数据。if (!fp_out) {printf("Could not open %s\n", filename_out);return -1;}y_size = src_frame->width * src_frame->height;//Encodefor (i = 0; i < framenum; i++) {av_init_packet(&pkt);//设置默认值pkt.data = NULL;    // packet data will be allocated by the encoderpkt.size = 0;   //从给定流 fp_in 读取数据到 src_frame->data[0] 所指向的数组中。//Read raw YUV dataif (fread(src_frame->data[0],1,y_size,fp_in)<= 0||		// Yfread(src_frame->data[1],1,y_size/4,fp_in)<= 0||	// Ufread(src_frame->data[2],1,y_size/4,fp_in)<= 0){	// Vreturn -1;}else if(feof(fp_in)){//测试给定流 fp_in 的文件结束标识符。break;}sws_scale(img_convert_ctx, (uint8_t const * const *)src_frame->data,src_frame->linesize, 0, src_frame->height, dst_frame->data, dst_frame->linesize);src_frame->pts = i;/* encode the image */ret = avcodec_encode_video2(pCodecCtx, &pkt, dst_frame, &got_output);//编码一帧数据。要传转换完的 dst_frameif (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Succeed to encode frame: %5d\tsize:%5d\n",framecnt,pkt.size);framecnt++;fwrite(pkt.data, 1, pkt.size, fp_out);//把 pkt.data 所指向的数组中的数据写入到给定流 fp_out 中av_free_packet(&pkt);//清空pkt中data以及buf的内容,并没有把pkt的指针清空}}//Flush Encoderfor (got_output = 1; got_output; i++) {ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);if (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",pkt.size);fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}fclose(fp_out);//关闭流 fp_out。刷新所有的缓冲区。avcodec_close(pCodecCtx);//关闭编码器av_free(pCodecCtx);//释放已分配av_malloc() AVCodecContextav_freep(&src_frame->data[0]);//释放并清理指针av_frame_free(&src_frame);//释放AVFrameav_frame_free(&dst_frame);//释放AVFramesws_freeContext(img_convert_ctx);	return 0;
}

下面是转换完的视频: 

 参考文档:

FFmpeg编码基础流程_zhaodb_的博客-CSDN博客_ffmpeg编码流程 FFmpeg: FFmepg中的sws_scale() 函数分析 - 夜行过客 - 博客园

图像视频编码和FFmpeg(3)-----用FFmpeg进行图像格式转换和AVFrame简介 avpicture_fill - bw_0927 - 博客园ffmpeg的API函数用法 :sws_scale函数的用法-具体应用 - 怀想天空2013 - 博客园图像视频编码和FFmpeg(3)-----用FFmpeg进行图像格式转换和AVFrame简介 avpicture_fill - bw_0927 - 博客园

相关文章: