SHARE
TWEET

FFMPEG actual video conversion with comments

MaKiPL Mar 11th, 2019 68 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #pragma comment (lib, "avutil")
  5. #include <libavutil/frame.h>
  6. #include <libavutil/mem.h>
  7. #pragma comment (lib, "avformat")
  8. #include <libavformat/avformat.h>
  9. #pragma comment (lib, "swscale")
  10. #include <libswscale/swscale.h>
  11. #pragma comment (lib, "avcodec")
  12. #include <libavcodec/avcodec.h>
  13. #include <dsound.h>
  14.  
  15. //this is for FFMpeg 4.11, as many functions are deprecated and finding something in the internet is quite a problem due to the fact many of the examples use deprecated functions. Also official "decode_video.c" example is "decoding" which doesn't mean decoding to for example RGB24
  16.  
  17. const char * c = "D:\\FF7\\Root\\data\\movies\\_opening.avi";
  18.  
  19. int main()
  20. {
  21.     AVFormatContext *format_ctx = 0;
  22.     int result = avformat_open_input(&format_ctx, c, NULL, NULL); //we open stream from file to format_ctx
  23.     result = avformat_find_stream_info(format_ctx, NULL); //this finds streams. Stream may be video, audio and even subtitle
  24.     int vidoestreamID = -1;
  25.     int audiostreamID = -1;
  26.     if (format_ctx->nb_streams > 1) //so we test if the file contains more than one type of data
  27.         for (int i = 0; i < format_ctx->nb_streams; i++)
  28.         {
  29.             if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) //we check if it's audio and if yes, we apply the stream id to audiostreamId so we know which stream id is the audio
  30.                 audiostreamID = i;
  31.             if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) //as above
  32.                 vidoestreamID = i;
  33.         }
  34.     AVCodecContext *audiocodec_ctx = format_ctx->streams[audiostreamID]->codec; //now we need to get the codec context
  35.     AVCodecContext *videocodec_ctx = format_ctx->streams[vidoestreamID]->codec; //as above
  36.     AVCodec *codec = avcodec_find_decoder(audiocodec_ctx->codec_id); //this finds the codec, because we need to tell ffmpeg which decoder should be used
  37.     AVCodec *videocodec = avcodec_find_decoder(videocodec_ctx->codec_id); //as above- this finds e.g. Vorbis decoder and associates it to context like audiocodec_ctx
  38.     result = avcodec_open2(audiocodec_ctx, codec, NULL); //it actually opens the found codec
  39.     result = avcodec_open2(videocodec_ctx, videocodec, NULL);
  40.  
  41.     AVFrame * frame = 0;
  42.     AVPacket packet;
  43.     frame = av_frame_alloc(); //okay, so we allocate a frame
  44.  
  45.     /*IDirectSound * ds;
  46.  
  47.     IDirectSoundBuffer *buffersound;*/
  48.  
  49.     while (result = av_read_frame(format_ctx, &packet) >= 0) //we usually just read the frame. It reads a data from file
  50.     { //if it's above or zero, then it's success, if it's below zero, then probably the file just ended.
  51.         //we save the "frame" to packet data. Packet is compressed data as in file
  52.         if (packet.stream_index == vidoestreamID) //so now the packet holds the ID, we need to check if it's video or audio
  53.         {
  54.             result = avcodec_send_packet(videocodec_ctx, &packet); //now we send packet to videocodec_ctx related decoder we opened with avcodec_open2
  55.             while (result >= 0) //if the result is negative, then something broken
  56.             {
  57.                 result = avcodec_receive_frame(videocodec_ctx, frame); //now we receive the uncompressed data. It's still trash!
  58.                 if (result == AVERROR(EAGAIN))
  59.                     continue;
  60.                 //so we have uncompressed data, but it's still in that exotic format. We want the frame in RGB24 format
  61.                 //therefore we need to create a buffer, that is heigh*width*pixelformat
  62.                 int bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, videocodec_ctx->width, videocodec_ctx->height);
  63.                 //now we create a three pointers
  64.                 char* plane[3] = { 0,0,0 };
  65.                 //and allocate the buffer for RGB24 frame
  66.                 uint8_t * buff = av_mallocz(bufferSize);
  67.                 //that stride is important too
  68.                 int strides[3] = { 0,0,0 };
  69.                 //now we set the plane first pointer to point to our frame location
  70.                 plane[0] = buff;
  71.                 //this is important, without this it returns zero bytes. It's stride, so usually width*pixelformat
  72.                 strides[0] = videocodec_ctx->width * 3;
  73.                
  74.                 //now we proceed with actual CONVERSION. In ffmpeg they called it "sws" whatever the fuck it means
  75.                 //swscontext and sws_scale are actual functions that convert the uncompressed RAW data to our format
  76.  
  77.                 struct SwsContext * swsContext = sws_getContext(videocodec_ctx->width, //source width
  78.                     videocodec_ctx->height, //source height
  79.                     videocodec_ctx->pix_fmt, //source pixel format
  80.                     videocodec_ctx->width, //destination width
  81.                     videocodec_ctx->height, //destination height
  82.                     AV_PIX_FMT_RGB24, //and here we actually tell to what pixel format should be the frame converted
  83.                     NULL, NULL, NULL, NULL);
  84.                 //now we have a struct, we can run the actual conversion class:
  85.                 sws_scale(swsContext, frame->data, frame->linesize, 0, videocodec_ctx->height, plane, strides);
  86.                 //we provide the struct, provide the raw uncompressed data, linesize, then 0 because it's like starting offset,
  87.                 //then height because it's like last height row (pretty weird)
  88.                 //and then provide ** pointer for both data frame and that stride
  89.  
  90.                 //after the function succeeds the *(plane[0]) holds first pixel R. Therefore just dump the plane[0] buffer
  91.                 FILE * fo = 0;
  92.                 errno_t er = fopen_s(&fo, "D:\\test.image", "wb");
  93.                 auto varr = fwrite(plane[0], 1, bufferSize, fo);
  94.                 fclose(fo);
  95.                 printf("test");
  96.                 //voila, there's test.image that holds raw RGB24 image with the frame. You can use IrfanView>Open as>RAW
  97.                 //then type the image width, height, select 24BPP and color order RGB
  98.  
  99.                 //don't forget to free the buffer
  100.                 free(buff);
  101.             }
  102.         }
  103.  
  104.         //I'm still not quite sure about the audio streams. They too need conversion from Vorbis for example to PCM
  105.         //normally audiocodec_ctx reports Vorbis as IEEE_FLOAT. It's supported by DirectSound, but it still needs
  106.         //conversion from Vorbis to Float32. How? I'll try to dig it up. Don't worry! :D
  107.         if (packet.stream_index == audiostreamID)
  108.         {
  109.             result = avcodec_send_packet(audiocodec_ctx, &packet);
  110.             while (result >= 0)
  111.             {
  112.                 result = avcodec_receive_frame(audiocodec_ctx, frame);
  113.                 if (result == AVERROR(EAGAIN))
  114.                     continue;
  115.             }
  116.         }
  117.         //this is important as it frees the packet only for one frame per stream. Therefore one frame with video and audio should call this two times.
  118.         av_packet_unref(&packet);
  119.     }
  120.    
  121.  
  122.     return 0;
  123. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top