📄 tutorial01.java
字号:
## An ffmpeg and SDL TutorialPage 1 2 3 4 5 6 7 8 End Prev Home Next Printable version Text version## Tutorial 01: Making ScreencapsCode: tutorial01.c### OverviewMovie files have a few basic components. First, the file itself is called a**container**, and the type of container determines where the information inthe file goes. Examples of containers are AVI and Quicktime. Next, you have abunch of **streams**; for example, you usually have an audio stream and avideo stream. (A "stream" is just a fancy word for "a succession of dataelements made available over time".) The data elements in a stream are called**frames**. Each stream is encoded by a different kind of **codec**. The codecdefines how the actual data is COded and DECoded - hence the name CODEC.Examples of codecs are DivX and MP3. **Packets** are then read from thestream. Packets are pieces of data that can contain bits of data that aredecoded into raw frames that we can finally manipulate for our application.For our purposes, each packet contains complete frames, or multiple frames inthe case of audio. At its very basic level, dealing with video and audio streams is very easy: 10 OPEN video_stream FROM video.avi 20 READ packet FROM video_stream INTO frame 30 IF frame NOT COMPLETE GOTO 20 40 DO SOMETHING WITH frame 50 GOTO 20 Handling multimedia with ffmpeg is pretty much as simple as this program,although some programs might have a very complex "DO SOMETHING" step. So inthis tutorial, we're going to open a file, read from the video stream insideit, and our DO SOMETHING is going to be writing the frame to a PPM file. ### Opening the FileFirst, let's see how we open a file in the first place. With ffmpeg, you haveto first initialize the library. (Note that some systems might have to use<ffmpeg/avcodec.h> and <ffmpeg/avformat.h> instead.) #include <avcodec.h> #include <avformat.h> ... int main(int argc, charg *argv[]) { av_register_all(); This registers all available file formats and codecs with the library so theywill be used automatically when a file with the corresponding format/codec isopened. Note that you only need to call av_register_all() once, so we do ithere in main(). If you like, it's possible to register only certain individualfile formats and codecs, but there's usually no reason why you would have todo that. Now we can actually open the file: AVFormatContext *pFormatCtx; // Open video file if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0) return -1; // Couldn't open file We get our filename from the first argument. This function reads the fileheader and stores information about the file format in the AVFormatContextstructure we have given it. The last three arguments are used to specify thefile format, buffer size, and format options, but by setting this to NULL or0, libavformat will auto-detect these. This function only looks at the header, so next we need to check out thestream information in the file.: // Retrieve stream information if(av_find_stream_info(pFormatCtx)<0) return -1; // Couldn't find stream information This function populates pFormatCtx->streams with the proper information. Weintroduce a handy debugging function to show us what's inside: // Dump information about file onto standard error dump_format(pFormatCtx, 0, argv[1], 0); Now pFormatCtx->streams is just an array of pointers, of sizepFormatCtx->nb_streams, so let's walk through it until we find a video stream. int i; AVCodecContext *pCodecCtx; // Find the first video stream 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 // Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec; The stream's information about the codec is in what we call the "codeccontext." This contains all the information about the codec that the stream isusing, and now we have a pointer to it. But we still have to find the actualcodec and open it: AVCodec *pCodec; // Find the decoder for the video stream pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if(avcodec_open(pCodecCtx, pCodec)<0) return -1; // Could not open codec Some of you might remember from the old tutorial that there were two otherparts to this code: adding CODEC_FLAG_TRUNCATED to pCodecCtx->flags and addinga hack to correct grossly incorrect frame rates. These two fixes aren't inffplay.c anymore, so I have to assume that they are not necessary anymore.There's another difference to point out since we removed that code:pCodecCtx->time_base now holds the frame rate information. time_base is astruct that has the numerator and denominator (AVRational). We represent theframe rate as a fraction because many codecs have non-integer frame rates(like NTSC's 29.97fps). ### Storing the DataNow we need a place to actually store the frame: AVFrame *pFrame; // Allocate video frame pFrame=avcodec_alloc_frame(); Since we're planning to output PPM files, which are stored in 24-bit RGB,we're going to have to convert our frame from its native format to RGB. ffmpegwill do these conversions for us. For most projects (including ours) we'regoing to want to convert our initial frame to a specific format. Let'sallocate a frame for the converted frame now. // Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame(); if(pFrameRGB==NULL) return -1; Even though we've allocated the frame, we still need a place to put the rawdata when we convert it. We use avpicture_get_size to get the size we need,and allocate the space manually: uint8_t *buffer; int numBytes; // 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)); av_malloc is ffmpeg's malloc that is just a simple wrapper around malloc thatmakes sure the memory addresses are aligned and such. It will _not_ protectyou from memory leaks, double freeing, or other malloc problems. Now we use avpicture_fill to associate the frame with our newly allocatedbuffer. About the AVPicture cast: the AVPicture struct is a subset of theAVFrame struct - the beginning of the AVFrame struct is identical to theAVPicture struct. // 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); Finally! Now we're ready to read from the stream! ### Reading the DataWhat we're going to do is read through the entire video stream by reading inthe packet, decoding it into our frame, and once our frame is complete, wewill convert and save it. int frameFinished; AVPacket packet; i=0; while(av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size); // Did we get a video frame? if(frameFinished) { // Convert the image from its native format to RGB img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); // Save the frame to disk if(++i<=5) SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); } **A note on packets**Technically a packet can contain partial frames or other bits of data, butffmpeg's parser ensures that the packets we get contain either complete ormultiple frames. The process, again, is simple: av_read_frame() reads in a packet and stores itin the AVPacket struct. Note that we've only allocated the packet structure -ffmpeg allocates the internal data for us, which is pointed to by packet.data.This is freed by the av_free_packet() later. avcodec_decode_video() convertsthe packet to a frame for us. However, we might not have all the informationwe need for a frame after decoding a packet, so avcodec_decode_video() setsframeFinished for us when we have the next frame. Finally, we useimg_convert() to convert from the native format (pCodecCtx->pix_fmt) to RGB.Remember that you can cast an AVFrame pointer to an AVPicture pointer.Finally, we pass the frame and height and width information to our SaveFramefunction. Now all we need to do is make the SaveFrame function to write the RGBinformation to a file in PPM format. We're going to be kind of sketchy on thePPM format itself; trust us, it works. void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; char szFilename[32]; int y; // Open file sprintf(szFilename, "frame%d.ppm", iFrame); pFile=fopen(szFilename, "wb"); if(pFile==NULL) return; // Write header fprintf(pFile, "P6\n%d %d\n255\n", width, height); // Write pixel data for(y=0; y<height; y++) fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile); // Close file fclose(pFile); } We do a bit of standard file opening, etc., and then write the RGB data. Wewrite the file one line at a time. A PPM file is simply a file that has RGBinformation laid out in a long string. If you know HTML colors, it would belike laying out the color of each pixel end to end like #ff0000#ff0000....would be a red screen. (It's stored in binary and without the separator, butyou get the idea.) The header indicated how wide and tall the image is, andthe max size of the RGB values. Now, going back to our main() function. Once we're done reading from the videostream, we just have to clean everything up: // 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; You'll notice we use av_free for the memory we allocated withavcode_alloc_frame and av_malloc. That's it for the code! Now, if you're on Linux or a similar platform, you'llrun: gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lavutil-lm If you have an older version of ffmpeg, you may need to drop -lavutil: gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lz -lm Most image programs should be able to open PPM files. Test it on some moviefiles. _**>>** Tutorial 2: Outputting to the Screen_* * *Function Reference Data Referenceemail:dranger at gmail dot comBack to dranger.comThis work is licensed under the Creative Commons Attribution-Share Alike 2.5License. To view a copy of this license, visithttp://creativecommons.org/licenses/by-sa/2.5/ or send a letter to CreativeCommons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. Code examples are based off of FFplay, Copyright (c) 2003 Fabrice Bellard, anda tutorial by Martin Bohme.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -