├── bunny_1080p.mp4 ├── .gitignore ├── Makefile ├── README.md └── part1.c /bunny_1080p.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsonservice/ffmpeg_c_player/HEAD/bunny_1080p.mp4 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | !v/small_bunny_1080p_60fps.mp4 3 | !v/small_bunny_1080p_30fps.mp4 4 | .ipynb_checkpoints/ 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | exec1: 2 | gcc -g -Wall -o build/player1 -lavformat -lavcodec -lswscale -lz part1.c && ./build/player1 bunny_1080p.mp4 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | This follows the tutorial on http://dranger.com/ffmpeg/tutorial01.html but using a most up to date ffmpeg libraries. (ie: ffmpeg/swscale become libswscale/swscale) 4 | 5 | This was tested with `MacOSx 10.12.3`, `ffmpeg 3.2.2` and `GCC stable 6.3.0` but I think you can run it on linux and even windows (with a few caveats). 6 | 7 | ## FFmpeg API documentation for this version 8 | 9 | https://www.ffmpeg.org/doxygen/3.2/index.html 10 | 11 | ## Basic terminology 12 | 13 | * **Container** - a wrapper, providing sync, metadata and muxing for the streams. 14 | * **Stream** - a continuous stream (audio and video) of data over time, the data itself are the frames, each stream is encoded by a different codec. 15 | * **Codec** - defines how data are COded and DECoded. 16 | * **Packet** - are the data decoded as raw frames (for this simple explanation), one frame for video and multiple for audio. 17 | -------------------------------------------------------------------------------- /part1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This follows the tutorial on http://dranger.com/ffmpeg/tutorial01.html but using a most up to date ffmpeg libraries. 3 | * 4 | * This was made based on ffmpeg 3.2.2 and tested with macosx 10.12.3 (gcc stable 6.3.0) 5 | * 6 | * https://www.ffmpeg.org/doxygen/3.2/index.html 7 | * 8 | * Container (Format) - a wrapper, providing sync, metadata and muxing for the streams. 9 | * Stream - a continuous stream (audio and video) of data over time, the data itself are the frames, each stream is encoded by a different codec. 10 | * Codec - defines how data are COded and DECoded. 11 | * Packet - are the data decoded as raw frames (for this simple explanation), one frame for video and multiple for audio. 12 | * Frame - a decoded raw frame 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame); 20 | void logg(const char *str); 21 | 22 | int main(int argc, const char *argv[]) 23 | { 24 | logg("initializing"); 25 | // registering all the formats (containers), codecs and protocols. 26 | // https://www.ffmpeg.org/doxygen/3.2/group__lavf__core.html#ga917265caec45ef5a0646356ed1a507e3 27 | av_register_all(); 28 | 29 | // holds container header info 30 | // https://www.ffmpeg.org/doxygen/3.2/structAVFormatContext.html 31 | AVFormatContext *pFormatCtx = NULL; 32 | int i; 33 | // https://www.ffmpeg.org/doxygen/3.2/structAVCodecContext.html 34 | AVCodecContext *pCodecCtxOrig = NULL; 35 | AVCodecContext *pCodecCtx = NULL; 36 | int videoStream = -1; 37 | // https://www.ffmpeg.org/doxygen/3.2/structAVCodec.html 38 | AVCodec *pCodec = NULL; 39 | AVFrame *pFrame = NULL; 40 | AVFrame *pFrameRGB = NULL; 41 | uint8_t *buffer = NULL; 42 | int numBytes; 43 | struct SwsContext *sws_ctx = NULL; 44 | int frameFinished; 45 | AVPacket packet; 46 | 47 | logg("opening the input, loading format (container) header"); 48 | // Open an input stream and read the header. The codecs are not opened. 49 | // https://www.ffmpeg.org/doxygen/3.2/group__lavf__decoding.html#ga31d601155e9035d5b0e7efedc894ee49 50 | if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) 51 | { 52 | logg("ERROR could not open the file"); 53 | return -1; 54 | } 55 | 56 | logg("finding stream info from format"); 57 | // Read packets of a media file to get stream information. 58 | // this function populates pFormatCtx->streams (of size equals to pFormatCtx->nb_streams) 59 | // https://www.ffmpeg.org/doxygen/3.2/group__lavf__decoding.html#gad42172e27cddafb81096939783b157bb 60 | if (avformat_find_stream_info(pFormatCtx, NULL) < 0) 61 | { 62 | logg("ERROR could not get the stream info"); 63 | return -1; 64 | } 65 | 66 | // Print detailed information about the input or output format, such as duration, bitrate, streams, container, programs, metadata, side data, codec and time base. 67 | // https://www.ffmpeg.org/doxygen/3.2/group__lavf__misc.html#gae2645941f2dc779c307eb6314fd39f10 68 | // av_dump_format(pFormatCtx, 0, argv[1], 0); 69 | 70 | // Find the first video stream 71 | for (i = 0; i < pFormatCtx->nb_streams; i++) 72 | { 73 | if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 74 | { 75 | logg("found video stream"); 76 | videoStream = i; 77 | break; 78 | } 79 | } 80 | 81 | if (videoStream==-1) 82 | { 83 | logg("ERROR could not find the video stream"); 84 | return -1; 85 | } 86 | 87 | // get a pointer to the codec context for the video stream 88 | pCodecCtxOrig = pFormatCtx->streams[videoStream]->codec; 89 | 90 | logg("finding the proper decoder"); 91 | // Find a registered decoder with a matching codec ID. 92 | // https://www.ffmpeg.org/doxygen/3.2/group__lavc__decoding.html#ga19a0ca553277f019dd5b0fec6e1f9dca 93 | pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id); 94 | 95 | if (pCodec==NULL) 96 | { 97 | logg("ERROR unsupported codec!"); 98 | return -1; 99 | } 100 | 101 | // Allocate an AVCodecContext and set its fields to default values. 102 | // https://www.ffmpeg.org/doxygen/3.2/group__lavc__core.html#gae80afec6f26df6607eaacf39b561c315 103 | pCodecCtx = avcodec_alloc_context3(pCodec); 104 | 105 | // Copy the settings of the source AVCodecContext into the destination AVCodecContext. 106 | // https://www.ffmpeg.org/doxygen/3.2/group__lavc__core.html#gae381631ba4fb14f4124575d9ceacb87eO 107 | // Deprectated 108 | if (avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) 109 | { 110 | logg("ERROR could not copy the codec context"); 111 | return -1; 112 | } 113 | 114 | // Initialize the AVCodecContext to use the given AVCodec. 115 | // https://www.ffmpeg.org/doxygen/3.2/group__lavc__core.html#ga11f785a188d7d9df71621001465b0f1d 116 | if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) 117 | { 118 | logg("ERROR could not open the codec context"); 119 | return -1; 120 | } 121 | 122 | // Allocate an AVFrame and set its fields to default values. 123 | // https://www.ffmpeg.org/doxygen/3.2/group__lavu__frame.html#gac700017c5270c79c1e1befdeeb008b2f 124 | pFrame = av_frame_alloc(); 125 | pFrameRGB = av_frame_alloc(); 126 | 127 | if (pFrameRGB == NULL || pFrame == NULL) 128 | { 129 | logg("ERROR could not allocated memory for the frames"); 130 | return -1; 131 | } 132 | 133 | logg("getting number of needed byte for a full frame"); 134 | // Return the size in bytes of the amount of data required to store an image with the given parameters 135 | // https://www.ffmpeg.org/doxygen/3.2/group__lavu__picture.html#ga24a67963c3ae0054a2a4bab35930e694 136 | numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); 137 | 138 | logg("allocating memory for the frame"); 139 | // allocates memory 140 | // https://www.ffmpeg.org/doxygen/3.2/tableprint__vlc_8h.html#ae97db1f58b6b1515ed57a83bea3dd572 141 | buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t)); 142 | 143 | logg("setup frame"); 144 | avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); 145 | 146 | // initialize SWS context for software scaling 147 | sws_ctx = sws_getContext(pCodecCtx->width, 148 | pCodecCtx->height, 149 | pCodecCtx->pix_fmt, 150 | pCodecCtx->width, 151 | pCodecCtx->height, 152 | AV_PIX_FMT_RGB24, 153 | SWS_BILINEAR, 154 | NULL, 155 | NULL, 156 | NULL 157 | ); 158 | 159 | i=0; 160 | 161 | logg("reading frame"); 162 | while(av_read_frame(pFormatCtx, &packet) >= 0) 163 | { 164 | if (packet.stream_index == videoStream) 165 | { 166 | logg("decoding frame"); 167 | avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 168 | } 169 | 170 | if (frameFinished) 171 | { 172 | logg("converting color to RGB 24b"); 173 | //convert from yuv420 to 24b rgb 174 | sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, 175 | pFrame->linesize, 0, pCodecCtx->height, 176 | pFrameRGB->data, pFrameRGB->linesize); 177 | if(++i<=1) 178 | { 179 | SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); 180 | logg("releasing frame"); 181 | av_free_packet(&packet); 182 | break; 183 | } 184 | } 185 | 186 | logg("releasing frame"); 187 | // Free the packet that was allocated by av_read_frame 188 | av_free_packet(&packet); 189 | } 190 | 191 | logg("releasing all the resources"); 192 | // Free the RGB image 193 | av_free(buffer); 194 | av_free(pFrameRGB); 195 | 196 | // Free the YUV frame 197 | av_free(pFrame); 198 | 199 | // Close the codecs 200 | avcodec_close(pCodecCtx); 201 | avcodec_close(pCodecCtxOrig); 202 | 203 | // Close the video file 204 | avformat_close_input(&pFormatCtx); 205 | 206 | return 0; 207 | } 208 | 209 | void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { 210 | FILE *pFile; 211 | char szFilename[32]; 212 | int y; 213 | logg("saving the frame"); 214 | 215 | // Open file 216 | sprintf(szFilename, "frame%d.ppm", iFrame); 217 | pFile=fopen(szFilename, "wb"); 218 | if(pFile==NULL) 219 | return; 220 | 221 | // Write header 222 | fprintf(pFile, "P6\n%d %d\n255\n", width, height); 223 | 224 | // Write pixel data 225 | for(y=0; ydata[0]+y*pFrame->linesize[0], 1, width*3, pFile); 227 | 228 | // Close file 229 | fclose(pFile); 230 | } 231 | 232 | void logg(const char *str) 233 | { 234 | printf("LOG: %s\n", str); 235 | } 236 | --------------------------------------------------------------------------------