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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

mp4v2再学习 -- H264视频编码成MP4文件

發布時間:2025/3/15 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mp4v2再学习 -- H264视频编码成MP4文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、H264視頻編碼成MP4文件

參看:H264視頻編碼成MP4文件 參看:mp4v2在VS2010下的編譯與在項目中的使用 最近做項目需要將H264文件封裝為mp4文件,從網上找到了MP4V2庫,下載下來后不知道從何下手,官方網站https://code.google.com/p/mp4v2/在windows下的編譯過程介紹的很簡短,對剛剛開始使用VS2010做大型項目的人來說,實在是有些無力。于是從網上找到了下面幾篇博客文章,親測可用,留下來以備查看。

(1)mp4v2 庫在VS2017下的編譯過程

? ? 注:此段內容參考:編譯MP4v2
Mp4v2 一開始是與 mpeg4ip 這個開源項目捆綁在一起的,現在已經脫離mpeg4ip以一個單獨的庫存在著。Mp4v2 庫提供了一些 API 用來創建修改 MP4 文件。
源碼在 http://mp4v2.googlecode.com/svn/trunk/,首先新建一個文件夾用來導入源碼,創建文件夾后右鍵 SVN checkout(需要預先在電腦上安裝 TortoiseSVN 客戶端),在 URL ?of repository 中填入http://mp4v2.googlecode.com/svn/trunk/ 這個網址,點擊ok后導入代碼,再把vstudio9.0\libmp4v2\Version.rc 放入 src 文件夾下。

上面的內容其實就是獲取 MP4V2 源碼。上面兩個谷歌網站沒有進入,原因你懂的。可從 CSDN 下載。 下載:mp4v2最新源碼

然后在進入 vs2013 目錄下,用 vs2017 打開 mp4v2.sln 。選擇配置為 release win32,成功后,編譯 libmp4v2。 在 mp4v2-master\buildwin\lib.vs2013\Win32\Release 下會生成 4 個文件,lbmp4v2.lib、libmp4v2.dll、libmp4v2.pdb、libmp4v2.exp。這樣就完成了。



備注: 1. mp4v2 的源碼下載需要用到 SVN 客戶端 TortoiseSVN,官方網站上能下載的是 Linux 平臺下的代碼。 Linux下安裝編譯之前有講,參看:mp4v2再學習 -- mp4v2 安裝說明
2. 此過程在 VS2017 下打開沒有問題。
3. 以后在你的項目中會用到 lbmp4v2.lib、libmp4v2.dll 這兩個文件。

上面的操作有幾處需要注意的: (1)首先選擇配置為 release win32,這個該如何配置? 配置方法:生成->配置管理器,然后將 libmp4v2 配置改為 Release?

(2)問題分析 出現 無法打開包括文件: “corecrt.h”
解決方法,參看:VS2015無法打開包括文件corecrt.h 簡單來說就是按如下圖,把里面的 10.0.15063.0 文件復制一份,重命名成 10.0.10240.0

(2)mp4v2庫在VS2010中的使用

注:此處測試代碼參考:H264視頻編碼成MP4文件,作者代碼寫的很好,贊一個。此處只是把配置過程再詳講一下。 原文如下:
最近需要將H264視頻編碼成MP4格式。研究了一下,一種方法是采用ffmpeg庫,可以先將H264文件解碼,再編碼生成MP4文件,但這種方式效率較低,10M的視頻可能需要幾秒鐘才能完成。另一種方式根據MP4文件協議直接將H264包封裝成MP4格式,由于是直接基于MP4的封裝,因而效率很高。H264可以很方便的封裝成FLV文件,但MP4格式格式相對比較復雜,封裝起來會比較麻煩。由于沒時間研究MP4協議,在Google Code上找到一個開源的MP4編解碼庫Mp4v2(https://code.google.com/p/mp4v2/),通過Mp4v2可以很方便的將H264編碼成MP4格式文件。為了方便使用,基于該庫封裝了一個MP4Encoder類,MP4Encoder封裝的接口如下。目前僅支持將H264文件或數據幀編碼成MP4文件。
[cpp]?view plaincopy
  • class?MP4Encoder????
  • {????
  • public:????
  • ????MP4Encoder(void);????
  • ????~MP4Encoder(void);????
  • public:????
  • ????//?open?or?creat?a?mp4?file.????
  • ????MP4FileHandle?CreateMP4File(const?char?*fileName,int?width,int?height,int?timeScale?=?90000,int?frameRate?=?25);????
  • ????//?wirte?264?metadata?in?mp4?file.????
  • ????bool?Write264Metadata(MP4FileHandle?hMp4File,LPMP4ENC_Metadata?lpMetadata);????
  • ????//?wirte?264?data,?data?can?contain??multiple?frame.????
  • ????int?WriteH264Data(MP4FileHandle?hMp4File,const?unsigned?char*?pData,int?size);?????
  • ????//?close?mp4?file.????
  • ????void?CloseMP4File(MP4FileHandle?hMp4File);????
  • ????//?convert?H264?file?to?mp4?file.????
  • ????//?no?need?to?call?CreateMP4File?and?CloseMP4File,it?will?create/close?mp4?file?automaticly.????
  • ????bool?WriteH264File(const?char*?pFile264,const?char*?pFileMp4);????
  • ????//?Prase?H264?metamata?from?H264?data?frame????
  • ????static?bool?PraseMetadata(const?unsigned?char*?pData,int?size,MP4ENC_Metadata?&metadata);????
  • };?????
  • 客戶端調用示例代碼: [cpp]?view plaincopy
  • #include?<stdio.h>????
  • #include?"MP4Encoder.h"????
  • ????
  • int?main(int?argc,?char**?argv)????
  • {????
  • ????MP4Encoder?mp4Encoder;????
  • ????//?convert?H264?file?to?mp4?file????
  • ????mp4Encoder.WriteH264File("test.264","test.mp4");????
  • }????
  • MP4Encoder的完整代碼如下:[cpp]?view plaincopy
  • /********************************************************************???
  • filename:???MP4Encoder.h??
  • created:????2013-04-16??
  • author:?????firehood???
  • purpose:????MP4編碼器,基于開源庫mp4v2實現(https://code.google.com/p/mp4v2/)。??
  • *********************************************************************/????
  • #pragma?once????
  • #include?"mp4v2\mp4v2.h"????
  • ????
  • //?NALU單元????
  • typedef?struct?_MP4ENC_NaluUnit????
  • {????
  • ????int?type;????
  • ????int?size;????
  • ????unsigned?char?*data;????
  • }MP4ENC_NaluUnit;????
  • ????
  • typedef?struct?_MP4ENC_Metadata????
  • {????
  • ????//?video,?must?be?h264?type????
  • ????unsigned?int????nSpsLen;????
  • ????unsigned?char???Sps[1024];????
  • ????unsigned?int????nPpsLen;????
  • ????unsigned?char???Pps[1024];????
  • ????
  • }?MP4ENC_Metadata,*LPMP4ENC_Metadata;????
  • ????
  • class?MP4Encoder????
  • {????
  • public:????
  • ????MP4Encoder(void);????
  • ????~MP4Encoder(void);????
  • public:????
  • ????//?open?or?creat?a?mp4?file.????
  • ????MP4FileHandle?CreateMP4File(const?char?*fileName,int?width,int?height,int?timeScale?=?90000,int?frameRate?=?25);????
  • ????//?wirte?264?metadata?in?mp4?file.????
  • ????bool?Write264Metadata(MP4FileHandle?hMp4File,LPMP4ENC_Metadata?lpMetadata);????
  • ????//?wirte?264?data,?data?can?contain??multiple?frame.????
  • ????int?WriteH264Data(MP4FileHandle?hMp4File,const?unsigned?char*?pData,int?size);?????
  • ????//?close?mp4?file.????
  • ????void?CloseMP4File(MP4FileHandle?hMp4File);????
  • ????//?convert?H264?file?to?mp4?file.????
  • ????//?no?need?to?call?CreateMP4File?and?CloseMP4File,it?will?create/close?mp4?file?automaticly.????
  • ????bool?WriteH264File(const?char*?pFile264,const?char*?pFileMp4);????
  • ????//?Prase?H264?metamata?from?H264?data?frame????
  • ????static?bool?PraseMetadata(const?unsigned?char*?pData,int?size,MP4ENC_Metadata?&metadata);????
  • private:????
  • ????//?read?one?nalu?from?H264?data?buffer????
  • ????static?int?ReadOneNaluFromBuf(const?unsigned?char?*buffer,unsigned?int?nBufferSize,unsigned?int?offSet,MP4ENC_NaluUnit?&nalu);????
  • private:????
  • ????int?m_nWidth;????
  • ????int?m_nHeight;????
  • ????int?m_nFrameRate;????
  • ????int?m_nTimeScale;????
  • ????MP4TrackId?m_videoId;????
  • };?????
  • MP4Encoder.cpp[cpp]?view plaincopy
  • /********************************************************************???
  • filename:???MP4Encoder.cpp??
  • created:????2013-04-16??
  • author:?????firehood???
  • purpose:????MP4編碼器,基于開源庫mp4v2實現(https://code.google.com/p/mp4v2/)。??
  • *********************************************************************/????
  • #include?"MP4Encoder.h"????
  • #include?<string.h>????
  • ????
  • #define?BUFFER_SIZE??(1024*1024)????
  • ????
  • MP4Encoder::MP4Encoder(void):????
  • m_videoId(NULL),????
  • m_nWidth(0),????
  • m_nHeight(0),????
  • m_nTimeScale(0),????
  • m_nFrameRate(0)????
  • {????
  • }????
  • ????
  • MP4Encoder::~MP4Encoder(void)????
  • {????
  • }????
  • ????
  • MP4FileHandle?MP4Encoder::CreateMP4File(const?char?*pFileName,int?width,int?height,int?timeScale/*?=?90000*/,int?frameRate/*?=?25*/)????
  • {????
  • ????if(pFileName?==?NULL)????
  • ????{????
  • ????????return?false;????
  • ????}????
  • ????//?create?mp4?file????
  • ????MP4FileHandle?hMp4file?=?MP4Create(pFileName);????
  • ????if?(hMp4file?==?MP4_INVALID_FILE_HANDLE)????
  • ????{????
  • ????????printf("ERROR:Open?file?fialed.\n");????
  • ????????return?false;????
  • ????}????
  • ????m_nWidth?=?width;????
  • ????m_nHeight?=?height;????
  • ????m_nTimeScale?=?90000;????
  • ????m_nFrameRate?=?25;????
  • ????MP4SetTimeScale(hMp4file,?m_nTimeScale);????
  • ????return?hMp4file;????
  • }????
  • ????
  • bool?MP4Encoder::Write264Metadata(MP4FileHandle?hMp4File,LPMP4ENC_Metadata?lpMetadata)????
  • {????
  • ????m_videoId?=?MP4AddH264VideoTrack????
  • ????????(hMp4File,?????
  • ????????m_nTimeScale,?????
  • ????????m_nTimeScale?/?m_nFrameRate,?????
  • ????????m_nWidth,?//?width????
  • ????????m_nHeight,//?height????
  • ????????lpMetadata->Sps[1],?//?sps[1]?AVCProfileIndication????
  • ????????lpMetadata->Sps[2],?//?sps[2]?profile_compat????
  • ????????lpMetadata->Sps[3],?//?sps[3]?AVCLevelIndication????
  • ????????3);???????????//?4?bytes?length?before?each?NAL?unit????
  • ????if?(m_videoId?==?MP4_INVALID_TRACK_ID)????
  • ????{????
  • ????????printf("add?video?track?failed.\n");????
  • ????????return?false;????
  • ????}????
  • ????MP4SetVideoProfileLevel(hMp4File,?0x01);?//??Simple?Profile?@?Level?3????
  • ????
  • ????//?write?sps????
  • ????MP4AddH264SequenceParameterSet(hMp4File,m_videoId,lpMetadata->Sps,lpMetadata->nSpsLen);????
  • ????
  • ????//?write?pps????
  • ????MP4AddH264PictureParameterSet(hMp4File,m_videoId,lpMetadata->Pps,lpMetadata->nPpsLen);????
  • ????
  • ????return?true;????
  • }????
  • ????
  • int?MP4Encoder::WriteH264Data(MP4FileHandle?hMp4File,const?unsigned?char*?pData,int?size)????
  • {????
  • ????if(hMp4File?==?NULL)????
  • ????{????
  • ????????return?-1;????
  • ????}????
  • ????if(pData?==?NULL)????
  • ????{????
  • ????????return?-1;????
  • ????}????
  • ????MP4ENC_NaluUnit?nalu;????
  • ????int?pos?=?0,?len?=?0;????
  • ????while?(len?=?ReadOneNaluFromBuf(pData,size,pos,nalu))????
  • ????{????
  • ????????if(nalu.type?==?0x07)?//?sps????
  • ????????{????
  • ????????????//?添加h264?track????????
  • ????????????m_videoId?=?MP4AddH264VideoTrack????
  • ????????????????(hMp4File,?????
  • ????????????????m_nTimeScale,?????
  • ????????????????m_nTimeScale?/?m_nFrameRate,?????
  • ????????????????m_nWidth,?????//?width????
  • ????????????????m_nHeight,????//?height????
  • ????????????????nalu.data[1],?//?sps[1]?AVCProfileIndication????
  • ????????????????nalu.data[2],?//?sps[2]?profile_compat????
  • ????????????????nalu.data[3],?//?sps[3]?AVCLevelIndication????
  • ????????????????3);???????????//?4?bytes?length?before?each?NAL?unit????
  • ????????????if?(m_videoId?==?MP4_INVALID_TRACK_ID)????
  • ????????????{????
  • ????????????????printf("add?video?track?failed.\n");????
  • ????????????????return?0;????
  • ????????????}????
  • ????????????MP4SetVideoProfileLevel(hMp4File,?1);?//??Simple?Profile?@?Level?3????
  • ????
  • ????????????MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);????
  • ????????}????
  • ????????else?if(nalu.type?==?0x08)?//?pps????
  • ????????{????
  • ????????????MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size);????
  • ????????}????
  • ????????else????
  • ????????{????
  • ????????????int?datalen?=?nalu.size+4;????
  • ????????????unsigned?char?*data?=?new?unsigned?char[datalen];????
  • ????????????//?MP4?Nalu前四個字節表示Nalu長度????
  • ????????????data[0]?=?nalu.size>>24;????
  • ????????????data[1]?=?nalu.size>>16;????
  • ????????????data[2]?=?nalu.size>>8;????
  • ????????????data[3]?=?nalu.size&0xff;????
  • ????????????memcpy(data+4,nalu.data,nalu.size);????
  • ????????????if(!MP4WriteSample(hMp4File,?m_videoId,?data,?datalen,MP4_INVALID_DURATION,?0,?1))????
  • ????????????{????
  • ????????????????return?0;????
  • ????????????}????
  • ????????????delete[]?data;????
  • ????????}????
  • ????????????
  • ????????pos?+=?len;????
  • ????}????
  • ????return?pos;????
  • }????
  • ????
  • int?MP4Encoder::ReadOneNaluFromBuf(const?unsigned?char?*buffer,unsigned?int?nBufferSize,unsigned?int?offSet,MP4ENC_NaluUnit?&nalu)????
  • {????
  • ????int?i?=?offSet;????
  • ????while(i<nBufferSize)????
  • ????{????
  • ????????if(buffer[i++]?==?0x00?&&????
  • ????????????buffer[i++]?==?0x00?&&????
  • ????????????buffer[i++]?==?0x00?&&????
  • ????????????buffer[i++]?==?0x01????
  • ????????????)????
  • ????????{????
  • ????????????int?pos?=?i;????
  • ????????????while?(pos<nBufferSize)????
  • ????????????{????
  • ????????????????if(buffer[pos++]?==?0x00?&&????
  • ????????????????????buffer[pos++]?==?0x00?&&????
  • ????????????????????buffer[pos++]?==?0x00?&&????
  • ????????????????????buffer[pos++]?==?0x01????
  • ????????????????????)????
  • ????????????????{????
  • ????????????????????break;????
  • ????????????????}????
  • ????????????}????
  • ????????????if(pos?==?nBufferSize)????
  • ????????????{????
  • ????????????????nalu.size?=?pos-i;??????
  • ????????????}????
  • ????????????else????
  • ????????????{????
  • ????????????????nalu.size?=?(pos-4)-i;????
  • ????????????}????
  • ????
  • ????????????nalu.type?=?buffer[i]&0x1f;????
  • ????????????nalu.data?=(unsigned?char*)&buffer[i];????
  • ????????????return?(nalu.size+i-offSet);????
  • ????????}????
  • ????}????
  • ????return?0;????
  • }????
  • ????
  • void?MP4Encoder::CloseMP4File(MP4FileHandle?hMp4File)????
  • {????
  • ????if(hMp4File)????
  • ????{????
  • ????????MP4Close(hMp4File);????
  • ????????hMp4File?=?NULL;????
  • ????}????
  • }????
  • ????
  • bool?MP4Encoder::WriteH264File(const?char*?pFile264,const?char*?pFileMp4)????
  • {????
  • ????if(pFile264?==?NULL?||?pFileMp4?==?NULL)????
  • ????{????
  • ????????return?false;????
  • ????}????
  • ????
  • ????MP4FileHandle?hMp4File?=?CreateMP4File(pFileMp4,352,288);????
  • ????
  • ????if(hMp4File?==?NULL)????
  • ????{????
  • ????????printf("ERROR:Create?file?failed!");????
  • ????????return?false;????
  • ????}????
  • ????
  • ????FILE?*fp?=?fopen(pFile264,?"rb");??????
  • ????if(!fp)??????
  • ????{??????
  • ????????printf("ERROR:open?file?failed!");????
  • ????????return?false;????
  • ????}??????
  • ????fseek(fp,?0,?SEEK_SET);????
  • ????
  • ????unsigned?char?*buffer??=?new?unsigned?char[BUFFER_SIZE];????
  • ????int?pos?=?0;????
  • ????while(1)????
  • ????{????
  • ????????int?readlen?=?fread(buffer+pos,?sizeof(unsigned?char),?BUFFER_SIZE-pos,?fp);????
  • ????
  • ????
  • ????????if(readlen<=0)????
  • ????????{????
  • ????????????break;????
  • ????????}????
  • ????
  • ????????readlen?+=?pos;????
  • ????
  • ????????int?writelen?=?0;????
  • ????????for(int?i?=?readlen-1;?i>=0;?i--)????
  • ????????{????
  • ????????????????if(buffer[i--]?==?0x01?&&????
  • ????????????????????buffer[i--]?==?0x00?&&????
  • ????????????????????buffer[i--]?==?0x00?&&????
  • ????????????????????buffer[i--]?==?0x00????
  • ????????????????????)????
  • ????????????????{????
  • ????????????????????writelen?=?i+5;????
  • ????????????????????break;????
  • ????????????????}????
  • ????????}????
  • ????????????
  • ????????writelen?=?WriteH264Data(hMp4File,buffer,writelen);????
  • ????????if(writelen<=0)????
  • ????????{????
  • ????????????break;????
  • ????????}????
  • ????????memcpy(buffer,buffer+writelen,readlen-writelen+1);????
  • ????????pos?=?readlen-writelen+1;????
  • ????}????
  • ????fclose(fp);????
  • ????
  • ????delete[]?buffer;????
  • ????CloseMP4File(hMp4File);????
  • ????
  • ????return?true;????
  • }????
  • ????
  • bool?MP4Encoder::?PraseMetadata(const?unsigned?char*?pData,int?size,MP4ENC_Metadata?&metadata)????
  • {????
  • ????if(pData?==?NULL?||?size<4)????
  • ????{????
  • ????????return?false;????
  • ????}????
  • ????MP4ENC_NaluUnit?nalu;????
  • ????int?pos?=?0;????
  • ????bool?bRet1?=?false,bRet2?=?false;????
  • ????while?(int?len?=?ReadOneNaluFromBuf(pData,size,pos,nalu))????
  • ????{????
  • ????????if(nalu.type?==?0x07)????
  • ????????{????
  • ????????????memcpy(metadata.Sps,nalu.data,nalu.size);????
  • ????????????metadata.nSpsLen?=?nalu.size;????
  • ????????????bRet1?=?true;????
  • ????????}????
  • ????????else?if((nalu.type?==?0x08))????
  • ????????{????
  • ????????????memcpy(metadata.Pps,nalu.data,nalu.size);????
  • ????????????metadata.nPpsLen?=?nalu.size;????
  • ????????????bRet2?=?true;????
  • ????????}????
  • ????????pos?+=?len;????
  • ????}????
  • ????if(bRet1?&&?bRet2)????
  • ????{????
  • ????????return?true;????
  • ????}????
  • ????return?false;????
  • }????
  • 其實講 ffmpeg 時就已經講了VS 開發環境搭建。參看:ffmpeg再學習 -- Windows下安裝說明 好吧,那我再講一遍。。 (1)新建項目 打開 VS;文件->新建->項目->Win32控制臺應用程序->選擇空項目,點擊完成。注意,選擇的位置最好不要有 空格或者漢字。

    (2)拷貝考法文件 頭文件( *.h)拷貝至項目文件夾的 include 子文件夾下
    導入庫文件( *.lib)拷貝至項目文件夾的 lib 子文件夾下
    動態庫文件( *.dll) 拷貝至項目文件夾下

    這三組文件具體是,mp4v2-master文件夾下的 include 文件夾 和?mp4v2-master\buildwin\lib.vs2013\Win32\Release 文件夾下的?libmp4v2.lib 和?libmp4v2.dll

    點擊右鍵,選擇在資源管理器中打開文件夾,進入項目目錄。(注意,如果手動進入注意文件夾位置,我就是沒找好位置,試了半天最后才發現,將上面的這些文件拷貝到錯誤的文件夾下了)

    放置完成即下圖:(3)添加代碼右擊 MP4Encoder 工程,添加->類
    雙擊 C++類 進入 一般 C++類向導,類名寫為 CMP4Encoder ,然后把上面 MP4Encoder.h 和MP4Encoder.cpp 的代碼拷貝到對應的文件中。

    右擊工程?MP4Encoder?->添加->新建項

    選擇C++文件,名稱寫做 main.cpp,位置默認即可,然后將上面的“客戶端調用示例代碼”拷貝到此文件。

    (4)配置開發文件

    打開屬性面板

    解決方案資源管理器->右鍵單擊項目->屬性


    頭文件配置

    配置屬性->C/C++->常規->附加包含目錄,輸入“ include”(剛才拷貝頭文件的目錄)

    導入庫配置

    配置屬性->鏈接器->常規->附加庫目錄,輸入“ lib” (剛才拷貝庫文件的目錄)

    配置屬性->鏈接器->輸入->附加依賴項,輸入?libmp4v2.lib;


    動態庫不用配置


    (5)測試 首先拷貝一個 H264 測試文件 (后綴為.264) 到 MP4Encoder 文件夾下。 測試文件可以從此處,下載:[開源世界]分享H.264視頻文件下載地址?
    注意,需要將該264文件,重命名為 test.h264,否則生成的 MP4 文件大小為1K。 或者你更改主函數:

    然后點擊本地Windows調試器,出現此項目已經過期,選擇 是。
    即可生成 test.mp4 文件,可以用VLC播放器打開看看。

    不過生成文件時會出現,無法查找或打開 PDB 文件。如下圖:

    它對于生成文件沒有影響的。如果看著不舒服,可參看:無法查找或打開 PDB 文件解決辦法 這是我寫的工程文件:MP4Encoder 工程文件

    二、工程文件(可用)

    講了這么多,你發現沒有上面這個例子只是將 H264 視頻編碼成MP4文件,缺少音頻啊親。

    下面這個即音視頻編碼轉MP4文件的例子,下載:MP4v2 -- h264 轉 MP4



    總結

    以上是生活随笔為你收集整理的mp4v2再学习 -- H264视频编码成MP4文件的全部內容,希望文章能夠幫你解決所遇到的問題。

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