python包装ffmpeg

python包装ffmpeg
项目需求开发网络流媒体播放器,整个项目就一人开发,整个系统平台,包括调度,存储集群服务,影像同步导入、系统api接口、视频查询和播放客户端,一切有了python就变得简单。一个人在战斗,还是蛮有劲道。

pyqt开发播放界面,python用于网络通信和粘合逻辑,ffmpeg用于解码,google一圈试用了几个pyffmpeg的库,非常不理想,干脆自己做:

采用ctypes访问dll,测试解码并将视频帧转换RGB24写入PPM文件,imagemagick转换为jpg,okay。。

C动态库
结构定义: 
 1  #define FF_BOOL int
 2  #define FF_TRUE 1
 3  #define FF_FALSE 0
 4 
 5 typedef unsigned  char  StreamByte_t;
 6 
 7  // 视频流信息
 8  struct MediaStreamInfo_t{
 9      int codec_type;
10      int codec_id;
11      int width;
12      int height;
13      int gopsize;
14      int pixfmt;
15      int tb_num;
16      int tb_den;
17      int bitrate;
18      int frame_number;
19      int videostream;  // 视频流编号
20  };
21 
22  struct MediaVideoFrame_t{
23     StreamByte_t *    rgb24;
24     size_t            size;
25      int                width;
26      int                height;
27     unsigned  int    sequence;  // 控制播放顺序
28      unsigned  int    duration;  // 播放时间
29  };
30 
31  struct MediaPacket_t{
32      StreamByte_t*    data;
33      size_t            size;
34     AVPacket    *    pkt;
35      int                stream;     // 流编号 
36       int                dts;
37      int                pts;
38     size_t            sequence;
39     size_t            duration;
40 
41 };
42 
43  struct MediaFormatContext_t;
44 
45  // 解码器
46  struct MediaCodecContext_t{
47     AVCodecContext * codecCtx;     // AVCodecContext*
48      AVCodec *        codec;    
49      int                stream;  // 流编号
50      AVFrame *        rgbframe24;  //
51      AVFrame*        frame;     //
52      StreamByte_t*    buffer;
53     size_t            bufsize;
54      void *            user;
55     MediaStreamInfo_t si;
56 };
57 
58  struct MediaFormatContext_t{
59     AVFormatContext * fc;  // AVFormatContext* 
60      MediaStreamInfo_t video;     // 视频信息
61      
62 };

实现部分
  1 
  2 MEDIACODEC_API   FF_BOOL InitLib(){
  3     avcodec_init();
  4     av_register_all();
  5      return FF_TRUE;
  6 }
  7 
  8 MEDIACODEC_API  void Cleanup(){
  9 
 10 }
 11 
 12 MEDIACODEC_API  MediaCodecContext_t* InitAvCodec(MediaStreamInfo_t* si){
 13     MediaCodecContext_t* ctx;
 14     AVCodecContext * codecCtx;
 15     
 16 
 17     codecCtx = avcodec_alloc_context();
 18     codecCtx->width = si->width;
 19     codecCtx->height = si->height;
 20     codecCtx->time_base.num = si->tb_num;
 21     codecCtx->time_base.den = si->tb_den;
 22     codecCtx->bit_rate = si->bitrate;
 23     codecCtx->frame_number = si->frame_number;
 24     codecCtx->codec_type =(AVMediaType) si->codec_type;
 25     codecCtx->codec_id = (CodecID)si->codec_id;
 26 
 27     AVCodec *codec;
 28     codec = avcodec_find_decoder(codecCtx->codec_id);
 29      if(codec == NULL){
 30          // avcodec_free_context()
 31           return NULL;
 32     }
 33      if(avcodec_open(codecCtx, codec)<0){
 34          return NULL;
 35     }
 36 
 37     ctx =  new MediaCodecContext_t();
 38     ctx->codecCtx    = codecCtx;
 39     ctx->codec        = codec;
 40     ctx->stream        = si->videostream;
 41     ctx->si = *si;
 42 
 43     uint8_t *buffer;
 44     size_t numBytes;
 45     ctx->rgbframe24 = avcodec_alloc_frame();
 46     ctx->frame =  avcodec_alloc_frame();
 47 
 48     numBytes=avpicture_get_size(PIX_FMT_RGB24, codecCtx->width,codecCtx->height);
 49     buffer=(uint8_t *)av_malloc(numBytes* sizeof(uint8_t));
 50     ctx->buffer = (StreamByte_t*)buffer;
 51     ctx->bufsize = numBytes;
 52 
 53     avpicture_fill((AVPicture *)ctx->rgbframe24, buffer, PIX_FMT_RGB24,codecCtx->width, codecCtx->height);  //  bind buffer with frame
 54 
 55      return ctx;
 56 }
 57 
 58 
 59 MEDIACODEC_API  void FreeAvCodec(MediaCodecContext_t* ctx){
 60     AVCodecContext * codecCtx;
 61     AVCodec *codec;
 62     codecCtx = (AVCodecContext*) ctx->codecCtx;
 63     codec = (AVCodec*) ctx->codec;
 64     avcodec_close(codecCtx);
 65     
 66     av_free(ctx->buffer);
 67     av_free(ctx->rgbframe24);
 68     av_free(ctx->frame);
 69     delete ctx;
 70 }
 71 
 72 
 73  // 图像格式转换
 74  int convert_picture(AVFrame *rgbframe, AVFrame *frame,  int width,  int height, PixelFormat format,PixelFormat toformat){
 75      struct SwsContext *sws;
 76 
 77      if (format == PIX_FMT_YUV420P){        
 78         sws = sws_getContext(width, height, format, width, height, toformat, SWS_FAST_BILINEAR, NULL, NULL, NULL);
 79          if (sws == 0){
 80              return -1;
 81         }
 82          if (sws_scale(sws, frame->data, frame->linesize, 0, height, rgbframe->data, rgbframe->linesize)){
 83              // return -1;
 84          }
 85     } else{
 86          return -1;
 87     }
 88      return 0;
 89 }
 90 
 91 MEDIACODEC_API MediaVideoFrame_t * DecodeVideoFrame(MediaCodecContext_t* ctx,MediaPacket_t* pkt){
 92     MediaVideoFrame_t * frame;
 93     
 94      if(pkt->stream != ctx->stream){
 95          return NULL;
 96     }
 97      int frameFinished;
 98     
 99     AVCodecContext* codecCtx;
100     codecCtx = (AVCodecContext*)ctx->codecCtx;
101 
102     avcodec_decode_video(codecCtx, ctx->frame, &frameFinished,pkt->data, pkt->size);
103      if( !frameFinished ){
104          return NULL;
105     }
106      int r;
107     r = convert_picture(ctx->rgbframe24,ctx->frame,codecCtx->width,codecCtx->height,codecCtx->pix_fmt,PIX_FMT_RGB24);
108      if(r){
109          return NULL;
110     }
111      //  复制一份
112      frame =  new MediaVideoFrame_t();
113     frame->rgb24 = (StreamByte_t*)malloc(ctx->bufsize);
114     frame->size =ctx->bufsize;
115     memcpy(frame->rgb24,ctx->buffer,ctx->bufsize);
116     frame->width = ctx->frame->width;
117     frame->height = ctx->frame->height;
118     frame->sequence = pkt->sequence;
119     frame->duration = pkt->duration;
120 
121      return frame;
122 }
123 
124 MEDIACODEC_API  void FreeVideoFrame(MediaVideoFrame_t* frame){
125      if(frame->rgb24){
126         free(frame->rgb24);
127     }
128     delete frame;
129 }
130 
131 MEDIACODEC_API MediaPacket_t * AllocPacket(){
132     MediaPacket_t *pkt;
133 
134     pkt =  new MediaPacket_t();
135     pkt->pkt = new AVPacket();
136     pkt->size = 0;
137     pkt->data = NULL;
138     pkt->stream = -1;
139      return pkt;
140 }
141 
142  //  s - 0 : 不释放内部的 av_free_packet(); 1 - 释放packet内部数据
143  MEDIACODEC_API  void FreePacket(MediaPacket_t* pkt, int s){
144     AVPacket* packet;
145     packet = pkt->pkt;
146      if(s){
147         av_free_packet(packet);
148     }
149     delete packet;
150     delete pkt;
151 }
152 
153 
154 MEDIACODEC_API MediaFormatContext_t* InitAvFormatContext( char * file){
155     MediaFormatContext_t* ctx;
156     AVFormatContext *ic;
157      int videoStream=-1;
158     unsigned  int i;
159     AVCodecContext *codecCtx;
160 
161 
162     
163     
164     ic = avformat_alloc_context();
165      if(avformat_open_input(&ic, file, NULL,NULL)!=0){
166         avformat_free_context(ic);
167          return NULL; 
168     }
169     
170      if(av_find_stream_info(ic)<0){ // 查询文件流信息
171          avformat_free_context(ic);
172          return NULL;
173     }
174     
175     
176      for(i=0; i < ic->nb_streams; i++){
177          if(ic->streams[i]->codec->codec_type== CODEC_TYPE_VIDEO) {
178             videoStream=i;
179              break;
180         }
181     }
182      if( videoStream ==-1){
183         avformat_free_context(ic);
184          return NULL;
185     }
186     codecCtx = ic->streams[videoStream]->codec;
187 
188     ctx =  new MediaFormatContext_t();
189     ctx->fc = ic;
190     ctx->video.codec_type = ( int)CODEC_TYPE_VIDEO;
191     ctx->video.codec_id = ( int)codecCtx->codec_id;
192     ctx->video.bitrate = codecCtx->bit_rate;
193     ctx->video.frame_number = codecCtx->frame_number;
194     ctx->video.gopsize = codecCtx->gop_size;
195     ctx->video.height = codecCtx->height;
196     ctx->video.pixfmt = codecCtx->pix_fmt;
197     ctx->video.tb_den = codecCtx->time_base.den;
198     ctx->video.tb_num = codecCtx->time_base.num;
199     ctx->video.width = codecCtx->width;
200     ctx->video.videostream = videoStream;
201      return ctx;
202 }
203 
204 MEDIACODEC_API  void FreeAvFormatContext(MediaFormatContext_t* ctx){
205      if(ctx->fc){
206         avformat_free_context((AVFormatContext*)ctx->fc);
207     }
208     delete ctx;
209 }
210 
211 MEDIACODEC_API MediaPacket_t* ReadNextPacket(MediaFormatContext_t* ctx){
212     MediaPacket_t* pkt = NULL;
213     
214      while(1){
215         pkt = AllocPacket();
216          if( pkt == NULL){
217              return NULL;
218         }
219          if( av_read_frame((AVFormatContext*)ctx->fc, (AVPacket*)pkt->pkt) <0 ){
220             FreePacket(pkt,0);  // 并没有释放内部的packet数据
221               // printf("read_fream error!\n");
222               return NULL;
223         }
224          if( ctx->video.videostream == pkt->pkt->stream_index){
225             pkt->data = pkt->pkt->data;
226             pkt->size = pkt->pkt->size;
227             pkt->stream = pkt->pkt->stream_index;
228             pkt->duration = pkt->pkt->duration;
229              return pkt;    
230         }        
231         FreePacket(pkt,1);
232     }
233 
234      return NULL;
235 }
236 
237 MEDIACODEC_API  void FlushBuffer(MediaCodecContext_t* codec){
238     avcodec_flush_buffers(codec->codecCtx);
239 }
240 
241 
242  // 跳转到指定时间 
243  MEDIACODEC_API  int SeekToTime(MediaFormatContext_t* ctx, int timesec){
244  // avcodec_flush_buffers(pFormatCtx->streams[video_stream]->codec);
245      
246      if( av_seek_frame(ctx->fc,-1,timesec*AV_TIME_BASE,AVSEEK_FLAG_BACKWARD)<0){
247          return -1;
248     }
249      // avcodec_flush_buffers(ctx->streams[video_stream]->codec);
250       return 0;
251 }
252 
253 MEDIACODEC_API  void ReadReset(MediaFormatContext_t* ctx){
254     SeekToTime(ctx,0);
255 }
256 

python测试代码: 

 1 
 2  import ffmpeg
 3 
 4 
 5  def printStreamInfo(s):
 6      print s.codec_type, ' codec_id: ',s.codec_id,\
 7          ' width: ',s.width, ' height: ',s.height,\
 8          ' gopsize: ',s.gopsize, ' pixfmt: ',s.pixfmt,\
 9          ' timebase_num: ',s.tb_num, ' timebase_den: ',s.tb_den,\
10          ' bitrate: ',s.bitrate, ' framenumber: ',s.frame_number,\
11          ' streamindex: ',s.videostream
12 
13  def printFrameInfo(f):
14      print len(f.rgb24[:f.size]), ' width: ',f.width, ' height: ',f.height, ' duration: ',f.duration, ' size: ',f.size
15     
16 
17  def saveppm(f,file):
18     fp = open(file, ' wb ')
19      if  not fp:
20          return
21     fp.write(  " P6\n%d %d\n255\n "%(f.width,f.height) )
22     fp.write(f.rgb24[:f.size])
23     fp.close()
24      print file
25         
26     
27 ffmpeg.InitLib()
28 fc =  ffmpeg.InitAvFormatContext( ' d:/movies/marieMcCray.avi ')
29 printStreamInfo(fc.contents.video)
30 bytes=0
31 codec = ffmpeg.InitAvCodec(fc.contents.video)
32  print codec  # apply a codec
33 
34 cnt=0
35  while True:
36     pkt = ffmpeg.ReadNextPacket(fc)
37      if  not pkt: break
38     bytes += pkt.contents.size
39     frame = ffmpeg.DecodeVideoFrame(codec,pkt)
40      if frame:
41         cnt+=1
42         printFrameInfo(frame.contents)
43          if cnt < 100:
44             saveppm(frame.contents, " d:/temp4/%s_xx.ppm "%cnt)
45             os.system( ' convertx %s %s '%( " d:/temp4/%s_xx.ppm "%cnt, " d:/temp4/%s_xx.jpg "%cnt))
46         ffmpeg.FreeVideoFrame(frame)  # memory leak
47       print pkt.contents.stream,pkt.contents.size,pkt.contents.duration
48     ffmpeg.FreePacket(pkt,1)
49 







你可能感兴趣的:(python包装ffmpeg)