自己配置工程配置了好久還是出現各種問題,最後用雷神配置好的工程上跑代碼。
基於tutorial的第一個簡單demo在雷神配置好的ffmpeg+SDL工程上順利通過,因爲tutorial上的demo使用的API有許多已經被刪除或修改了,所以需要對照新的API稍作一些修改,一下是tutorial的第一個小程序的流程圖和代碼
流程圖:
代碼:
#include <stdio.h> #define __STDC_CONSTANT_MACROS #ifdef _WIN32 //Windows extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/imgutils.h" #include "SDL2/SDL.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <SDL2/SDL.h> #include <libavutil/imgutils.h> #ifdef __cplusplus }; #endif #endif //Output YUV420P data as a file #define OUTPUT_YUV420P 0 void SaveFrame(AVFrame*,int,int,int); int main(int argc, char *argv[]) { av_register_all();//註冊文件格式和庫 char* Filename = "bigbuckbunny_480x272.h265"; AVFormatContext *pFormatCtx;//文件頭部格式信息 avformat_network_init(); pFormatCtx = avformat_alloc_context(); if(avformat_open_input(&pFormatCtx, Filename, NULL, NULL)!=0) return -1; //根據頭部尋找數據流信息,填到pFormatCtx->streams if(av_find_stream_info(pFormatCtx)<0) return -1; int i; int videoStream = -1; for(i = 0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){ videoStream = i; break; } if(videoStream == -1) return -1; //指向流context的指針,裏面包含流的解碼信息 AVCodecContext *pCodecCtx; pCodecCtx = pFormatCtx->streams[videoStream]->codec; //找到並相應解碼器 AVCodec *pCodec; pCodec= avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL){ fprintf(stderr, "Unsupported codec!\n"); return -1; } //打開解碼器 if(avcodec_open2(pCodecCtx,pCodec, NULL)<0) return -1; //需要申請保存幀的buffer AVFrame *pFrame, *pFrameRGB; pFrame = avcodec_alloc_frame(); pFrameRGB = avcodec_alloc_frame(); if(pFrameRGB == NULL) return -1; //手動申請一個臨時保存的buffer,這個要知道大小 uint8_t *buffer; int numBytes; numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); //這個av_malloc保證字節對齊 buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); //AVframe繼承自(可能不是class實現,類似)AVPicture //將PFrameRGB與buffer聯繫到一起 avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); //下面開始從流中讀取數據 int frameEnd; AVPacket packet; struct SwsContext *img_convert_ctx;//格式轉換使用 i = 0; while(av_read_frame(pFormatCtx, &packet)>=0){ //is this a packet from the video stream? if(packet.stream_index==videoStream){ //decode video frame avcodec_decode_video2(pCodecCtx,pFrame,&frameEnd,&packet); } //讀取一幀 if(frameEnd){ img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); if(img_convert_ctx == NULL){ fprintf(stderr, "Cannot initialize the conversion context!\n"); exit(1); } sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameRGB->data, pFrameRGB->linesize); if(++i<=5) //對數據進行操作 SaveFrame(pFrameRGB,pCodecCtx->width, pCodecCtx->height, i); } } av_free(buffer); av_free(pFrameRGB); av_free(pFrame); avcodec_close(pCodecCtx); av_close_input_file(pFormatCtx); av_free_packet(&packet); return 0; } void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; char szFilename[32]; int y; //openfile sprintf(szFilename, "frame%d.ppm", iFrame); pFile = fopen(szFilename, "wb"); if(pFile == NULL) return; //write header fprintf(pFile, "P6\n%d %d\n255\n", width, height); //write pixel data for(y=0; y<height; y++) fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile); //close file fclose(pFile); }