Linux应用程序--线程


终止同一进程的其他线程:

ret = pthread_cancel(tid);

线程终止回调:

// 注册回调函数
pthread_cleanup_push(task_exit, "acrel_exits");    
// 此处是线程主体
while(1)
{

}
pthread_cleanup_pop(0);

- 阅读全文 -

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);

- 阅读全文 -


Copyright©2024 春天花会开, All Rights Reserved. Email: webmaster@oroct.com