git常用命令说明
下载地址: https://git-scm.com/download
在线文档: https://git-scm.com/docs
设置参数:
设置用户名/邮件地址:
$ git config --global user.name "[name]"
$ git config --global user.email "[email address]"
--global用于指定全局范围,不使用本参数则本项目中有效,并且项目中的设置会覆盖全局设置。
如果不指定设置值,则为查询
查看已设置参数清单:
$ git config --list
关闭回车换行自动转换: 一些未工程文件打开后会显示修改状态(实际上并未修改)
$ git config core.autocrlf false
true - Git会将你add的所有文件视为文本,将结尾的CRLF转换为LF,而checkout时会再将文件的LF格式转为CRLF格式。
false - 不做任何改变,文本文件保持其原来的样子(建议此选项)。
input - Git在add时把CRLF转换为LF,check时仍旧为LF,Windows操作系统不建议设置此值。
关闭文件权限检查: 将Linux文件权限的改变,也视为文件修改(根据需要设置)
$ git config core.filemode false
设置远程版本库:
git remote set-url origin http://url
添加多个版本库:
git remote add origin2 http://url
删除多余版本库:
git remote remove origin2
从远程仓库下载:
git clone https://e.coding.net/oroct/code/example.git
添加文件: 至仓库,可使用通配符
git add filelist
添加所有修改过的文件:
git add -u
提交至本地:
git commit -am "提交的信息"
-m 后跟提交的备注信息
-a 相当于add,添加修改过的文件,但不会添加新增的文件
--amend可以修改最近一次提交,如果忘记添加某个文件,可以先add,再--amend即可。
从远端仓库拉取最新版本:
git pull
推送至远端仓库:
git push
指定仓库别名(多个仓库时)和分支
git push -u origin master
查看仓库当前的状态:
git status
# 版本已是最新
Your branch is up to date with 'origin/master'.
# 远端代码有2个更新(关键词behind),需要执行拉取动作
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
# 本机代码有1个提交未同步到远端(关键词ahead)
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
删除工作区文件:
git rm <filename> -f
-r 如果删除的是目录,则需要指定参数
-f 表示同时从磁盘也删除文件
-c 表示只删除暂存区,保留文件在磁盘
移动或重命名工作区文件:
git mv <oldname> <newname>
版本回滚:
$ git reset HEAD # 放弃所有修改,回滚到最近提交的状态
$ git reset HEAD^ # 回退所有内容到上一个版本
$ git reset HEAD^ hello.c # 回退 hello.c到上一个版本
$ git reset 052e # 回退到指定版本 hashid
--soft: 保留工作目录中的修改,并把重置版本所带来的新的差异放进暂存区(用于提交)
--hard: 撤销工作区中所有未提交的修改内容,将暂存区与工作区都回到上一次版本,并删除之前的所有信息提交(Warnin),(改乱时用)
--mixed: 缺省参数,重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变(也就是撤回提交动作)。
删除未跟踪的目录和文件:
git clean -d -f
-f 强制删除
-d 删除目录
-x 删除忽略列表的文件(X表示只删除忽略的文件)
git diff 比较文件的不同,即暂存区和工作区的差异。
Changes not staged for commit:
合并多个提交: hash_id是不需要合并的版本,将在此之前的全部版本合并
git rebase -i hash_id
或者合并最近的N个版本:
git rebase -i HEAD~3
合并到第1个版本:
git rebase -i --root
该命令会进入vi编辑模式,将需要合并的提交记录的pick值修改改为squash(或者s),
squash意思是合并到前边commit中,
需要保留最上边那条记录为pick,后边的可任意p(保留)或者s(与上边的一条合并)
根据需要修改提交注释(可选项)
:wq 保存退出,进入下一个编辑界面,保存退出。
推送至远端: 需要加加-f参数
git push -f
建议: 只合并本地未push的提交,已经push至远端的不要再合并,否则会坑队友。
合并提交时到最一个时,也可以使用reset命令:
git reset --soft HEAD^
git commit --amend
git push -f
修改提交信息:
修改最后一次提交信息:
git commit --amend
修改提交的作者信息:
git commit --amend --author "admin <webmaster.oroct.com>" --no-edit
修改历史提交信息:
git rebase -i HEAD~10
# 将所要修改的提交由pick修改为edit
git commit --amend
# 修改相关内容
git rebase --continue
HEAD用法:
HEAD 表示当前版本
HEAD^ 上一个版本
HEAD^^ 上上一个版本
HEAD^^^ 上上上一个版本
以此类推...
HEAD~0 表示当前版本
HEAD~1 上一个版本
HEAD^2 上上一个版本
HEAD^3 上上上一个版本
以此类推...
创建分支:
git branch <branchname>
切换分支:
git checkout <branchname>
-b表示创建分支并切换至该分支
也可以用switch命令:
git switch <branchName>
-c表示创建分支并切换至该分支
删除本地分支:
git branch -d <branchname>
创建远程分支: 直接推过去就行
git push --set-upstream origin release
删除远程分支:
git push origin --delete <branchname>
删除本地的远程分支:
git branch -r -D origin/<branchname>
查看分支:
$ git branch
-a 显示全部分支
-r 只显示远程分支
-l 只显示本地分支(默认)
合并分支: 先切换到master分支,拉取最新内容(多人合作时),执行合并操作,(合并完成后可以手动删除分支)
$ git checkout master
$ git pull origin master
$ git merge <branchname>
$ git push origin master
正常工作时,可从主分支检出一个分支,在此基础上多次修改提交,
在功能验证完成后,把分支修改过的提交rebase成为一次提交(不要rebase之前的提交)
再合并分支,主分支只有一次提交的记录(除了了中间态无用的修改内容)
导出工程:
$ git archive --output ../test.tar.gz master
放弃本地所有修改:
$ git reset --hard
放弃本地所有修改,重新从服务器拉取:
$ git fetch --all
$ git reset --hard origin/master
$ git pull
放弃当前修改,重新检出文件: 如果后连跟的是分支名称,则只切换分支(保留修改内容)
git checkout <filename>
开发分支合并:
对于在开发过程中并未完善的版本,可以通过开发分支进行提交,待功能完成后,先rebase合并相关提交,再通过merge命令合并分支。
release版本发布:
创建一个dev分支用于开发
git branch dev
通过cherry-命令将dev最新的分支合并过来即可
git cherry-pick dev
也可以通过指定hash值来合并指定的提交
git cherry-pick a7d0bf8c
设置RSA登陆模式:
创建ssh密钥对:
$ ssh-keygen -t rsa -C "webmaster@oroct.com"
邮箱名称可以任意填写,没有任何关联
此命令将产生id_rsa.pub(公钥,可以公开发布)及ssh_ras(私钥)文件,可以将这两个文件保存起来用在不同地机器上。
登陆git管理页面,添加公钥即可。
创建标签(tag):
git tag -a "标签标题" -m "详细说明"
创建历史标签:
git tag -a v1.2 9fceb02
查看已有标签:
git tag
推送标签:
git push --tags
Linux系统应用--常用配置文件管理
sudo
/etc/sudoers用于配置sudo用户
# 免输入密码,关键字 NOPASSWD
%sudo ALL=(ALL:ALL) NOPASSWD:ALL
# 其中%sudo 表示用户组的权限
# 从指定目录加载脚本
# includedir /etc/sudoers.d
添加动态库路径:
在/etc/ld.so.conf.d/目录下新建文件ffmpeg.conf,添加库文件的路径:
/usr/local/ffmepg/lib
重新载入:
$ sudo ldconfig
msys2应用笔记
使用系统环境变量:
修改安装目录下的msys2_shell.cmd,去掉MSYS2_PATH_TYPE那一行的注释
set MSYS2_PATH_TYPE=inherit
修改软件源: 前缀都一样根据扩展名修改后缀即可
编辑 /etc/pacman.d/mirrorlist.mingw32 ,在文件开头添加:
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686
编辑 /etc/pacman.d/mirrorlist.mingw64 ,在文件开头添加:
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64
编辑 /etc/pacman.d/mirrorlist.ucrt64 ,在文件开头添加:
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/ucrt64
编辑 /etc/pacman.d/mirrorlist.clang64 ,在文件开头添加:
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/clang64
编辑 /etc/pacman.d/mirrorlist.msys ,在文件开头添加:
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch
然后执行 pacman -Sy 刷新软件包数据即可。
更新软件源:
$ pacman -Sy
$ pacman -Syu
$ pacman -Su
安装软件:
$ pacman -S mingw-w64-x86_64-gcc
查找软件:
$ pacman -Sl | grep gcc
QT5开发笔记(22)--ffmpeg解码音视频
操作系统:Windows 10
软件版本:Qt5.12
关于ffmpeg的编译及应用参考相关文章
修改.pro文件: 指定库文件及头位置
INCLUDEPATH+=$$PWD\ffmpeg\include
LIBS += $$PWD\ffmpeg\lib\libav*.dll.a
# 也可以引用静态库
# LIBS += $$PWD\ffmpeg\bin\av*.lib
测试代码: 发布时需要将ffmpeg的dll动态库文件一起打包
#include <QDebug>
extern "C"{
#include <libavcodec/avcodec.h>
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
unsigned version = avcodec_version();
qDebug() << "version:" << version;
return 0;
}
音频解码:
unsigned int i;
AVFormatContext *pFormatCtx;
AVCodecContext *pCodeCtx;
AVCodec *pCodec;
AVPacket *packet;
AVFrame *frame;
SwrContext *swrCtx;
int audio_stream_idx = -1;
int out_channel_nb;
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; // 输出的采样格式 16bit PCM
int out_sample_rate = 44100; // 输出的采样率
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; // 输出的声道布局
// 分配封装格式内存
pFormatCtx = avformat_alloc_context();
// 打开音频文件
if (avformat_open_input(&pFormatCtx, "rain.mp3", NULL, NULL) != 0)
{
return;
}
// 获取音频信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
return;
}
// 查找音频/视频流
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
// 判断是否是音频格式(codecpar取代了codec)
if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audio_stream_idx = i;
// 初始化音频解码器
}
// 判断是否是视频格式
else if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
//添加视频解码器
}
else
{
}
}
初始化音频解码器:
qDebug() << "音频时长" << av_format->streams[i]->duration;
qDebug() << "采 样 率" << av_format->streams[i]->codecpar->sample_rate;
avcodec_parameters_to_context(pCodeCtxA,av_format->streams[audio_stream_idx]->codecpar);
AVCodec *pCodec = avcodec_find_decoder(pCodeCtxA->codec_id);
if (avcodec_open2(pCodeCtxA, pCodec, NULL) < 0)
{
qDebug("打开音频解码器失败");
}
// 通过声道布局获取声道数
out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
out_sample_rate = pCodeCtxA->sample_rate; // 输出的采样率
out_ch_layout = AV_CH_LAYOUT_STEREO; // 输出的声道布局(立体声)
out_sample_fmt = AV_SAMPLE_FMT_S16; // 输出的采样格式 16bit PCM
int out_sample_byte = av_get_bytes_per_sample(out_sample_fmt);
// 设置解码器选项--------------------------------------------------
audio_swr_ctx = swr_alloc();
swr_alloc_set_opts(audio_swr_ctx, // 转换器上下文
out_ch_layout, // 输出的声道布局
out_sample_fmt, // 输出的采样格式 16bit PCM
pCodeCtxA->sample_rate, // 输出的采样率(与输入相同)
pCodeCtxA->channel_layout, // 输入的声道布局
pCodeCtxA->sample_fmt, // 输入的采样格式
pCodeCtxA->sample_rate, // 输入的采样率
0, // 日志偏移 log_offset
NULL); // 日志指针 log_ctx
swr_init(audio_swr_ctx); // 初始化解码器
// -----------------------------------------------------
// -----------------------------------------------------
// 申请单个采样点PCM数据所需要的内存(2*2*44100)
// 采样率 * 通道号 * 每个样本的字节数(16bit为2个字节)
audio_buff_size = out_channel_nb * out_sample_rate * out_sample_byte;
audio_outbuf = (uint8_t *)av_malloc(audio_buff_size);
if (audio_outbuf == NULL)
{
exit(1);
}
qDebug("audio buff size %d\r\n", audio_buff_size);
// -----------------------------------------------------
// 设置声音输出参数
QAudioFormat fmt;
fmt.setSampleRate(44100); // 设置采样率(一般都是44.1KHz)
fmt.setChannelCount(2); // 通道数
fmt.setSampleSize(16); // 样本数据位数(16bit)
fmt.setSampleType(QAudioFormat::UnSignedInt); // 数据类型
fmt.setByteOrder(QAudioFormat::LittleEndian); // 数据格式
fmt.setCodec("audio/pcm");
pAudioOut = new QAudioOutput(fmt);
pcmDev = pAudioOut->start();
// 申请数据内存用取数据
packet = (AVPacket *)av_malloc(sizeof(AVPacket));
// 申请数据内存:解码数据
frame = av_frame_alloc();
// 逐帧读取压缩的音视频数据AVPacket
while (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == audio_stream_idx)
{
// 解码:AVPacket->AVFrame
if (avcodec_send_packet(pCodeCtx, packet) != 0)
{
}
if(avcodec_receive_frame(pCodeCtx, frame) == 0)
{
swr_convert(swrCtx,
&audio_buff , // 输出数据指针
audio_buff_size , // 输出缓冲区大小
(const uint8_t **)frame->data, // 输入数据指
frame->nb_samples); // 每通道的采样数(帧数)
// 通过帧数,音频格式,通道数计算每次转换后输出的字节数
// 理论上应该每次都相同,但最后一包可能会不同
int out_buffer_size = av_samples_get_buffer_size(NULL, // 传出值,与返回值相同
out_channel_nb, // 通道数
frame->nb_samples, // 采样数(帧数)
out_sample_fmt, // 音频格式
1); // 对齐模式(1 - 紧凑,不对齐)
// 转码后的数据写入文件或者写入PCM进行播放
while(audioOutput->bytesFree() < out_buffer_size)
{
msleep(10);
}
device->write((char*)audio_buff,out_buffer_size);
}
}
av_packet_unref(packet); //av_free_packet(packet);
}
// 释放动态申请的内存
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrCtx);
avcodec_close(pCodeCtx); // 关闭解码器
avformat_close_input(&pFormatCtx); // 关闭输入文件
视频解码: 视频解码与音频解码步骤差不多
AVCodecContext *pCodeCtxV;
AVCodec *pCodecV;
AVFrame *vedio_frame;
SwsContext *img_convert_ctx;
uint8_t *vedio_buff;
// 查找解码器
pCodeCtxV = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodeCtxV,pFormatCtx->streams[video_stream_idx]->codecpar);
pCodecV = avcodec_find_decoder(pCodeCtxV->codec_id);
// 打开解码器
if (avcodec_open2(pCodeCtxV, pCodecV, NULL) < 0)
{
qDebug("打开视频解码器失败");
}
// 分配内存用于保存帧内容
vedio_frame = av_frame_alloc();
int av_img_size =av_image_get_buffer_size(AV_PIX_FMT_RGB32, //AV_PIX_FMT_YUV420P,
pCodeCtxV->width,
pCodeCtxV->height,
1);
vedio_buff = (unsigned char *) av_malloc(av_img_size);
av_image_fill_arrays(vedio_frame->data,
vedio_frame->linesize,
vedio_buff,
AV_PIX_FMT_RGB32,//AV_PIX_FMT_YUV420P,
pCodeCtxV->width,
pCodeCtxV->height,
1);
// 设置转换输出为RGB32格式
img_convert_ctx = sws_getContext(pCodeCtxV->width,
pCodeCtxV->height,
pCodeCtxV->pix_fmt,
pCodeCtxV->width,
pCodeCtxV->height,
AV_PIX_FMT_RGB32,//AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
//---------------------------------------------------
while (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == video_stream_idx)
{
avcodec_send_packet(pCodeCtxV, packet);
if(avcodec_receive_frame(pCodeCtxV, frame) == 0)
{
// 格式转换为RGB32格式(前边设置的转换格式)
sws_scale(img_convert_ctx,
(const uint8_t *const *)frame->data,
frame->linesize,
0,
pCodeCtxV->height,
vedio_frame->data,
vedio_frame->linesize);
// 将图像放入队列中(在主线程中刷新显示,因为需要按帧率更新)
// 注意:一定要QPixmap格式,QImage会造成播放速度过快
QImage image((uchar *)video_outbuf,pCodeCtxV->width,pCodeCtxV->height,QImage::Format_RGB32);
video_list.append(image);
}
}
else if (packet->stream_index == audio_stream_idx)
{
}
}
通过Label显示: 定时调用
if(!video_list.isEmpty())
{
QImage image = video_list.first().scaled(label->size());
// 此处可加入添加水印等图像处理操作
label->setPixmap(QPixmap::fromImage(image));
image_list.removeFirst();
}
获取当前播放(解码)的位置:
if(avcodec_receive_frame(pCodeCtxV, av_frame) == 0)
{
qDebug() <<"timestamp" << av_frame->best_effort_timestamp * av_q2d(av_format->streams[video_stream_idx]->time_base);
}
跳到指定位置:
跳转到距离时间戳最近的关键帧位置,第2个参数是音视频索引, 如果设置 -1 , 则所有的媒体流同时跳转,跳转后记得清空解码队列;
int64_t seek = 10* 1000 * 1000; // 单位是微秒
av_seek_frame(av_format, -1, seek, AVSEEK_FLAG_BACKWARD);