Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libavutil/avutil.h>
- #include <libswscale/swscale.h>
- /** We do a bit of standard file opening, etc., and then write the RGB data.
- * We write the file one line at a time. A PPM file is simply a file that has RGB
- * information laid out in a long string. If you know HTML colors, it would be like
- * 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, but you get the idea.)
- * The header indicated how wide and tall the image is, and the max size of the RGB values. */
- void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
- FILE *pFile;
- char szFilename[80];
- 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);
- }
- int main ( int argc, char* argv[] )
- {
- /** This registers all available file formats and codecs with the library
- * so they will be used automatically when a file with the corresponding
- * format/codec is opened. Note that you only need to call av_register_all() once,
- * so we do it here in main(). If you like, it's possible to register only certain
- * individual file formats and codecs, but there's usually no reason why you
- * would have to do that. */
- av_register_all();
- AVFormatContext* pFormatCtx;
- pFormatCtx = NULL;
- // Open video file
- /** We get our filename from the first argument.
- * This function reads the file header and stores information about the file
- * format in the AVFormatContext structure we have given it.
- * The last three arguments are used to specify the file format, buffer size,
- * and format options, but by setting this to NULL or 0, libavformat will
- * auto-detect these.
- * This function only looks at the header, so next we need to check out the stream information in the file. */
- if ( avformat_open_input ( &pFormatCtx, argv[1], NULL, NULL ) != 0 )
- return -1; // couldn't open file
- // Retrieve stream information
- /** This function populates pFormatCtx->streams with the proper information.
- * We introduce a handy debugging function to show us what's inside: */
- if ( avformat_find_stream_info ( pFormatCtx, NULL ) < 0 )
- return -1; // couldn't find stream information
- /** Now pFormatCtx->streams is just an array of pointers, of size pFormatCtx->nb_streams,
- * so let's walk through it until we find a video stream. */
- int i;
- AVCodecContext* pCodecCtx;
- // find the first video stream
- int videoStream = -1;
- for ( i = 0; i < pFormatCtx->nb_streams; i++ )
- if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_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 "codec context."
- * This contains all the information about the codec that the stream is using, and
- * now we have a pointer to it. But we still have to find the actual codec 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_open2 ( pCodecCtx, pCodec, NULL ) < 0 )
- return -1; // Could not open codec
- /** Some of you might remember from the old tutorial that there were two other parts to this code:
- * adding CODEC_FLAG_TRUNCATED to pCodecCtx->flags and adding a hack to correct grossly incorrect
- * frame rates. These two fixes aren't in ffplay.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 a struct that has the numerator and denominator (AVRational).
- * We represent the frame rate as a fraction because many codecs have non-integer frame rates (like NTSC's 29.97fps).
- */
- /** Now we need a place to actually store the frame: */
- AVFrame* pFrame;
- AVFrame* pFrameRGB;
- // 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.
- * ffmpeg will do these conversions for us. For most projects (including ours)
- * we're going to want to convert our initial frame to a specific format.
- * Let's allocate 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 raw data
- * 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
- * that makes sure the memory addresses are aligned and such. It will not protect
- * you from memory leaks, double freeing, or other malloc problems.
- * Now we use avpicture_fill to associate the frame with our newly allocated buffer.
- * About the AVPicture cast: the AVPicture struct is a subset of the AVFrame struct
- * - the beginning of the AVFrame struct is identical to the AVPicture 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 Data
- /** What we're going to do is read through the entire video stream by reading in the packet,
- * decoding it into our frame, and once our frame is complete, we will convert and save it. */
- int frameFinished;
- AVPacket packet;
- i = 0;
- struct SwsContext* pSWSContext;
- pSWSContext = NULL;
- pSWSContext = sws_getCachedContext(pSWSContext, pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, 1920, 1080, PIX_FMT_RGB24, SWS_BILINEAR, 0, 0, 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_video2 ( pCodecCtx, pFrame, &frameFinished,
- &packet );
- // Did we get a video frame?
- if ( frameFinished ) {
- // Convert the image from its native format to RGB
- sws_scale ( pSWSContext, (const uint8_t * const *) *pFrameRGB->data, pFrameRGB->linesize, 0, pFrameRGB->height, pFrame->data, pFrame->linesize );
- // Save the frame to disk
- if ( ++i <= 1000 )
- SaveFrame ( pFrameRGB, pCodecCtx->width,
- pCodecCtx->height, i );
- }
- }
- // Free the packet that was allocated by av_read_frame
- av_free_packet ( &packet );
- if(i > 1000) break;
- }
- //A note on packets
- /** Technically a packet can contain partial frames or other bits of data,
- * but ffmpeg's parser ensures that the packets we get contain either
- * complete or multiple frames.
- * The process, again, is simple: av_read_frame() reads in a packet and stores
- * it in 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() converts the
- * packet to a frame for us. However, we might not have all the information we need
- * for a frame after decoding a packet, so avcodec_decode_video() sets frameFinished
- * for us when we have the next frame. Finally, we use img_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 SaveFrame function.
- * Now all we need to do is make the SaveFrame function to write the RGB information
- * to a file in PPM format. We're going to be kind of sketchy on the PPM format itself;
- * trust us, it works. */
- // Now, going back to our main() function. Once we're done reading from the video stream, 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
- avformat_close_input ( &pFormatCtx );
- return 0;
- /**You'll notice we use av_free for the memory we allocated with avcode_alloc_frame and av_malloc.
- That's it for the code! Now, if you're on Linux or a similar platform, you'll run:
- 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 movie files.
- */
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement