最簡單的基於FFmpeg的記憶體讀寫的例子:記憶體播放器

來源:互聯網
上載者:User

標籤:ffmpeg   記憶體   播放   解碼   

打算記錄兩個最簡單的FFmpeg進行記憶體讀寫的例子。之前的所有有關FFmpeg的例子都是對檔案進行操作的。例如《100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器》播放的是一個視頻的檔案。而《最簡單的基於FFMPEG的轉碼程式》也是將一個視頻檔案轉換為另一個視頻檔案。《最簡單的基於FFmpeg的視頻編碼器(YUV編碼為H.264)》也是最後編碼得到一個H.264視頻檔案。實際上,並不是所有視頻的編碼,解碼都是針對檔案進行處理的。有的時候需要的解碼的視頻資料在一段記憶體中。例如,通過其他系統送來的視頻資料。同樣,有的時候編碼後的視頻資料也未必要儲存成一個檔案。例如,要求將編碼後的視頻資料送給其他的系統進行下一步的處理。以上兩種情況就要求FFmpeg不僅僅是對檔案進行“讀,寫”操作,而是要對記憶體進行“讀,寫”操作。因此打算記錄的兩個例子就是使用FFmpeg對記憶體進行讀寫的例子。

有關FFmpeg讀寫記憶體的例子已經在文章《ffmpeg 從記憶體中讀取資料(或將資料輸出到記憶體)》中有過敘述,但是一直沒有做完整代碼的工程。本文記錄《最簡單的基於FFmpeg記憶體播放器》。該例子中,首先將檔案中的視頻資料通過fread()讀取到記憶體中,然後使用FFmpeg播放記憶體中的資料。

下篇文章計劃記錄的第二個例子是《最簡單的基於FFmpeg記憶體轉碼器》。該例子中,首先將檔案中的視頻資料通過fread()讀取到記憶體中,然後使用FFmpeg讀取該資料並進行轉碼,接著將轉碼後的資料輸出到另一塊記憶體中,最後將該資料通過fwrite()寫入成檔案。

關於如何從記憶體中讀取資料在這裡不再詳述,可以參考文章:

 ffmpeg 從記憶體中讀取資料(或將資料輸出到記憶體)

關鍵點

關鍵點就兩個:

1.      初始化自訂的AVIOContext,指定自訂的回呼函數。範例程式碼如下:

//AVIOContext中的緩衝unsigned char *aviobuffer=(unsigned char*)av_malloc(32768);AVIOContext *avio=avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);pFormatCtx->pb=avio; if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){           printf("Couldn‘t open inputstream.(無法開啟輸入資料流)\n");           return -1;}

上述代碼中,自訂了回呼函數read_buffer()。在使用avformat_open_input()開啟媒體資料的時候,就可以不指定檔案的URL了,即其第2個參數為NULL(因為資料不是靠檔案讀取,而是由read_buffer()提供)

2.      自己寫回呼函數。範例程式碼如下:

//Callbackint read_buffer(void *opaque, uint8_t *buf, int buf_size){if(!feof(fp_open)){inttrue_size=fread(buf,1,buf_size,fp_open);return true_size;}else{return -1;}}

當系統需要資料的時候,會自動調用該回呼函數以擷取資料。這個例子為了簡單,直接使用fread()讀取資料至記憶體。回呼函數需要格外注意它的參數和傳回值。

 

原始碼

下面直接貼上程式的原始碼:

 

/** * 最簡單的基於FFmpeg的記憶體讀寫例子(記憶體播放器) * Simplest FFmpeg mem Player * * 雷霄驊 * [email protected] * 中國傳媒大學/數字電視技術 * Communication University of China / Digital TV Technology * http://blog.csdn.net/leixiaohua1020 * * 本程式實現了對記憶體中的視頻資料的播放。 * 是最簡單的使用FFmpeg讀記憶體的例子。 * * This software play video data in memory (not a file). * It‘s the simplest example to use FFmpeg to read from memory. * */#include <stdio.h>extern "C"{#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswscale/swscale.h"//SDL#include "sdl/SDL.h"#include "sdl/SDL_thread.h"};//Output YUV420P #define OUTPUT_YUV420P 0FILE *fp_open=NULL;//Callbackint read_buffer(void *opaque, uint8_t *buf, int buf_size){if(!feof(fp_open)){int true_size=fread(buf,1,buf_size,fp_open);return true_size;}else{return -1;}}int main(int argc, char* argv[]){AVFormatContext*pFormatCtx;inti, videoindex;AVCodecContext*pCodecCtx;AVCodec*pCodec;char filepath[]="cuc60anniversary_start.mkv";av_register_all();avformat_network_init();pFormatCtx = avformat_alloc_context();fp_open=fopen(filepath,"rb+");//AVIOContext中的緩衝unsigned char *aviobuffer=(unsigned char *)av_malloc(32768);AVIOContext *avio =avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);pFormatCtx->pb=avio;if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){printf("Couldn‘t open input stream.(無法開啟輸入資料流)\n");return -1;}if(av_find_stream_info(pFormatCtx)<0){printf("Couldn‘t find stream information.(無法擷取流資訊)\n");return -1;}videoindex=-1;for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;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;}AVFrame*pFrame,*pFrameYUV;pFrame=avcodec_alloc_frame();pFrameYUV=avcodec_alloc_frame();uint8_t *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);//SDL----------------------------if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1;} int screen_w=0,screen_h=0;SDL_Surface *screen; screen_w = pCodecCtx->width;screen_h = pCodecCtx->height;screen = SDL_SetVideoMode(screen_w, screen_h, 0,0);if(!screen) {  printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError());  return -1;}SDL_Overlay *bmp; bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen); SDL_Rect rect;//SDL End------------------------int ret, got_picture;AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));//Output Information-----------------------------printf("File Information---------------------------------\n");av_dump_format(pFormatCtx,0,filepath,0);printf("-------------------------------------------------\n");#if OUTPUT_YUV420P     FILE *fp_yuv=fopen("output.yuv","wb+");  #endif  struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //------------------------------while(av_read_frame(pFormatCtx, packet)>=0){if(packet->stream_index==videoindex){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if(ret < 0){printf("Decode Error.(解碼錯誤)\n");return -1;}if(got_picture){sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);#if OUTPUT_YUV420Pint y_size=pCodecCtx->width*pCodecCtx->height;  fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //Ufwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V#endifSDL_LockYUVOverlay(bmp);bmp->pixels[0]=pFrameYUV->data[0];bmp->pixels[2]=pFrameYUV->data[1];bmp->pixels[1]=pFrameYUV->data[2];     bmp->pitches[0]=pFrameYUV->linesize[0];bmp->pitches[2]=pFrameYUV->linesize[1];   bmp->pitches[1]=pFrameYUV->linesize[2];SDL_UnlockYUVOverlay(bmp); rect.x = 0;    rect.y = 0;    rect.w = screen_w;    rect.h = screen_h;  SDL_DisplayYUVOverlay(bmp, &rect); //Delay 40msSDL_Delay(40);}}av_free_packet(packet);}sws_freeContext(img_convert_ctx);#if OUTPUT_YUV420P     fclose(fp_yuv);#endif fclose(fp_open);SDL_Quit();av_free(out_buffer);av_free(pFrameYUV);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;}

 

可以通過代碼定義的宏來確定是否將解碼後的YUV420P資料輸出成檔案:

#define OUTPUT_YUV420P 0 

結果

程式的運行結果如下。可以解碼播放測試視頻。適逢60周年校慶,因此截取了一小段校慶晚會的開場畫面作為測試視頻,給母校慶生~

 

下載 

SourceForge項目首頁:
https://sourceforge.net/projects/simplestffmpegmemhandler/
CSDN項目:
http://download.csdn.net/detail/leixiaohua1020/8003731


註:
 本工程包含兩個FFmpeg讀寫記憶體的例子:
 simplest_ffmpeg_mem_player:基於FFmpeg的記憶體播放器。
 simplest_ffmpeg_mem_transcoder:基於FFmpeg的記憶體轉碼器(下篇文章記錄)。


最簡單的基於FFmpeg的記憶體讀寫的例子:記憶體播放器

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.