文章目录

  • 1.定义图片RGB数据结构体
  • 2.定义分配和释放内存的C方法
  • 3.提取视频文件中的原始图像数据
  • 4.将YUV数据转换成图片RGB数据
  • 5.将RGB数据保存成对应的图片
  • 完整工程代码

在浏览视频的过程中,有时候我们需要将视频中的某一帧图像以图片的形式保存到本地。这时候我们就需要对视频流中的图像数据进行转码提取和数据存储了。视频流转图片的流程如下图所示:

1.定义图片RGB数据结构体

首先定义图片数据结构体用来存储转换之后的图片RGB数据

//定义RGB图片数据结构
typedef struct RGB888 {uint8_t r;uint8_t g;uint8_t b;
} RGB888;typedef struct PICTURE {int32_t width;int32_t height;RGB888 *data;
} PICTURE;

2.定义分配和释放内存的C方法

通过封装的方法来对图片数据进行分配和释放。

//创建内存块
void *Memory_Alloc(size_t size)
{void *result = malloc(size);if (!result) {av_log(NULL,AV_LOG_DEBUG,"ERROR: Could not allocate enough memory");}memset(result, 0, size);return result;
}//释放内存
void Memory_Free(void *memory)
{if (memory) {free(memory);}
}//重新分配内存块
void *Memory_Realloc(void *memory, size_t size)
{void *result = realloc(memory, size);if (!result) {av_log(NULL, AV_LOG_DEBUG, "ERROR: Could not allocate enough memory");}return result;
}//清空内存
void Memory_FreePointer(void *arg)
{void *memory;memcpy(&memory, arg, sizeof(void *));memcpy(arg, &(void *) { NULL }, sizeof(void *));Memory_Free(memory);
}//根据宽高创建图片内存
PICTURE *Picture_Create(int width, int height)
{PICTURE *picture = Memory_Alloc(sizeof(PICTURE));picture->width = width;picture->height = height;picture->data = Memory_Alloc(width * height * sizeof(RGB888));return picture;
}

3.提取视频文件中的原始图像数据

将视频文件中的视频流提取出来,然后将视频流中的数据包解析成原始的YUV数据。

int main(int argc, char **argv)
{int ret;const char *filename, *outfilename;AVFormatContext *fmt_ctx = NULL;const AVCodec *codec;AVCodecContext *codec_ctx = NULL;AVStream *st = NULL;int stream_index;int frame_count;AVFrame *frame;struct SwsContext *img_convert_ctx;AVPacket avpkt;PICTURE* target_pic = NULL;if (argc <= 2) {fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);exit(0);}filename = argv[1];outfilename = argv[2];//打开媒体文件的上下文if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) < 0) {fprintf(stderr, "Could not open source file %s\n", filename);exit(1);}//获取流信息if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");exit(1);}//查找视频流ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if (ret < 0) {fprintf(stderr, "Could not find %s stream in input file '%s'\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO), filename);return ret;}stream_index = ret;st = fmt_ctx->streams[stream_index];//查找解码器codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec) {fprintf(stderr, "Failed to find %s codec\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO));return AVERROR(EINVAL);}//设置编码器上下文的参数codec_ctx = avcodec_alloc_context3(NULL);if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}if ((ret = avcodec_parameters_to_context(codec_ctx, st->codecpar)) < 0) {fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO));return ret;}//打开编码器if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}//初始化数据包av_init_packet(&avpkt);//初始化帧信息frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}//读取帧数据并保存成图片frame_count = 0;while (av_read_frame(fmt_ctx, &avpkt) >= 0) {if (avpkt.stream_index == stream_index) {int ret = decode_frame(codec_ctx, frame, &frame_count, &avpkt, &target_pic);if (ret == 0){char buf[1024];snprintf(buf, sizeof(buf), "%s-%d.jpg", outfilename, frame_count);SavePictureToFile(target_pic, buf);Memory_FreePointer(&target_pic->data);}}av_packet_unref(&avpkt);}//数据清理avformat_close_input(&fmt_ctx);avcodec_free_context(&codec_ctx);av_frame_free(&frame);return 0;
}

4.将YUV数据转换成图片RGB数据

//提取帧数据中的YUV数据并转换成RGB888
int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *frame_count, AVPacket *pkt, PICTURE** target_pic)
{int ret, len = 0;char buf[1024];struct SwsContext *sws_ctx = NULL;uint8_t *dst_data[4] = { 0 };int dst_linesize[4] = { 0 };//解析数据包ret = avcodec_send_packet(avctx, pkt);if (ret < 0) {fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret));return ret;}//读取数据帧信息ret = avcodec_receive_frame(avctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0){fprintf(stderr, "Error during decoding\n");return 1;}//分配图片内存*target_pic = Picture_Create(frame->width, frame->height);//数据格式转换上下文//YUV---RGB888sws_ctx = sws_getContext(avctx->width, avctx->height, avctx->pix_fmt,(*target_pic)->width, (*target_pic)->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,NULL, NULL, NULL);if (!sws_ctx) {Memory_FreePointer(&(*target_pic)->data);return 2;}ret = av_image_alloc(dst_data, dst_linesize, (*target_pic)->width, (*target_pic)->height,AV_PIX_FMT_RGB24, 1);if (ret < 0) {Memory_FreePointer(&(*target_pic)->data);return 3;}//转换数据格式sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0,frame->height, dst_data, dst_linesize);//拷贝数据内容av_image_copy_to_buffer((uint8_t *)(*target_pic)->data,(*target_pic)->width *  (*target_pic)->height * sizeof(RGB888),(const uint8_t *const *)dst_data, dst_linesize, AV_PIX_FMT_RGB24,(*target_pic)->width, (*target_pic)->height, 1);//清理分配的内存fflush(stdout);av_freep(&dst_data[0]);if (sws_ctx) {sws_freeContext(sws_ctx);}(*frame_count)++;return 0;
}

5.将RGB数据保存成对应的图片

这里支持存储成png图片和jpg图片

//将图片数据写入到文件中
//根据文件后缀来确定图片的格式,支持png和jpg
int SavePictureToFile(const PICTURE *pic, const char *path)
{int ret = 0;int error_code = 0;const AVCodec *codec = NULL;AVCodecContext *codec_ctx = NULL;AVFrame *frame = NULL;AVPacket *packet = NULL;struct SwsContext *sws_ctx = NULL;FILE *fp = NULL;//源格式enum AVPixelFormat source_pix_fmt = AV_PIX_FMT_RGB24;//目标格式enum AVPixelFormat target_pix_fmt;enum AVCodecID codec_id;//判断输出文件的类型if (strstr(path, ".jpg")) {target_pix_fmt = AV_PIX_FMT_YUVJ420P;codec_id = AV_CODEC_ID_MJPEG;}else if (strstr(path, ".png")) {target_pix_fmt = AV_PIX_FMT_RGB24;codec_id = AV_CODEC_ID_PNG;}else {goto cleanup;}fp = fopen(path, "wb");if (!fp) {goto cleanup;}//查找图片编码器codec = avcodec_find_encoder(codec_id);if (!codec) {error_code = AVERROR_MUXER_NOT_FOUND;goto cleanup;}//设置编码器的参数codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {error_code = AVERROR(ENOMEM);goto cleanup;}codec_ctx->bit_rate = 400000;codec_ctx->width = pic->width;codec_ctx->height = pic->height;codec_ctx->time_base = (AVRational) { 1, 25 };codec_ctx->pix_fmt = target_pix_fmt;if (codec_id == AV_CODEC_ID_MJPEG) {// 9 JPEG qualitycodec_ctx->flags |= AV_CODEC_FLAG_QSCALE;codec_ctx->global_quality = FF_QP2LAMBDA * 9;}//打开编码器的上下文error_code = avcodec_open2(codec_ctx, codec, NULL);if (error_code < 0){goto cleanup;}//创建帧frame = av_frame_alloc();if (!frame) {error_code = AVERROR(ENOMEM);goto cleanup;}frame->format = codec_ctx->pix_fmt;frame->width = codec_ctx->width;frame->height = codec_ctx->height;frame->pts = 0;error_code = av_image_alloc(frame->data, frame->linesize, codec_ctx->width, codec_ctx->height,codec_ctx->pix_fmt, 32);if (error_code < 0) {goto cleanup;}//创建数据包packet = av_packet_alloc();av_new_packet(packet, 0);//获取图片处理对象的上下文sws_ctx = sws_getContext(pic->width, pic->height, source_pix_fmt, frame->width, frame->height,target_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx) {error_code = AVERROR_EXTERNAL;goto cleanup;}//将图片数据添加到帧中uint8_t *src_planes[4];int src_linesize[4];av_image_fill_arrays(src_planes, src_linesize, (const uint8_t *)pic->data, source_pix_fmt,pic->width, pic->height, 1);sws_scale(sws_ctx, (const uint8_t *const *)src_planes, src_linesize, 0,pic->height, frame->data, frame->linesize);//编码器对像素格式进行转换//将处理后的目标格式帧数据,写入到文件中error_code = avcodec_send_frame(codec_ctx, frame);if (error_code < 0) {goto cleanup;}while (error_code >= 0) {error_code = avcodec_receive_packet(codec_ctx, packet);if (error_code == AVERROR(EAGAIN) || error_code == AVERROR_EOF) {error_code = 0;break;}if (error_code < 0) {goto cleanup;}fwrite(packet->data, 1, packet->size, fp);av_packet_unref(packet);}//清理释放数据
cleanup:if (error_code) {av_log(NULL,AV_LOG_DEBUG,"Error while saving picture %s: %s", path, av_err2str(error_code));}if (fp) {fclose(fp);fp = NULL;}if (sws_ctx) {sws_freeContext(sws_ctx);}if (packet) {av_packet_free(&packet);}if (codec) {avcodec_close(codec_ctx);av_free(codec_ctx);codec_ctx = NULL;}if (frame) {av_freep(&frame->data[0]);av_frame_free(&frame);}return ret;
}

完整工程代码

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>#pragma pack(2)//定义RGB图片数据结构
typedef struct RGB888 {uint8_t r;uint8_t g;uint8_t b;
} RGB888;typedef struct PICTURE {int32_t width;int32_t height;RGB888 *data;
} PICTURE;//创建内存块
void *Memory_Alloc(size_t size)
{void *result = malloc(size);if (!result) {av_log(NULL,AV_LOG_DEBUG,"ERROR: Could not allocate enough memory");}memset(result, 0, size);return result;
}//释放内存
void Memory_Free(void *memory)
{if (memory) {free(memory);}
}//重新分配内存块
void *Memory_Realloc(void *memory, size_t size)
{void *result = realloc(memory, size);if (!result) {av_log(NULL, AV_LOG_DEBUG, "ERROR: Could not allocate enough memory");}return result;
}//清空内存
void Memory_FreePointer(void *arg)
{void *memory;memcpy(&memory, arg, sizeof(void *));memcpy(arg, &(void *) { NULL }, sizeof(void *));Memory_Free(memory);
}//根据宽高创建图片内存
PICTURE *Picture_Create(int width, int height)
{PICTURE *picture = Memory_Alloc(sizeof(PICTURE));picture->width = width;picture->height = height;picture->data = Memory_Alloc(width * height * sizeof(RGB888));return picture;
}//将图片数据写入到文件中
//根据文件后缀来确定图片的格式,支持png和jpg
int SavePictureToFile(const PICTURE *pic, const char *path)
{int ret = 0;int error_code = 0;const AVCodec *codec = NULL;AVCodecContext *codec_ctx = NULL;AVFrame *frame = NULL;AVPacket *packet = NULL;struct SwsContext *sws_ctx = NULL;FILE *fp = NULL;//源格式enum AVPixelFormat source_pix_fmt = AV_PIX_FMT_RGB24;//目标格式enum AVPixelFormat target_pix_fmt;enum AVCodecID codec_id;//判断输出文件的类型if (strstr(path, ".jpg")) {target_pix_fmt = AV_PIX_FMT_YUVJ420P;codec_id = AV_CODEC_ID_MJPEG;}else if (strstr(path, ".png")) {target_pix_fmt = AV_PIX_FMT_RGB24;codec_id = AV_CODEC_ID_PNG;}else {goto cleanup;}fp = fopen(path, "wb");if (!fp) {goto cleanup;}//查找图片编码器codec = avcodec_find_encoder(codec_id);if (!codec) {error_code = AVERROR_MUXER_NOT_FOUND;goto cleanup;}//设置编码器的参数codec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {error_code = AVERROR(ENOMEM);goto cleanup;}codec_ctx->bit_rate = 400000;codec_ctx->width = pic->width;codec_ctx->height = pic->height;codec_ctx->time_base = (AVRational) { 1, 25 };codec_ctx->pix_fmt = target_pix_fmt;if (codec_id == AV_CODEC_ID_MJPEG) {// 9 JPEG qualitycodec_ctx->flags |= AV_CODEC_FLAG_QSCALE;codec_ctx->global_quality = FF_QP2LAMBDA * 9;}//打开编码器的上下文error_code = avcodec_open2(codec_ctx, codec, NULL);if (error_code < 0){goto cleanup;}//创建帧frame = av_frame_alloc();if (!frame) {error_code = AVERROR(ENOMEM);goto cleanup;}frame->format = codec_ctx->pix_fmt;frame->width = codec_ctx->width;frame->height = codec_ctx->height;frame->pts = 0;error_code = av_image_alloc(frame->data, frame->linesize, codec_ctx->width, codec_ctx->height,codec_ctx->pix_fmt, 32);if (error_code < 0) {goto cleanup;}//创建数据包packet = av_packet_alloc();av_new_packet(packet, 0);//获取图片处理对象的上下文sws_ctx = sws_getContext(pic->width, pic->height, source_pix_fmt, frame->width, frame->height,target_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx) {error_code = AVERROR_EXTERNAL;goto cleanup;}//将图片数据添加到帧中uint8_t *src_planes[4];int src_linesize[4];av_image_fill_arrays(src_planes, src_linesize, (const uint8_t *)pic->data, source_pix_fmt,pic->width, pic->height, 1);sws_scale(sws_ctx, (const uint8_t *const *)src_planes, src_linesize, 0,pic->height, frame->data, frame->linesize);//编码器对像素格式进行转换//将处理后的目标格式帧数据,写入到文件中error_code = avcodec_send_frame(codec_ctx, frame);if (error_code < 0) {goto cleanup;}while (error_code >= 0) {error_code = avcodec_receive_packet(codec_ctx, packet);if (error_code == AVERROR(EAGAIN) || error_code == AVERROR_EOF) {error_code = 0;break;}if (error_code < 0) {goto cleanup;}fwrite(packet->data, 1, packet->size, fp);av_packet_unref(packet);}//清理释放数据
cleanup:if (error_code) {av_log(NULL,AV_LOG_DEBUG,"Error while saving picture %s: %s", path, av_err2str(error_code));}if (fp) {fclose(fp);fp = NULL;}if (sws_ctx) {sws_freeContext(sws_ctx);}if (packet) {av_packet_free(&packet);}if (codec) {avcodec_close(codec_ctx);av_free(codec_ctx);codec_ctx = NULL;}if (frame) {av_freep(&frame->data[0]);av_frame_free(&frame);}return ret;
}//提取帧数据中的YUV数据并转换成RGB888
int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *frame_count, AVPacket *pkt, PICTURE** target_pic)
{int ret, len = 0;char buf[1024];struct SwsContext *sws_ctx = NULL;uint8_t *dst_data[4] = { 0 };int dst_linesize[4] = { 0 };//解析数据包ret = avcodec_send_packet(avctx, pkt);if (ret < 0) {fprintf(stderr, "Error submitting a packet for decoding (%s)\n", av_err2str(ret));return ret;}//获取对应的数据帧,一个包里面可能有很多帧ret = avcodec_receive_frame(avctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0){fprintf(stderr, "Error during decoding\n");return 1;}*target_pic = Picture_Create(frame->width, frame->height);//数据格式转换上下文//YUV---RGB888sws_ctx = sws_getContext(avctx->width, avctx->height, avctx->pix_fmt,(*target_pic)->width, (*target_pic)->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,NULL, NULL, NULL);if (!sws_ctx) {Memory_FreePointer(&(*target_pic)->data);return 2;}ret = av_image_alloc(dst_data, dst_linesize, (*target_pic)->width, (*target_pic)->height,AV_PIX_FMT_RGB24, 1);if (ret < 0) {Memory_FreePointer(&(*target_pic)->data);return 3;}//转换数据格式sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0,frame->height, dst_data, dst_linesize);//拷贝数据内容av_image_copy_to_buffer((uint8_t *)(*target_pic)->data,(*target_pic)->width *  (*target_pic)->height * sizeof(RGB888),(const uint8_t *const *)dst_data, dst_linesize, AV_PIX_FMT_RGB24,(*target_pic)->width, (*target_pic)->height, 1);//将帧数据中第一个图片保存成文件fflush(stdout);av_freep(&dst_data[0]);if (sws_ctx) {sws_freeContext(sws_ctx);}(*frame_count)++;return 0;
}int main(int argc, char **argv)
{int ret;const char *filename, *outfilename;AVFormatContext *fmt_ctx = NULL;const AVCodec *codec;AVCodecContext *codec_ctx = NULL;AVStream *st = NULL;int stream_index;int frame_count;AVFrame *frame;struct SwsContext *img_convert_ctx;AVPacket avpkt;PICTURE* target_pic = NULL;if (argc <= 2) {fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);exit(0);}filename = argv[1];outfilename = argv[2];//打开媒体文件的上下文if (avformat_open_input(&fmt_ctx, filename, NULL, NULL) < 0) {fprintf(stderr, "Could not open source file %s\n", filename);exit(1);}//获取流信息if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");exit(1);}//查找视频流ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if (ret < 0) {fprintf(stderr, "Could not find %s stream in input file '%s'\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO), filename);return ret;}stream_index = ret;st = fmt_ctx->streams[stream_index];//查找解码器codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec) {fprintf(stderr, "Failed to find %s codec\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO));return AVERROR(EINVAL);}//设置编码器上下文的参数codec_ctx = avcodec_alloc_context3(NULL);if (!codec_ctx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}if ((ret = avcodec_parameters_to_context(codec_ctx, st->codecpar)) < 0) {fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO));return ret;}//打开编码器if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}//初始化数据包av_init_packet(&avpkt);//初始化帧信息frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}//读取帧数据并保存成图片frame_count = 0;while (av_read_frame(fmt_ctx, &avpkt) >= 0) {if (avpkt.stream_index == stream_index) {int ret = decode_frame(codec_ctx, frame, &frame_count, &avpkt, &target_pic);if (ret == 0){char buf[1024];snprintf(buf, sizeof(buf), "%s-%d.jpg", outfilename, frame_count);SavePictureToFile(target_pic, buf);Memory_FreePointer(&target_pic->data);}}av_packet_unref(&avpkt);}//数据清理avformat_close_input(&fmt_ctx);avcodec_free_context(&codec_ctx);av_frame_free(&frame);return 0;
}

FFmpeg基础:视频流转图片相关推荐

  1. ffmpeg将视频流转化成图片帧,将图片帧转化为视频(基本无损)

    ffmpeg将视频流转化成图片帧,将图片帧转化为视频(基本无损) 其中-r 和-framerate 都是代表帧率(这个属性属于图片的属性,需要放在图片之前) -s 控制输出格式大小 本人在一个视频稳定 ...

  2. ffmpeg 给视频或者图片添加水印和马赛克的方法

    可以使用 FFmpeg 给视频或图片添加水印和马赛克.以下是具体方法: 添加水印 如果需要给视频添加水印,可以使用 overlay 滤镜.该滤镜将两个输入叠加在一起,即将视频和水印画面结合在一起.以下 ...

  3. 使用ffmpeg给视频添加图片及文字水印

    1. 准备工具 MacBook Pro homebrew ffmpeg 2.工具安装 homebrew安装 说起homebrew确实是Mac上非常好用的一个工具,像什么CocoPods,node,ff ...

  4. FFmpeg基础: 视频裁剪

    基本概念 在对视频进行裁剪之前,我们先讲几个基本概念. 第一个概念是PTS(Presentation Time Stamp) 显示时间戳,用来定位帧的显示时间. 第二个概念是DTS(Decode Ti ...

  5. FFmpeg给视频添加图片,文字(vb.net,类库——11)

    给视频添加文字,可以使用添加文字的方法,但是想添加中文字,那只能使用微软雅黑了 那我们想:文字可以被印到图片上,然后图片可以被轻而易举的添加到视频中 借助GDI+完成这一转换 Public Funct ...

  6. 使用 ffmpeg 命令将视频转图片

    使用 ffmpeg 命令将视频转图片 因为要从视频中提取只出现了一瞬间的画面,所以需要将视频转为图片.这里就使用 ffmpeg 进行操作,FFmpeg 配置参考:https://blog.csdn.n ...

  7. 音视频处理 ffmpeg中级开发 视频转图片

    操作流程 目的:使用FFmpeg将视频的每一帧数据转换为图片 1,打开输入的多媒体文件,检索多媒体文件中的流信息 2,查找视频流的索引号,通过索引号获取数据流:通过解析视频流中的编码参数得到解码器ID ...

  8. 使用ffmpeg调整视频时长倍速

    简介:通过ffmpeg调整视频时长,既可以尽量因调整视频时长引起的对视频质量的侵害,也能避免使用第三方工具收费或者广告问题,从而更干净安全的获取目标视频时长转换. 相关攻略: 利用ffmpeg将avi ...

  9. ffmpeg音视频基础知识

    ffmpeg音视频基础知识 前言 一.图像的基础知识 二.视频编码基础知识 1.视频和图片之间的关系 2.为什么要编码? 3.什么是编码? 视频相关专业术语 提示:文章写完后,目录可以自动生成,如何生 ...

  10. FFmpeg 视频添加水印图片

    最近学习FFmpeg编程开发,想写个视频添加水印图片的demo(未对音频或字幕进行处理),代码编写中遇见很多问题,在这里进行做一个笔记来,易于自己记忆和理解.期间在网上找demo,发现很多都是ffmp ...

最新文章

  1. 学习linux要会mysql吗_linux 学习 mysql安装到连接
  2. 文科生都能看懂的机器学习教程:梯度下降、线性回归、逻辑回归
  3. 关于button按钮在IE中的宽度问题、、、、
  4. c语言自学技巧,轻松学C语言,教给你学习技巧
  5. DCMTK:DcmAttributeMatching的单元测试
  6. Python打断点(亲测)
  7. 四川第七届 C Censor (字符串哈希)
  8. 团队作业2——需求分析原型设计
  9. HBASE自带小工具,统计表的行数
  10. python mysql创建表日期型_python 操作mysql,按照当前时间建立表,无法创建表..
  11. h5 富文本输入框_Html富文本编辑器
  12. vue汉王签字板_汉王ESP370U驱动下载|汉王签字板ESP370U驱动(附控件接口) - 驱动无忧...
  13. iOS 封装Healthkit
  14. BIM模型文件下载——江湖别墅Revit模型
  15. oracle表级附加日志视图,Oracle 附加日志(supplemental log)
  16. 基因组代谢网络(GEMS)与全基因组模型从入门到实践系列-----(1)模型构建环境的布置
  17. 用java实现四色定理
  18. 前端实战:React 多页签缓存处理
  19. 遗传算法(确定性排挤)
  20. python groupby apply_python – 使用自己的函数优化groupby.apply

热门文章

  1. tar命令常见用法汇总
  2. pdn阻抗测试_阻抗测试基础(超详细,安捷伦工程师力作)(上篇)
  3. ssm房屋中介管理系统毕业设计(附源码、运行环境)
  4. iphone 扩容测试软件,拯救iPhone 12 64G!闪迪打造的扩容神器上手:轻松省钱
  5. xshell通过隧道连接_如何通过SSH隧道实现远程连接
  6. spark大数据技术与应用 实训
  7. docker上安装多个mysql_docker:安装mysql多个
  8. 「程序猿 DD」星球活动第一期,将在 7 月 9 日晚 20:00 整开放!
  9. 第23个520情人节,女程序猿送男朋友什么?
  10. 盘点下玩过的解谜游戏