认识FFmpeg
FFMPEG堪称自由软件中最完备的一套多媒体支持库,它几乎实现了所有当下常见的数据封装格式、多媒体传输协议以及音视频编解码器。因此,对于从事多媒体技术开发的工程师来说,深入研究FFMPEG成为一门必不可少的工作,可以这样说,FFMPEG之于多媒体开发工程师的重要性正如kernel之于嵌入式系统工程师一般。
几个小知识:
-
FFMPEG项目是由法国人Fabrice Bellard发起的,此人也是著名的CPU模拟器项目QEMU的发起者,同时还是圆周率算法纪录的保持者。
-
FF是Fast Forward的意思,翻译成中文是“快进”。
-
FFMPEG的LOGO是一个”Z字扫描”示意图,Z字扫描用于将图像的二维频域数据一维化,同时保证了一维化的数据具备良好的统计特性,从而提高其后要进行的一维熵编码的效率。
关于耻辱柱(Hall of Shame):FFMpeg大部分代码遵循LGPL许可证,如果使用者对FFMpeg进行了修改,要求公布修改的源代码;有少部分代码遵循GPL许可证,要求使用者同时公开使用FFMpeg的软件的源代码。实际上,除去部分大的系统软件开发商(Microsoft、Apple等)以及某些著名的音视频服务提供商(Divx、Real等)提供的自有播放器之外,绝大部分第三方开发的播放器都离不开FFMpeg的支持,像Linux桌面环境中的开源播放器VLC、MPlayer,Windows下的KMPlayer、暴风影音以及Android下几乎全部第三方播放器都是基于FFMpeg的。也有许多看似具备自主技术的播放器,其实也都不声不响地使用了FFMpeg,这种行为被称为“盗窃”,参与“盗窃”的公司的名字则被刻在耻辱柱上,国产播放器暴风影音、QQ影音于2009年上榜。
示例程序
[html] view plaincopy
- <span style="font-size:18px;">解码:</span>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/time.h>
- #include "libavutil/avstring.h"
- #include "libavformat/avformat.h"
- #include "libavdevice/avdevice.h"
- #include "libavutil/opt.h"
- #include "libswscale/swscale.h"
- #define DECODED_AUDIO_BUFFER_SIZE 192000
- struct options
- {
- int streamId;
- int frames;
- int nodec;
- int bplay;
- int thread_count;
- int64_t lstart;
- char finput[256];
- char foutput1[256];
- char foutput2[256];
- };
- int parse_options(struct options *opts, int argc, char** argv)
- {
- int optidx;
- char *optstr;
- if (argc < 2) return -1;
- opts->streamId = -1;
- opts->lstart = -1;
- opts->frames = -1;
- opts->foutput1[0] = 0;
- opts->foutput2[0] = 0;
- opts->nodec = 0;
- opts->bplay = 0;
- opts->thread_count = 0;
- strcpy(opts->finput, argv[1]);
- optidx = 2;
- while (optidx < argc)
- {
- optstr = argv[optidx++];
- if (*optstr++ != '-') return -1;
- switch (*optstr++)
- {
- case 's': //< stream id
- opts->streamId = atoi(optstr);
- break;
- case 'f': //< frames
- opts->frames = atoi(optstr);
- break;
- case 'k': //< skipped
- opts->lstart = atoll(optstr);
- break;
- case 'o': //< output
- strcpy(opts->foutput1, optstr);
- strcat(opts->foutput1, ".mpg");
- strcpy(opts->foutput2, optstr);
- strcat(opts->foutput2, ".raw");
- break;
- case 'n': //decoding and output options
- if (strcmp("dec", optstr) == 0)
- opts->nodec = 1;
- break;
- case 'p':
- opts->bplay = 1;
- break;
- case 't':
- opts->thread_count = atoi(optstr);
- break;
- default:
- return -1;
- }
- }
- return 0;
- }
- void show_help(char* program)
- {
- printf("Simple FFMPEG test program\n");
- printf("Usage: %s inputfile [-sstreamid [-fframes] [-kskipped] [-ooutput_filename(without extension)] [-ndec] [-p] [-tthread_count]]\n",
- program);
- return;
- }
- static void log_callback(void* ptr, int level, const char* fmt, va_list vl)
- {
- vfprintf(stdout, fmt, vl);
- }
- /*
- * audio renderer code (oss)
- */
- #include <sys/ioctl.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/soundcard.h>
- #define OSS_DEVICE "/dev/dsp0"
- struct audio_dsp
- {
- int audio_fd;
- int channels;
- int format;
- int speed;
- };
- int map_formats(enum AVSampleFormat format)
- {
- switch(format)
- {
- case AV_SAMPLE_FMT_U8:
- return AFMT_U8;
- case AV_SAMPLE_FMT_S16:
- return AFMT_S16_LE;
- default:
- return AFMT_U8;
- }
- }
- int set_audio(struct audio_dsp* dsp)
- {
- if (dsp->audio_fd == -1)
- {
- printf("Invalid audio dsp id!\n");
- return -1;
- }
- if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_SETFMT, &dsp->format))
- {
- printf("Failed to set dsp format!\n");
- return -1;
- }
- if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_CHANNELS, &dsp->channels))
- {
- printf("Failed to set dsp format!\n");
- return -1;
- }
- if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_SPEED, &dsp->speed))
- {
- printf("Failed to set dsp format!\n");
- return -1;
- }
- return 0;
- }
- int play_pcm(struct audio_dsp* dsp, unsigned char *buf, int size)
- {
- if (dsp->audio_fd == -1)
- {
- printf("Invalid audio dsp id!\n");
- return -1;
- }
- if (-1 == write(dsp->audio_fd, buf, size))
- {
- printf("Failed to write audio dsp!\n");
- return -1;
- }
- return 0;
- }
- /* audio renderer code end */
- /* video renderer code*/
- #include <linux/fb.h>
- #include <sys/mman.h>
- #define FB_DEVICE "/dev/fb0"
- enum pic_format
- {
- eYUV_420_Planer,
- };
- struct video_fb
- {
- int video_fd;
- struct fb_var_screeninfo vinfo;
- struct fb_fix_screeninfo finfo;
- unsigned char *fbp;
- AVFrame *frameRGB;
- struct
- {
- int x;
- int y;
- } video_pos;
- };
- int open_video(struct video_fb *fb, int x, int y)
- {
- int screensize;
- fb->video_fd = open(FB_DEVICE, O_WRONLY);
- if (fb->video_fd == -1) return -1;
- if (ioctl(fb->video_fd, FBIOGET_FSCREENINFO, &fb->finfo)) return -2;
- if (ioctl(fb->video_fd, FBIOGET_VSCREENINFO, &fb->vinfo)) return -2;
- printf("video device: resolution %dx%d, %dbpp\n", fb->vinfo.xres, fb->vinfo.yres, fb->vinfo.bits_per_pixel);
- screensize = fb->vinfo.xres * fb->vinfo.yres * fb->vinfo.bits_per_pixel / 8;
- fb->fbp = (unsigned char *) mmap(0, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fb->video_fd, 0);
- if (fb->fbp == -1) return -3;
- if (x >= fb->vinfo.xres || y >= fb->vinfo.yres)
- {
- return -4;
- }
- else
- {
- fb->video_pos.x = x;
- fb->video_pos.y = y;
- }
- fb->frameRGB = avcodec_alloc_frame();
- if (!fb->frameRGB) return -5;
- return 0;
- }
- /* only 420P supported now */
- int show_picture(struct video_fb *fb, AVFrame *frame, int width, int height, enum pic_format format)
- {
- struct SwsContext *sws;
- int i;
- unsigned char *dest;
- unsigned char *src;
- if (fb->video_fd == -1) return -1;
- if ((fb->video_pos.x >= fb->vinfo.xres) || (fb->video_pos.y >= fb->vinfo.yres)) return -2;
- if (fb->video_pos.x + width > fb->vinfo.xres)
- {
- width = fb->vinfo.xres - fb->video_pos.x;
- }
- if (fb->video_pos.y + height > fb->vinfo.yres)
- {
- height = fb->vinfo.yres - fb->video_pos.y;
- }
- if (format == PIX_FMT_YUV420P)
- {
- sws = sws_getContext(width, height, format, width, height, PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL);
- if (sws == 0)
- {
- return -3;
- }
- if (sws_scale(sws, frame->data, frame->linesize, 0, height, fb->frameRGB->data, fb->frameRGB->linesize))
- {
- return -3;
- }
- dest = fb->fbp + (fb->video_pos.x+fb->vinfo.xoffset) * (fb->vinfo.bits_per_pixel/8) +(fb->video_pos.y+fb->vinfo.yoffset) * fb->finfo.line_length;
- for (i = 0; i < height; i++)
- {
- memcpy(dest, src, width*4);
- src += fb->frameRGB->linesize[0];
- dest += fb->finfo.line_length;
- }
- }
- return 0;
- }
- void close_video(struct video_fb *fb)
- {
- if (fb->video_fd != -1)
- {
- munmap(fb->fbp, fb->vinfo.xres * fb->vinfo.yres * fb->vinfo.bits_per_pixel / 8);
- close(fb->video_fd);
- fb->video_fd = -1;
- }
- }
- /* video renderer code end */
- int main(int argc, char **argv)
- {
- AVFormatContext* pCtx = 0;
- AVCodecContext *pCodecCtx = 0;
- AVCodec *pCodec = 0;
- AVPacket packet;
- AVFrame *pFrame = 0;
- FILE *fpo1 = NULL;
- FILE *fpo2 = NULL;
- int nframe;
- int err;
- int got_picture;
- int picwidth, picheight, linesize;
- unsigned char *pBuf;
- int i;
- int64_t timestamp;
- struct options opt;
- int usefo = 0;
- struct audio_dsp dsp;
- struct video_fb fb;
- int dusecs;
- float usecs1 = 0;
- float usecs2 = 0;
- struct timeval elapsed1, elapsed2;
- int decoded = 0;
- av_register_all();
- av_log_set_callback(log_callback);
- av_log_set_level(50);
- if (parse_options(&opt, argc, argv) < 0 || (strlen(opt.finput) == 0))
- {
- show_help(argv[0]);
- return 0;
- }