概述
OK, 由於項目需要, 需要在嵌入式平台上將264的視頻流解碼出來, 所以用到了FFMPEG。
一、平台搭建
首先,需要搭建好平台, 我用的是2.4版本的FFMPEG。 從官網下載下來的FFMPEG, 解壓出來, 進到目錄后直接可以運行 # ./configure 一般會提示 “yasm/nasm not found or too old. Use --disable-yasm for a crippled build.”, 系統並沒有預先裝yasm, 所以提示出錯。 此時只需要照着提示加個選項 # ./configure --disable-yasm。 后面一般都不會有其它問題,根據典型的安裝,先編譯 #make 后安裝 # sudo make install
二、編寫代碼
前面平台如果搭建都沒什么問題的話,下面可以進行編碼, 其實說是說編碼,也只是參考別人的來改而已, 主要是百度以及參照官網提供的例子程序來進行修改的。 在此,遇到問題多看官網上的說明, 尤其是幫助文檔 點擊打開鏈接。 並且需要找到對應的版本幫助文檔才有用, 網上很多代碼都是老版本的, 有不少方法都更新了,不再是從前那個接口, 所以這點對新手來說要多注意。
OK,閑話不多說,下面貼出代碼, 因為代碼量也不大,沒有對其進行封裝處理,將就着用一下吧。
#include "stdio.h"
#include "stdlib.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/parseutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/fifo.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avstring.h"
#include "libavutil/imgutils.h"
#include "libavutil/timestamp.h"
#include "libavutil/bprint.h"
#include "libavutil/time.h"
#include "libavutil/threadmessage.h"
#include "libavfilter/avcodec.h"
#include "libavcodec/avcodec.h"
#if HAVE_SYS_RESOURCE_H
#include
#include
#include
#elif HAVE_GETPROCESSTIMES
#include
#endif
#if HAVE_GETPROCESSMEMORYINFO
#include
#include
#endif
#if HAVE_SYS_SELECT_H
#include
#endif
#if HAVE_TERMIOS_H
#include
#include
#include
#include
#elif HAVE_KBHIT
#include
#endif
#if HAVE_PTHREADS
#include
#endif
#include
#include "libavutil/avassert.h"
#define MAX_LEN 1024 * 50
此方法參考官網的例子
static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
FILE *f)
{
// FILE *f;
int i;
// f = fopen(filename,"w");
// fprintf(f, "P5n%d %dn%dn", xsize, ysize, 255);
for (i = 0; i < ysize; i++)
fwrite(buf + i * wrap, 1, xsize, f);
// fclose(f);
}
int main()
{
//下面初始化h264解碼庫
//avcodec_init();
av_register_all();
AVFrame *pFrame_ = NULL;
/* find the video encoder */
AVCodec *videoCodec = avcodec_find_decoder(CODEC_ID_H264);//得到264的解碼器類
if(!videoCodec)
{
printf("avcodec_find_decoder errorn");
return -1;
}
AVCodecParserContext *avParserContext = av_parser_init(CODEC_ID_H264);//得到解析幀類,主要用於后面的幀頭查找
if(!avParserContext)
{
printf("av_parser_init errorn");
return -1;
}
AVCodecContext *codec_ = avcodec_alloc_context3(videoCodec);//解碼會話層
if(!codec_)
{
printf("avcodec_alloc_context3 errorn");
return -1;
}
//初始化參數,下面的參數應該由具體的業務決定
codec_->time_base.num = 1;
codec_->frame_number = 1; //每包一個視頻幀
codec_->codec_type = AVMEDIA_TYPE_VIDEO;
codec_->bit_rate = 0;
codec_->time_base.den = 25;//幀率
codec_->width = 352;//視頻寬
codec_->height = 288;//視頻高
if(avcodec_open2(codec_, videoCodec, NULL) >= 0)//打開解碼器
{
pFrame_ = avcodec_alloc_frame();// Allocate video frame 成功打開解碼器后, 此時可以分配幀內存, 當然你也可以在后面每次都分配、釋放, 在此我省功夫, 只在開始分配一次
if (!pFrame_) {
fprintf(stderr, "Could not allocate video framen");
exit(1);
}
}
else
{
printf("avcodec_open2 errorn");
return -1;
}
AVPacket packet = {0};
int dwBufsize = 10;
int frameFinished = dwBufsize;//這個是隨便填入數字,沒什么作用
av_init_packet(&packet);
packet.data = NULL;//這里填入一個指向完整H264數據幀的指針
packet.size = 0;//這個填入H264數據幀的大小
FILE *myH264 = fopen("test3.264", "rb");//解碼的文件264
if(myH264 == NULL)
{
perror("cant open 264 filen");
return -1;
}
FILE *yuvfile = fopen("my264.yuv", "wb");//成功解碼后保存成的YUV文件, 可以用YUV工具打開瀏覽
if(yuvfile == NULL)
{
perror("cant open YUV filen");
return -1;
}
int readFileLen = 1;
char readBuf[MAX_LEN];
unsigned char *parseBuf = malloc(20*MAX_LEN);//這個地方浪費了我一個下午時間, 當時我用的是棧內存,即unsigned char parseBuf[20*MAX_LEN], 結果運行程序一直報錯, 此處需要用堆內存才能正常解碼
int parseBufLen = 0;
int frameCount = 0;
printf("begin...n");
printf("readBuf address is %xn", readBuf);
while(readFileLen > 0)//開始解碼工作
{
//printf("begin...n");
readFileLen = fread(readBuf, 1, sizeof(readBuf), myH264);//首先從文件里讀出數據
if(readFileLen <= 0)
{
printf("read overn");
break;
}
else
{
int handleLen = 0;
int handleFileLen = readFileLen;
while(handleFileLen > 0)
{
int nLength = av_parser_parse2(avParserContext, codec_, &parseBuf, &parseBufLen, readBuf + handleLen, handleFileLen, 0, 0, 0);//查找264幀頭
handleFileLen -= nLength;
handleLen += nLength;
if(parseBufLen <= 0)//當parseBufLen大於0時,說明查找到了幀頭
{
continue;
}
packet.size = parseBufLen;//將查找到的幀長度送入
packet.data = parseBuf;//將查找到的幀內存送入
//printf("parseBuf address is %xn", parseBuf);
while(packet.size > 0)
{//下面開始真正的解碼
int decodeLen = avcodec_decode_video2(codec_, pFrame_, &frameFinished, &packet);
if(decodeLen < 0)
break;
packet.size -= decodeLen;
packet.data += decodeLen;
if(frameFinished > 0)//成功解碼
{
int picSize = codec_->height * codec_->width;
//int newSize = picSize * 1.5;
//申請內存
//unsigned char *buf = malloc(newSize);
int height = pFrame_->height;
int width = pFrame_->width;
//printf("OK, get datan");
//printf("Frame height is %dn", height);
//printf("Frame width is %dn", width);
frameCount ++;
printf("Frame count is %dn", frameCount);
pgm_save(pFrame_->data[0], pFrame_->linesize[0],//保存Y
codec_->width, codec_->height, yuvfile);
pgm_save(pFrame_->data[1], pFrame_->linesize[1],//保存U
codec_->width/2, codec_->height/2, yuvfile);
pgm_save(pFrame_->data[2], pFrame_->linesize[2],//保存V
codec_->width/2, codec_->height/2, yuvfile);
///有了YUV數據, 后面可以用FFMPEG提供的轉換方法,將其轉成RGB數據,進行后續的顯示或其它的圖像處理工作
}
else
printf("failed to decodecn");
}
}
}
}
//釋放工作
avcodec_close(codec_);
av_free(codec_);
av_free_packet(&packet);
av_frame_free(&pFrame_);
fclose(yuvfile);
fclose(myH264);
}
值得一提的是,代碼在開始時遇到一個奇怪的問題, 編譯后運行到解碼方法avcodec_decode_video2時總提示錯誤:[h264 @ 0x1a791e0] no frame! 后來發現是我將一個內存弄成棧而不是堆內存(在代碼注釋中有說明),而FFMPEG卻不對其解碼, 這一點讓我很奇怪, 一定要堆內存么?
三、編譯
編譯說起來其實是一件很容易的事,不過這也弄了快一天的時間, 主要是我第一次用這個FFMPEG的庫, 沒想到它的方法依賴性那么強, 就是庫與庫之前的依賴性比較強, 在編譯過程對引用庫的順序要特別注意, 不然會報很多方法沒有找到的錯。 只要順序搞對, 其它遇到一些方法沒找到時主要是沒引用上相應的庫,那些都好辦。順序一般為 -lavformat -lavcodec -lswresample -lavuti
總結一下, 解碼264原來以為是一件挺難搞的事, 整了兩三天發現其實也挺容易的, 關鍵得多看幫助文檔。 寫到這里, 后面提供我的工程,在工程里帶有用於測試用的264文件。 工程鏈接:點擊打開鏈接 YUV查看工具: 點擊打開鏈接
最后
以上就是优雅猎豹为你收集整理的linux 264,LINUX下用FFMPEG解碼264的全部内容,希望文章能够帮你解决linux 264,LINUX下用FFMPEG解碼264所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复