/* * http://ffmpeg.org/doxygen/trunk/index.html * * Main components * * Format (Container) - a wrapper, providing sync, metadata and muxing for the streams. * Stream - a continuous stream (audio or video) of data over time. * Codec - defines how data are enCOded (from Frame to Packet) * and DECoded (from Packet to Frame). * Packet - are the data (kind of slices of the stream data) to be decoded as raw frames. * Frame - a decoded raw frame (to be encoded or filtered). */ #include #include #include #include #include #include #include // print out the steps and errors static void logging(const char *fmt, ...); // decode packets into frames static int decode_packet(AVPacket *pPacket, AVCodecContext *pCodecContext, AVFrame *pFrame); // save a frame into a .pgm file static void save_gray_frame(unsigned char *buf, int wrap, int xsize, int ysize, char *filename); int main(int argc, const char *argv[]) { if (argc < 2) { printf("You need to specify a media file.\n"); return -1; } logging("initializing all the containers, codecs and protocols."); // AVFormatContext holds the header information from the format (Container) // Allocating memory for this component // http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html AVFormatContext *pFormatContext = avformat_alloc_context(); if (!pFormatContext) { logging("ERROR could not allocate memory for Format Context"); return -1; } logging("opening the input file (%s) and loading format (container) header", argv[1]); // Open the file and read its header. The codecs are not opened. // The function arguments are: // AVFormatContext (the component we allocated memory for), // url (filename), // AVInputFormat (if you pass NULL it'll do the auto detect) // and AVDictionary (which are options to the demuxer) // http://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#ga31d601155e9035d5b0e7efedc894ee49 if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) { logging("ERROR could not open the file"); return -1; } // now we have access to some information about our file // since we read its header we can say what format (container) it's // and some other information related to the format itself. logging("format %s, duration %lld us, bit_rate %lld", pFormatContext->iformat->name, pFormatContext->duration, pFormatContext->bit_rate); logging("finding stream info from format"); // read Packets from the Format to get stream information // this function populates pFormatContext->streams // (of size equals to pFormatContext->nb_streams) // the arguments are: // the AVFormatContext // and options contains options for codec corresponding to i-th stream. // On return each dictionary will be filled with options that were not found. // https://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#gad42172e27cddafb81096939783b157bb if (avformat_find_stream_info(pFormatContext, NULL) < 0) { logging("ERROR could not get the stream info"); return -1; } // the component that knows how to enCOde and DECode the stream // it's the codec (audio or video) // http://ffmpeg.org/doxygen/trunk/structAVCodec.html AVCodec *pCodec = NULL; // this component describes the properties of a codec used by the stream i // https://ffmpeg.org/doxygen/trunk/structAVCodecParameters.html AVCodecParameters *pCodecParameters = NULL; int video_stream_index = -1; // loop though all the streams and print its main information for (int i = 0; i < pFormatContext->nb_streams; i++) { AVCodecParameters *pLocalCodecParameters = NULL; pLocalCodecParameters = pFormatContext->streams[i]->codecpar; logging("AVStream->time_base before open coded %d/%d", pFormatContext->streams[i]->time_base.num, pFormatContext->streams[i]->time_base.den); logging("AVStream->r_frame_rate before open coded %d/%d", pFormatContext->streams[i]->r_frame_rate.num, pFormatContext->streams[i]->r_frame_rate.den); logging("AVStream->start_time %" PRId64, pFormatContext->streams[i]->start_time); logging("AVStream->duration %" PRId64, pFormatContext->streams[i]->duration); logging("finding the proper decoder (CODEC)"); AVCodec *pLocalCodec = NULL; // finds the registered decoder for a codec ID // https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga19a0ca553277f019dd5b0fec6e1f9dca pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id); if (pLocalCodec==NULL) { logging("ERROR unsupported codec!"); // In this example if the codec is not found we just skip it continue; } // when the stream is a video we store its index, codec parameters and codec if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) { if (video_stream_index == -1) { video_stream_index = i; pCodec = pLocalCodec; pCodecParameters = pLocalCodecParameters; } logging("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height); } else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) { logging("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate); } // print its name, id and bitrate logging("\tCodec %s ID %d bit_rate %lld", pLocalCodec->name, pLocalCodec->id, pLocalCodecParameters->bit_rate); } if (video_stream_index == -1) { logging("File %s does not contain a video stream!", argv[1]); return -1; } // https://ffmpeg.org/doxygen/trunk/structAVCodecContext.html AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec); if (!pCodecContext) { logging("failed to allocated memory for AVCodecContext"); return -1; } // Fill the codec context based on the values from the supplied codec parameters // https://ffmpeg.org/doxygen/trunk/group__lavc__core.html#gac7b282f51540ca7a99416a3ba6ee0d16 if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0) { logging("failed to copy codec params to codec context"); return -1; } // Initialize the AVCodecContext to use the given AVCodec. // https://ffmpeg.org/doxygen/trunk/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) { logging("failed to open codec through avcodec_open2"); return -1; } // https://ffmpeg.org/doxygen/trunk/structAVFrame.html AVFrame *pFrame = av_frame_alloc(); if (!pFrame) { logging("failed to allocate memory for AVFrame"); return -1; } // https://ffmpeg.org/doxygen/trunk/structAVPacket.html AVPacket *pPacket = av_packet_alloc(); if (!pPacket) { logging("failed to allocate memory for AVPacket"); return -1; } int response = 0; int how_many_packets_to_process = 8; // fill the Packet with data from the Stream // https://ffmpeg.org/doxygen/trunk/group__lavf__decoding.html#ga4fdb3084415a82e3810de6ee60e46a61 while (av_read_frame(pFormatContext, pPacket) >= 0) { // if it's the video stream if (pPacket->stream_index == video_stream_index) { logging("AVPacket->pts %" PRId64, pPacket->pts); response = decode_packet(pPacket, pCodecContext, pFrame); if (response < 0) break; // stop it, otherwise we'll be saving hundreds of frames if (--how_many_packets_to_process <= 0) break; } // https://ffmpeg.org/doxygen/trunk/group__lavc__packet.html#ga63d5a489b419bd5d45cfd09091cbcbc2 av_packet_unref(pPacket); } logging("releasing all the resources"); avformat_close_input(&pFormatContext); av_packet_free(&pPacket); av_frame_free(&pFrame); avcodec_free_context(&pCodecContext); return 0; } static void logging(const char *fmt, ...) { va_list args; fprintf( stderr, "LOG: " ); va_start( args, fmt ); vfprintf( stderr, fmt, args ); va_end( args ); fprintf( stderr, "\n" ); } static int decode_packet(AVPacket *pPacket, AVCodecContext *pCodecContext, AVFrame *pFrame) { // Supply raw packet data as input to a decoder // https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga58bc4bf1e0ac59e27362597e467efff3 int response = avcodec_send_packet(pCodecContext, pPacket); if (response < 0) { logging("Error while sending a packet to the decoder: %s", av_err2str(response)); return response; } while (response >= 0) { // Return decoded output data (into a frame) from a decoder // https://ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c response = avcodec_receive_frame(pCodecContext, pFrame); if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { break; } else if (response < 0) { logging("Error while receiving a frame from the decoder: %s", av_err2str(response)); return response; } if (response >= 0) { logging( "Frame %d (type=%c, size=%d bytes, format=%d) pts %d key_frame %d [DTS %d]", pCodecContext->frame_number, av_get_picture_type_char(pFrame->pict_type), pFrame->pkt_size, pFrame->format, pFrame->pts, pFrame->key_frame, pFrame->coded_picture_number ); char frame_filename[1024]; snprintf(frame_filename, sizeof(frame_filename), "%s-%d.pgm", "frame", pCodecContext->frame_number); // Check if the frame is a planar YUV 4:2:0, 12bpp // That is the format of the provided .mp4 file // RGB formats will definitely not give a gray image // Other YUV image may do so, but untested, so give a warning if (pFrame->format != AV_PIX_FMT_YUV420P) { logging("Warning: the generated file may not be a grayscale image, but could e.g. be just the R component if the video format is RGB"); } // save a grayscale frame into a .pgm file save_gray_frame(pFrame->data[0], pFrame->linesize[0], pFrame->width, pFrame->height, frame_filename); } } return 0; } static void save_gray_frame(unsigned char *buf, int wrap, int xsize, int ysize, char *filename) { FILE *f; int i; f = fopen(filename,"w"); // writing the minimal required header for a pgm file format // portable graymap format -> https://en.wikipedia.org/wiki/Netpbm_format#PGM_example fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255); // writing line by line for (i = 0; i < ysize; i++) fwrite(buf + i * wrap, 1, xsize, f); fclose(f); }