簡介
以前寫了一遍提取MP4中的音視頻而且解碼,這一篇引入SDL2.0來顯示解碼後的視頻序列 實現一個簡易的 視頻播放器。 數組
我這裏用的FFMPEG和SDL2.0都是最新版的 可能網上的資料不是不少,API接口也變了不少,不過大致的思路仍是同樣的。ide
分析幾個FFMPEG函數
在這以前咱們分析幾個代碼中可能引發疑問的FFMPEG幾個函數的源代碼,我已經盡個人能力添加了註釋,由於實在沒有文檔可能有的地方也不是很詳盡 不過大致仍是能看懂的函數
av_image_alloc (分配圖片緩衝區)
咱們在FFMPEG中引用了此函數,下面列舉的函數都是這個函數裏所引用到的 我都 添加了註釋 這裏注意下面的工具
- pointers 參數是一個指針數組 實際上他在初始化完畢以後會被賦值成連續的內存序列 具體看源代碼
- int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
- int w, int h, enum AVPixelFormat pix_fmt, int align)
- {
-
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
-
- int i, ret;
- uint8_t *buf;
-
- if (!desc)
- return AVERROR(EINVAL);
-
- if ((ret = av_image_check_size(w, h, 0, NULL)) < 0)
- return ret;
-
- if ((ret = av_image_fill_linesizes(linesizes, pix_fmt, align>7 ? FFALIGN(w, 8) : w)) < 0)
- return ret;
-
-
- for (i = 0; i < 4; i++)
- linesizes[i] = FFALIGN(linesizes[i], align);
-
- if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, NULL, linesizes)) < 0)
- return ret;
-
- buf = av_malloc(ret + align);
- if (!buf)
- return AVERROR(ENOMEM);
-
- if ((ret = av_image_fill_pointers(pointers, pix_fmt, h, buf, linesizes)) < 0) {
-
- av_free(buf);
- return ret;
- }
-
-
-
- if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
-
- avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt);
-
- return ret;
- }
avpriv_set_systematic_pal2(設置系統調色板)
//設置系統化調色板根據不一樣像素格式 ui
- int avpriv_set_systematic_pal2(uint32_t pal[256], enum AVPixelFormat pix_fmt)
- {
- int i;
-
- for (i = 0; i < 256; i++) {
- int r, g, b;
-
- switch (pix_fmt) {
- case AV_PIX_FMT_RGB8:
- r = (i>>5 )*36;
- g = ((i>>2)&7)*36;
- b = (i&3 )*85;
- break;
- case AV_PIX_FMT_BGR8:
- b = (i>>6 )*85;
- g = ((i>>3)&7)*36;
- r = (i&7 )*36;
- break;
- case AV_PIX_FMT_RGB4_BYTE:
- r = (i>>3 )*255;
- g = ((i>>1)&3)*85;
- b = (i&1 )*255;
- break;
- case AV_PIX_FMT_BGR4_BYTE:
- b = (i>>3 )*255;
- g = ((i>>1)&3)*85;
- r = (i&1 )*255;
- break;
- case AV_PIX_FMT_GRAY8:
- r = b = g = i;
- break;
- default:
- return AVERROR(EINVAL);
- }
- pal[i] = b + (g << 8) + (r << 16) + (0xFFU << 24);
- }
-
- return 0;
- }
av_image_fill_pointers(填充av_image_alloc傳遞的unsigned char** data和linesize)
- int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height,
- uint8_t *ptr, const int linesizes[4])
- {
- int i, total_size, size[4] = { 0 }, has_plane[4] = { 0 };
-
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
-
- memset(data , 0, sizeof(data[0])*4);
-
- if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
- return AVERROR(EINVAL);
-
- data[0] = ptr;
-
- if (linesizes[0] > (INT_MAX - 1024) / height)
- return AVERROR(EINVAL);
-
- size[0] = linesizes[0] * height;
-
- if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
- desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
- {
- size[0] = (size[0] + 3) & ~3;
- data[1] = ptr + size[0];
- return size[0] + 256 * 4;
- }
-
- for (i = 0; i < 4; i++)
- has_plane[desc->comp[i].plane] = 1;
-
- total_size = size[0];
- for (i = 1; i < 4 && has_plane[i]; i++) {
- int h, s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
- data[i] = data[i-1] + size[i-1];
- h = (height + (1 << s) - 1) >> s;
- if (linesizes[i] > INT_MAX / h)
- return AVERROR(EINVAL);
- size[i] = h * linesizes[i];
- if (total_size > INT_MAX - size[i])
- return AVERROR(EINVAL);
- total_size += size[i];
- }
-
- return total_size;
- }
av_image_fill_linesizes(填充行線寬)
- int av_image_fill_linesizes(int linesizes[4], enum AVPixelFormat pix_fmt, int width)
- {
- int i, ret;
-
- const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
- int max_step [4];
- int max_step_comp[4];
-
- memset(linesizes, 0, 4*sizeof(linesizes[0]));
-
- if (!desc || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
- return AVERROR(EINVAL);
-
- av_image_fill_max_pixsteps(max_step, max_step_comp, desc);
- for (i = 0; i < 4; i++) {
- if ((ret = image_get_linesize(width, i, max_step[i], max_step_comp[i], desc)) < 0)
- return ret;
- linesizes[i] = ret;
- }
-
- return 0;
- }
例子 提取MP4文件的視頻,並播放實現簡易視頻播放器
- #include "stdafx.h"
- #define __STDC_FORMAT_MACROS
- #ifdef _CPPRTTI
- extern "C"
- {
- #endif
- #include "libavutil/imgutils.h" //圖像工具
- #include "libavutil/samplefmt.h" // 音頻樣本格式
- #include "libavutil/timestamp.h" //時間戳工具能夠 被用於調試和日誌目的
- #include "libavformat/avformat.h" //Main libavformat public API header 包含了libavf I/O和 Demuxing 和Muxing 庫
- #include "SDL.h"
- #ifdef _CPPRTTI
- };
- #endif
-
- static AVCodecContext *pVideoContext,*pAudioContext;
- static FILE *fVideoFile,*fAudioFile;
- static AVStream *pStreamVideo,*pStreamAudio;
- static unsigned char * videoDstData[4];
- static int videoLineSize[4];
- static int videoBufferSize;
- static AVFormatContext *pFormatCtx=NULL;
- static AVFrame*pFrame=NULL ;
- static AVPacket pkt;
- static int ret=0;
- static int gotFrame;
- static int videoStreamIndex,audioStreamIndex;
- SDL_Window * pWindow = NULL;
- SDL_Renderer *pRender = NULL;
- SDL_Texture *pTexture = NULL;
- SDL_Rect dstrect = {0,0,800,600};
- int frame = 0;
- int indexFrameVideo=0;
- static int decode_packet(int* gotFrame, int param2)
- {
- int ret = 0 ;
-
- int decodedSize=pkt.size ;
-
- *gotFrame=0;
-
- if(pkt.stream_index==videoStreamIndex)
- {
-
-
- if((ret=avcodec_decode_video2(pVideoContext,pFrame,gotFrame,&pkt))<0)
- {
-
- return ret ;
- }
- indexFrameVideo++;
-
- if(*gotFrame)
- {
-
- av_image_copy(videoDstData,videoLineSize, (const uint8_t **)(pFrame->data), pFrame->linesize,pVideoContext->pix_fmt, pVideoContext->width, pVideoContext->height);
-
-
- printf("輸出當前第%d幀,大小:%d\n",indexFrameVideo,videoBufferSize);
- int n = SDL_BYTESPERPIXEL(pStreamVideo->codec->pix_fmt);
-
-
-
- SDL_UpdateTexture(pTexture, &dstrect, (const void*)videoDstData[0], videoLineSize[0]);
-
-
- SDL_RenderCopy(pRender, pTexture,NULL, &dstrect);
-
- SDL_Delay(1000 * 1 / frame);
-
- SDL_RenderPresent(pRender);
- }else
- {
- printf("第%d幀,丟失\n",indexFrameVideo);
- }
- }
-
- else if(pkt.stream_index==audioStreamIndex)
- {
-
- }
-
- av_frame_unref(pFrame);
- return decodedSize ;
- }
-
- int Demuxing(int argc, char** argv)
- {
- if (argc < 4)
- {
- printf("Parameter Error!\n");
- return 0;
- }
-
-
- av_register_all();
-
- avcodec_register_all();
-
- char*pInputFile = argv[1];
-
- char*pOutputVideoFile = argv[3];
-
- char*pOutputAudioFile = argv[2];
-
- pFormatCtx = avformat_alloc_context();
-
- if (avformat_open_input(&pFormatCtx, pInputFile, NULL, NULL) < 0)
- {
- printf("Open Input Error!\n");
- return 0;
- }
-
- if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
- {
- printf("獲取流媒體信息失敗!\n");
- return 0;
- }
-
- av_dump_format(pFormatCtx, 0, pInputFile, 0);
- for (unsigned i = 0; i < pFormatCtx->nb_streams; i++)
- {
- AVStream *pStream = pFormatCtx->streams[i];
- AVMediaType mediaType = pStream->codec->codec_type;
-
- if (mediaType == AVMEDIA_TYPE_VIDEO)
- {
- videoStreamIndex = i;
- pVideoContext = pStream->codec;
- pStreamVideo = pStream;
- fVideoFile = fopen(pOutputVideoFile, "wb");
- frame = pVideoContext->framerate.num;
- if (!fVideoFile)
- {
- printf("con't open file!\n");
- goto end;
- }
-
-
-
- int ret = av_image_alloc(videoDstData, videoLineSize, pVideoContext->width, pVideoContext->height, pVideoContext->pix_fmt, 1);
- if (ret < 0)
- {
- printf("Alloc video buffer error!\n");
- goto end;
- }
-
- videoBufferSize = ret;
- }
- else if (mediaType == AVMEDIA_TYPE_AUDIO)
- {
- audioStreamIndex = i;
- pAudioContext = pStream->codec;
- pStreamAudio = pStream;
- fAudioFile = fopen(pOutputAudioFile, "wb");
- if (!fAudioFile)
- {
- printf("con't open file!\n");
- goto end;
- }
-
- pFrame = av_frame_alloc();
- if (pFrame == NULL)
- {
- av_freep(&videoDstData[0]);
- printf("alloc audio frame error\n");
- goto end;
- }
- }
- AVCodec *dec;
-
- dec = avcodec_find_decoder(pStream->codec->codec_id);
- if (dec == NULL)
- {
- printf("查找編碼器失敗!\n");
- goto end;
- }
- if (avcodec_open2(pStream->codec, dec, nullptr) != 0)
- {
- printf("打開編碼器失敗!\n");
- goto end;
- }
-
- }
- av_init_packet(&pkt);
- pkt.data = NULL;
- pkt.size = 0;
-
-
- while (av_read_frame(pFormatCtx, &pkt) >= 0)
- {
- AVPacket oriPkt = pkt;
- do
- {
-
- ret = decode_packet(&gotFrame, 0);
- if (ret < 0)
- break;
-
- pkt.data += ret;
- pkt.size -= ret;
-
- } while (pkt.size > 0);
-
- av_free_packet(&oriPkt);
- }
-
- end:
-
- avcodec_close(pVideoContext);
-
- avcodec_close(pAudioContext);
- avformat_close_input(&pFormatCtx);
- fclose(fVideoFile);
- fclose(fAudioFile);
-
- avcodec_free_frame(&pFrame);
-
- av_free(videoDstData[0]);
- return 0;
- }
-
- int _tmain(int argc, char*argv[])
- {
- SDL_Init(SDL_INIT_VIDEO);
-
- pWindow = SDL_CreateWindow("YUV420P", 200, 100, 800, 600, 0);
-
- pRender=SDL_CreateRenderer(pWindow, -1, 0);
- dstrect.x = 0;
- dstrect.y = 0;
- dstrect.w = 1280;
- dstrect.h = 720;
-
- pTexture = SDL_CreateTexture(pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, 1280, 720);
- Demuxing(argc, argv);
-
- SDL_RenderClear(pRender);
- SDL_DestroyTexture(pTexture);
- SDL_DestroyRenderer(pRender);
- SDL_DestroyWindow(pWindow);
- SDL_Quit();
- return 0;
- }
代碼運行界面