ffmpeg编程(一)
这篇主要讲如何以路径的方式打开一个视频
int main() { AVFormatContext *pFormatCtx; int i, videoStream; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; AVPacket packet; int frameFinished = NULL; int numBytes; uint8_t *buffer; struct SwsContext *pSwsCtx;
AVFormatContext是输入输出信息的容器,需要注意的是其中两个成员:
struct AVInputFormat * AVIFormat; //数据输入格式 struct AVOutputFormat * AVIFormat; //数据输出格式
这两个成员不能同时赋值,简单来说 AVFormatContext 不能作为输入容器又作为输出容器。
AVCodec:ffmpeg中编解码器是由链表结构管理的,链表的第一个节点是在文件libavcodec/util.c中声明的:
static AVCodec *first_avcodec = NULL;
对于编码器、与解码器的操作都是在围绕AVCodec该链表执行的。
AVFrame用来保存数据缓存的对像
AVPacket主要记录音视频数据帧,时钟信息和压缩数据首地址,大小等信息。
SwsContext视频分辩率、色彩空间变换时所需要的上下文句柄。
av_register_all(); //视频的路径 const char *filename = "/root/Desktop/test.flv"; if (av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0) return -1; // Couldn't open file if (av_find_stream_info(pFormatCtx) < 0) return -1; // Couldn't find stream information // Dump information about file onto standard error dump_format(pFormatCtx, 0, filename, 0);
av_register_all();函数通过执行libvcodec/allcodes.c文件里的avcodec_register_all()来初始化所有encoder/decoder。
av_open_input_file 只是检测了文件的头部,所以接着 av_find_stream_info(pFormatCtx) 函数负责检查在文件中的流的信息。
videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { videoStream = i; break; } if (videoStream == -1) return -1; // Didn't find a video stream
现在pFormatCtx->streams仅仅是一组大小为pFormatCtx->nb_streams的指针,所以让我们先跳过它直到我们找到一个视频流并且记录该数组的坐标。
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
得到编解码器上下文信息。
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found }
得到了编码器的上下文信息之后,但是AVCodecContext 包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。所以必需要找到真正的编解码器avcodec_find_decoder(pCodecCtx->codec_id)函数就是做的这件事情。
if (avcodec_open(pCodecCtx, pCodec) < 0) return -1; // Could not open codec
avcodec_open(pCodecCtx, pCodec)函数负责打开pCodec解码器。
// Allocate video frame pFrame = avcodec_alloc_frame(); // Allocate an AVFrame structure pFrameRGB = avcodec_alloc_frame(); if (pFrameRGB == NULL) return -1;
avcodec_alloc_frame()函数负责申请内存,注意:初始化的时候AVFrame中的元素data,linesize均为空。未指向任何内存数据。
// Determine required buffer size and allocate buffer numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t)); // Assign appropriate parts of buffer to image planes in pFrameRGB // Note that pFrameRGB is an AVFrame, but AVFrame is a superset // of AVPicture avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
函数avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);负责计算AVCodecContext缓冲区的大小和申请空间。
函数av_malloc(numBytes * sizeof(uint8_t))申请内存对齐(但不保证内存泄漏)。
函数avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);把帧和pFrameRGB关联起来。
// Read frames and save first five frames to disk i = 0; while (av_read_frame(pFormatCtx, &packet) >= 0) { // Is this a packet from the video stream? if (packet.stream_index == videoStream) { // Allocate video frame pFrame = avcodec_alloc_frame(); int w = pCodecCtx->width; int h = pCodecCtx->height; // Decode video frame avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); pSwsCtx = sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL); // Did we get a video frame? if (frameFinished) { // Convert the image from its native format to RGB sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); // Save the frame to disk ++i; printf("%d\n", i); SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } }
函数av_read_frame(pFormatCtx, &packet)读取一个包并且把它保存到AVPacket结构体中。
函数avcodec_decode_video2()把包转换为帧。
函数sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);负责得到视频分辩率、色彩空间变换时所需要的上下文句柄。
函数sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0,pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);把RGB格式转换成image。
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; const char *szFilename = "/root/Desktop/ffmpeg"; int y; // Open file pFile = fopen(szFilename, "wt"); if (pFile == NULL) return; // Write header //printf("P6\n%d %d\n255\n", width, height); printf("bbbbbbbbbb\n"); // Write pixel data for (y = 0; y < height; y++) fwrite(pFrame->data[0] + y * pFrame->linesize[0], sizeof(char), width * 3, pFile); // Close file fclose(pFile); }
把图片保存成文件,这里就不再叙述了。
// Free the packet that was allocated by av_read_frame av_free_packet(&packet); } // Free the RGB image av_free(buffer); av_free(pFrameRGB); // Free the YUV frame av_free(pFrame); // Close the codec avcodec_close(pCodecCtx); // Close the video file av_close_input_file(pFormatCtx); return 0; }
释放内存。
c文件下载