FFmpeg + SDL的視訊播放器的製作(3)
阿新 • • 發佈:2018-12-15
FFmpeg + SDL的視訊播放器的製作(3)
ffmpeg解碼的函式和資料結構
例項程式執行:simplest_ffmpeg_decoder.cpp
/** * 最簡單的基於FFmpeg的解碼器 * Simplest FFmpeg Decoder * * 雷霄驊 Lei Xiaohua * [email protected] * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程式實現了視訊檔案的解碼(支援HEVC,H.264,MPEG2等)。 * 是最簡單的FFmpeg視訊解碼方面的教程。 * 通過學習本例子可以瞭解FFmpeg的解碼流程。 * This software is a simplest video decoder based on FFmpeg. * Suitable for beginner of FFmpeg. * */ #include <stdio.h> #define __STDC_CONSTANT_MACROS extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" }; int main(int argc, char* argv[]) { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame,*pFrameYUV; uint8_t *out_buffer; AVPacket *packet; int y_size; int ret, got_picture; struct SwsContext *img_convert_ctx; //輸入檔案路徑 char filepath[]="Titanic.ts"; int frame_cnt; av_register_all();//註冊所有元件 avformat_network_init(); pFormatCtx = avformat_alloc_context(); if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){//開啟輸入視訊流檔案 printf("Couldn't open input stream.\n"); return -1; } if(avformat_find_stream_info(pFormatCtx,NULL)<0){//開啟視訊流檔案的資訊 printf("Couldn't find stream information.\n"); return -1; } videoindex=-1; for(i=0; i<pFormatCtx->nb_streams; i++)//nb_streams專門記錄有多少個stream(音訊還是視訊),遍歷nb_streams if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){//判斷avstream是音訊還是視訊 videoindex=i;//視訊所在的avstream流的序號 break; } if(videoindex==-1){ printf("Didn't find a video stream.\n"); return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id);//找出 查詢對應的解碼器 if(pCodec==NULL){ printf("Codec not found.\n"); return -1; } if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){//開啟解碼器 printf("Could not open codec.\n"); return -1; } FILE *fp = fopen("info.txt", "wb+");//以讀寫的方式開啟一個檔案info.txt二進位制檔案 fprintf(fp, "shichang: %d\n", pFormatCtx->duration);//獲取AVFormatContext裡面的視訊時長duration(微妙為單位) fprintf(fp, "fengzhuanggeshi: %s\n", pFormatCtx->iformat->name);//AVFormatContext是所有資料的來源 fprintf(fp, "kuangao:%d*%d\n", pFormatCtx->streams[videoindex]->codec->width, pFormatCtx->streams[videoindex]->codec->height);//AVStream影象的寬高 printf("shichang: %d\n", pFormatCtx->duration);//獲取AVFormatContext裡面的視訊時長duration(微妙為單位) printf("fengzhuanggeshi: %s\n", pFormatCtx->iformat->name);//AVFormatContext是所有資料的來源 printf("kuangao:%d*%d\n", pFormatCtx->streams[videoindex]->codec->width, pFormatCtx->streams[videoindex]->codec->height);//AVStream影象的寬高 fclose(fp);//關閉檔案 /* * 在此處新增輸出視訊資訊的程式碼 * 取自於pFormatCtx,使用fprintf() */ pFrame=av_frame_alloc(); pFrameYUV=av_frame_alloc(); out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); packet=(AVPacket *)av_malloc(sizeof(AVPacket));//AVPacket,裡面是H.264。(AVFrame裡面是YUV) //Output Info----------------------------- printf("--------------- File Information ----------------\n"); av_dump_format(pFormatCtx,0,filepath,0); printf("-------------------------------------------------\n"); img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); FILE *fp_264 = fopen("test264.h264", "wb+");//解碼前的H.264碼流資料 FILE *fp_yuv = fopen("testyuv.yuv", "wb+");//解碼後的YUV畫素資料 frame_cnt=0; while(av_read_frame(pFormatCtx, packet)>=0){//讀取一幀的視訊的資料 if(packet->stream_index==videoindex){//有沒有讀取到視訊流?是否讀取到末尾? /* * 在此處新增輸出H264碼流的程式碼 * 取自於packet,使用fwrite() */ fwrite(packet->data, 1, packet->size, fp_264);//解碼前的H.264碼流資料 ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//核心函式,解碼一幀壓縮資料,會生成AVFrame。(AVFrame裡面是YUV) if(ret < 0){ printf("Decode Error.\n"); return -1; } if(got_picture){ //sws_scale裁減右邊的那塊 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); printf("Decoded frame index: %d\n",frame_cnt); /* * 在此處新增輸出YUV的程式碼 * 取自於pFrameYUV,使用fwrite() */ //YUV420 //pFrameYUV是經過sws_scale裁減後的YUV fwrite(pFrameYUV->data[0], 1, pCodecCtx->width*pCodecCtx->height, fp_yuv);//Y資料 //U,V資料量只有Y資料的四分之一 //色差訊號U,V的取樣頻率為亮度訊號取樣頻率的四分之一,在水平方向和垂直方向上的取樣點數均為Y的一半,因此UV的解析度是Y的1/4 fwrite(pFrameYUV->data[1], 1, pCodecCtx->width*pCodecCtx->height/4, fp_yuv);//U資料 fwrite(pFrameYUV->data[2], 1, pCodecCtx->width*pCodecCtx->height/4, fp_yuv);//V資料 frame_cnt++; } } av_free_packet(packet); } fclose(fp_264); fclose(fp_yuv); sws_freeContext(img_convert_ctx); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx);//關閉解碼器 avformat_close_input(&pFormatCtx);//關閉輸入視訊檔案 return 0; }