日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

学习记录:RGBA格式数据加边框

發布時間:2023/12/15 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 学习记录:RGBA格式数据加边框 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

項目場景:

最近學習使用ffmpeg在做一個視頻縮略圖的例子。在這里做一個記錄。 在網上找了很多資料,加邊框都是更改圖片外圍數據,這樣圖片會缺失一部分,不太符合我的需求。 按照這個思路,下面是一種實現在原始圖片的基礎上,在外部添加一個框。

參考大神的文章:https://blog.csdn.net/leixiaohua1020/article/details/15811977


需求描述:

  • 需要獲取指定時間點處的視頻縮略圖。
  • 縮略圖需要保持原有的長寬比列,縮放至480x360,缺失的部分使用純色填充。

實現計劃:

  • 根據視頻的分辨率和要求的480x360計算處,縮放后的視頻分辨率。
  • 視頻需要解復用并且解碼視頻,使用ffmpeg直接軟解。
  • 視頻需要縮放,考慮過avfilter過濾器,但是部分圖片縮放后會花屏【還沒有找到問題原因】。暫時先使用swscale來實現。
  • 拿到縮放后的數據,分辨率可能不完全等于480x360,這個時候需要在數據外部填充一些純色。

  • 具體實現:

    1.計算縮放后的視頻分辨率

    //視頻原始大小 m_VideoWidth = pCodecCtx->width; m_VideoHeight = pCodecCtx->height;//calculate //m_width 和 m_height 是用戶規定的大小,這里設置為480x360 //m_SwsWidth 和 m_SwsHeight是視頻縮放后的大小 if (m_VideoWidth <= m_width && m_VideoHeight <= m_height) {m_SwsWidth = m_VideoWidth;m_SwsHeight = m_VideoHeight; }else if(m_VideoWidth <= m_width && m_VideoHeight > m_height) {float cc_h = (double)m_VideoHeight / m_height;m_SwsHeight = m_height;m_SwsWidth = m_VideoWidth / cc_h; }else if(m_VideoWidth > m_width && m_VideoHeight <= m_height) {float cc_w = (double)m_VideoWidth / m_width;m_SwsWidth = m_width;m_SwsHeight = m_VideoHeight / cc_w; }else{//m_VideoWidth > m_width && m_VideoHeight > m_heightfloat cc_w = (double)m_VideoWidth / m_width;float cc_h = (double)m_VideoHeight / m_height;if(cc_w >= cc_h){m_SwsWidth = m_width;m_SwsHeight = m_VideoHeight / cc_w;}else{m_SwsHeight = m_height;m_SwsWidth = m_VideoWidth / cc_h;} }printf("video:width = %d, height = %d.\n", m_VideoWidth, m_VideoHeight); printf("user:width = %d, height = %d.\n", m_width, m_height); printf("SWS:width = %d, height = %d.\n", m_SwsWidth, m_SwsHeight);

    2.由于我使用的是ffmpeg 4.3.2版本,所以解碼過程如下:

    AVPacket packet; AVFrame *frame;frame = av_frame_alloc();/* read all packets */while (1) {if ((ret = av_read_frame(fmt_ctx, &packet)) < 0)break;if (packet.stream_index == video_stream_index) {ret = avcodec_send_packet(dec_ctx, &packet);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");break;}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");goto end;}//這里我們拿到了一包解碼后的數據frame.av_frame_unref(frame);}}av_packet_unref(&packet);}

    3.使用swscale進行格式轉換和縮放。

    AVFrame *pRgba = av_frame_alloc();int video_size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, m_SwsWidth, m_SwsHeight, 1); uint8_t * picture_buf = (uint8_t *)av_malloc(video_size); //picture_buf 指向pRgba的data av_image_fill_arrays(pRgba->data, pRgba->linesize, picture_buf, AV_PIX_FMT_RGBA, m_SwsWidth, m_SwsHeight, 1); //視頻像素數據格式轉換上下文 SwsContext *sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, /*INPUT W H FMT*/m_SwsWidth, m_SwsHeight, AV_PIX_FMT_RGBA, /*OUTPUT W H FMT*/SWS_BICUBIC, /*拉伸算法*/NULL, NULL, NULL); /*其他參數*/ //將AVFrame轉成視頻像素數YUV420P格式 sws_scale(sws_ctx, (const uint8_t * const *)frame->data, frame->linesize,0, dec_ctx->height, pRgba->data, pRgba->linesize); sws_freeContext(sws_ctx); av_frame_free(&pRgba);

    4.拿到picture_buf后,需要進行判斷并填充。測試視頻為3840x2160
    了解rgba數據格式,參考:[FFmpeg] RGBA 和 YUV 存儲方式

    • m_width == m_SwsWidth && m_height == m_SwsHeight:
      這個是最簡單的,不需要任何處理。

    • m_width == m_SwsWidth && m_height > m_SwsHeight:
      寬度相同,但是高度不匹配,需要對上下分別填充 m_width * (m_height - m_SwsHeight)/2 邊框。

    /*** -------------* | top |* -------------* -------------* | pic |* -------------* -------------* | buttom |* -------------*/ int diff_h = m_height - m_SwsHeight; int diff_top = diff_h/2; int diff_buttom = diff_h - diff_top; //這里是防止diff_h 是一個奇數。int pos = 0; //用來標記當前行的位置 //create top for(int i = 0; i < diff_top; i++) {pos = i * m_width * 4;for(int j = 0; j < m_width; j++){m_picture_buf2[pos + j * 4 + 0] = 235;//Rm_picture_buf2[pos + j * 4 + 1] = 142;//Gm_picture_buf2[pos + j * 4 + 2] = 85;//Bm_picture_buf2[pos + j * 4 + 3] = 0;//A 0:完全透明 ===>> 1:完全不透明} } //original buf memcpy(picture_buf2 + diff_top * m_width * 4, picture_buf, m_SwsWidth * m_SwsHeight * 4);//create buttom for(int i = diff_top + m_SwsHeight; i < m_height; i++) {pos = i * m_width * 4;for(int j = 0; j < m_width; j++){m_picture_buf2[pos + j * 4 + 0] = 235;//Rm_picture_buf2[pos + j * 4 + 1] = 142;//Gm_picture_buf2[pos + j * 4 + 2] = 85;//Bm_picture_buf2[pos + j * 4 + 3] = 0;//A 0:完全透明 ===>> 1:完全不透明} }

    原圖縮放后:

    加上邊框后:

    • m_width > m_SwsWidth && m_height == m_SwsHeight:
      高度相同,但是寬度不匹配,需要對左右分別填充 (m_width - m_SwsWidth )/2 * m_height 邊框。
    /*** |--left--||--pic--||--right--|*/ int diff_w = m_width - m_SwsWidth; int diff_left = diff_w/2; int diff_right = diff_w - diff_left; int pos = 0;//原圖的位置 int pos_line = 0;//行位置for(int i = 0; i < m_height; i++) {pos_line = i * m_width * 4;for(int j = 0; j < m_width; j++){if(j < diff_left || j >= (diff_left + m_SwsWidth)){m_picture_buf2[pos_line + j * 4 + 0] = 235; //Rm_picture_buf2[pos_line + j * 4 + 1] = 142; //Gm_picture_buf2[pos_line + j * 4 + 2] = 85; //Bm_picture_buf2[pos_line + j * 4 + 3] = 0; //A}else{m_picture_buf2[pos_line + j * 4 + 0] = picture_buf[pos ++];//Rm_picture_buf2[pos_line + j * 4 + 1] = picture_buf[pos ++];//Gm_picture_buf2[pos_line + j * 4 + 2] = picture_buf[pos ++];//Bm_picture_buf2[pos_line + j * 4 + 3] = picture_buf[pos ++];//A}} }
    • m_width > m_SwsWidth && m_height > m_SwsHeight:
      寬度和高度不匹配,需要對上下m_width * (m_height - m_SwsHeight)/2 邊框,左右 (m_width - m_SwsWidth )/2 * m_SwsHeight。
    /*** |----------------------top-----------------------|* |--left--||------------pic------------||--right--|* |-------------------buttom-----------------------|*/ //top & buttom 's height int diff_h = m_height - m_SwsHeight; int diff_top = diff_h/2; int diff_buttom = diff_h - diff_top; //left & right 's width int diff_w = m_width - m_SwsWidth; int diff_left = diff_w/2; int diff_right = diff_w - diff_left; int pos = 0; int pos_line = 0;for(int i = 0; i < m_height; i++) {pos_line = i * m_width * 4;for(int j = 0; j < m_width; j++){if(j < diff_left || j >= (diff_left + m_SwsWidth) || i < diff_top || i >= (diff_top + m_SwsHeight)){m_picture_buf2[pos_line + j * 4 + 0] = 235; //Rm_picture_buf2[pos_line + j * 4 + 1] = 142; //Gm_picture_buf2[pos_line + j * 4 + 2] = 85; //Bm_picture_buf2[pos_line + j * 4 + 3] = 0; //A}else{m_picture_buf2[pos_line + j * 4 + 0] = picture_buf[pos ++];//Rm_picture_buf2[pos_line + j * 4 + 1] = picture_buf[pos ++];//Gm_picture_buf2[pos_line + j * 4 + 2] = picture_buf[pos ++];//Bm_picture_buf2[pos_line + j * 4 + 3] = picture_buf[pos ++];//A}} }

    總結

    以上是生活随笔為你收集整理的学习记录:RGBA格式数据加边框的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。