hedgewars/avwrapper.c
changeset 7198 5debd5fe526e
parent 7180 53ffc8853008
child 7235 baa69bd025d9
--- a/hedgewars/avwrapper.c	Fri Jun 08 02:41:14 2012 +0400
+++ b/hedgewars/avwrapper.c	Fri Jun 08 02:52:35 2012 +0400
@@ -16,8 +16,11 @@
 static AVCodecContext* g_pAudio;
 static AVCodecContext* g_pVideo;
 
-static int g_Width, g_Height, g_Framerate;
+static int g_Width, g_Height;
 static int g_Frequency, g_Channels;
+static int g_VQuality, g_AQuality;
+static AVRational g_Framerate;
+static const char* g_pPreset;
 
 static FILE* g_pSoundFile;
 static int16_t* g_pSamples;
@@ -77,7 +80,7 @@
     AddFileLogRaw(Buffer);
 }
 
-static void AddAudioStream(enum CodecID codec_id)
+static void AddAudioStream()
 {
 #if LIBAVCODEC_VERSION_MAJOR >= 54
     g_pAStream = avformat_new_stream(g_pContainer, g_pACodec);
@@ -89,15 +92,24 @@
     g_pAStream->id = 1;
 
     g_pAudio = g_pAStream->codec;
+
     avcodec_get_context_defaults3(g_pAudio, g_pACodec);
-    g_pAudio->codec_id = codec_id;
+    g_pAudio->codec_id = g_pACodec->id;
 
     // put parameters
     g_pAudio->sample_fmt = AV_SAMPLE_FMT_S16;
- //   pContext->bit_rate = 128000;
     g_pAudio->sample_rate = g_Frequency;
     g_pAudio->channels = g_Channels;
 
+    // set quality
+    if (g_AQuality > 100)
+        g_pAudio->bit_rate = g_AQuality;
+    else
+    {
+        g_pAudio->flags |= CODEC_FLAG_QSCALE;
+        g_pAudio->global_quality = g_AQuality*FF_QP2LAMBDA;
+    }
+
     // some formats want stream headers to be separate
     if (g_pFormat->flags & AVFMT_GLOBALHEADER)
         g_pAudio->flags |= CODEC_FLAG_GLOBAL_HEADER;
@@ -123,6 +135,9 @@
 // returns non-zero if there is more sound
 static int WriteAudioFrame()
 {
+    if (!g_pAStream)
+        return 0;
+
     AVPacket Packet = { 0 };
     av_init_packet(&Packet);
 
@@ -166,7 +181,7 @@
 }
 
 // add a video output stream
-static void AddVideoStream(enum CodecID codec_id)
+static void AddVideoStream()
 {
 #if LIBAVCODEC_VERSION_MAJOR >= 54
     g_pVStream = avformat_new_stream(g_pContainer, g_pVCodec);
@@ -177,8 +192,9 @@
         FatalError("Could not allocate video stream");
 
     g_pVideo = g_pVStream->codec;
-    avcodec_get_context_defaults3( g_pVideo, g_pVCodec );
-    g_pVideo->codec_id = codec_id;
+    
+    avcodec_get_context_defaults3(g_pVideo, g_pVCodec);
+    g_pVideo->codec_id = g_pVCodec->id;
 
     // put parameters
     // resolution must be a multiple of two
@@ -188,29 +204,28 @@
        of which frame timestamps are represented. for fixed-fps content,
        timebase should be 1/framerate and timestamp increments should be
        identically 1. */
-    g_pVideo->time_base.den = g_Framerate;
-    g_pVideo->time_base.num = 1;
+    g_pVideo->time_base.den = g_Framerate.num;
+    g_pVideo->time_base.num = g_Framerate.den;
     //g_pVideo->gop_size = 12; /* emit one intra frame every twelve frames at most */
     g_pVideo->pix_fmt = PIX_FMT_YUV420P;
 
+    // set quality
+    if (g_VQuality > 100)
+        g_pVideo->bit_rate = g_VQuality;
+    else
+    {
+        g_pVideo->flags |= CODEC_FLAG_QSCALE;
+        g_pVideo->global_quality = g_VQuality*FF_QP2LAMBDA;
+    }
+
+    AVDictionary* pDict = NULL;
+    if (strcmp(g_pVCodec->name, "libx264") == 0)
+        av_dict_set(&pDict, "preset", g_pPreset, 0);
+
     // some formats want stream headers to be separate
     if (g_pFormat->flags & AVFMT_GLOBALHEADER)
         g_pVideo->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
-    AVDictionary* pDict = NULL;
-    if (codec_id == CODEC_ID_H264)
-    {
-       // av_dict_set(&pDict, "tune", "animation", 0);
-       // av_dict_set(&pDict, "preset", "veryslow", 0);
-       av_dict_set(&pDict, "crf", "20", 0);
-    }
-    else
-    {
-        g_pVideo->flags |= CODEC_FLAG_QSCALE;
-       // g_pVideo->bit_rate = g_Width*g_Height*g_Framerate/4;
-        g_pVideo->global_quality = 15*FF_QP2LAMBDA;
-    }
-
     // open the codec
     if (avcodec_open2(g_pVideo, g_pVCodec, &pDict) < 0)
         FatalError("Could not open video codec %s", g_pVCodec->long_name);
@@ -237,6 +252,9 @@
             AudioTime = (double)g_pAStream->pts.val*g_pAStream->time_base.num/g_pAStream->time_base.den;
         while (AudioTime < VideoTime && WriteAudioFrame());
     }
+    
+    if (!g_pVStream)
+        return 0;
 
     AVPacket Packet;
     av_init_packet(&Packet);
@@ -300,111 +318,73 @@
     WriteFrame(g_pVFrame);
 }
 
-void AVWrapper_GetList()
-{
-    // initialize libav and register all codecs and formats
-    av_register_all();
-
-#if 0
-    AVOutputFormat* pFormat = NULL;
-    while (pFormat = av_oformat_next(pFormat))
-    {
-        Log("%s; %s; %s;\n", pFormat->name, pFormat->long_name, pFormat->mime_type);
-        
-        AVCodec* pCodec = NULL;
-        while (pCodec = av_codec_next(pCodec))
-        {
-            if (!av_codec_is_encoder(pCodec))
-                continue;
-            if (avformat_query_codec(pFormat, pCodec->id, FF_COMPLIANCE_NORMAL) != 1)
-                continue;
-            if (pCodec->type = AVMEDIA_TYPE_VIDEO)
-            {
-                if (pCodec->supported_framerate != NULL)
-                    continue;
-                Log("    Video: %s; %s;\n", pCodec->name, pCodec->long_name);
-            }
-            if (pCodec->type = AVMEDIA_TYPE_AUDIO)
-            {
-               /* if (pCodec->supported_samplerates == NULL)
-                    continue;
-                int i;
-                for(i = 0; i <)
-                    supported_samplerates*/
-                Log("    Audio: %s; %s;\n", pCodec->name, pCodec->long_name);
-            }
-        }
-   /*     struct AVCodecTag** pTags = pCur->codec_tag;
-        int i;
-        for (i = 0; ; i++)
-        {
-            enum CodecID id = av_codec_get_id(pTags, i);
-            if (id == CODEC_ID_NONE)
-                break;
-            AVCodec* pCodec = avcodec_find_encoder(id);
-            Log("    %i: %s; %s;\n", id, pCodec->name, pCodec->long_name);
-        }*/
-    }
-#endif
-}
-
-void AVWrapper_Init(void (*pAddFileLogRaw)(const char*), const char* pFilename, const char* pSoundFile, int Width, int Height, int Framerate, int Frequency, int Channels)
+void AVWrapper_Init(
+         void (*pAddFileLogRaw)(const char*),
+         const char* pFilename,
+         const char* pSoundFile,
+         const char* pFormatName,
+         const char* pVCodecName,
+         const char* pACodecName,
+         const char* pVPreset,
+         int Width, int Height,
+         int FramerateNum, int FramerateDen,
+         int Frequency, int Channels,
+         int VQuality, int AQuality)
 {    
     AddFileLogRaw = pAddFileLogRaw;
     av_log_set_callback( &LogCallback );
 
     g_Width = Width;
     g_Height = Height;
-    g_Framerate = Framerate;
+    g_Framerate.num = FramerateNum;
+    g_Framerate.den = FramerateDen;
     g_Frequency = Frequency;
     g_Channels = Channels;
+    g_VQuality = VQuality;
+    g_AQuality = AQuality;
+    g_pPreset = pVPreset;
 
     // initialize libav and register all codecs and formats
     av_register_all();
-    
-    AVWrapper_GetList();
 
-    // allocate the output media context
-#if LIBAVCODEC_VERSION_MAJOR >= 54
-    avformat_alloc_output_context2(&g_pContainer, NULL, "mp4", pFilename);
-#else
-    g_pFormat = av_guess_format(NULL, pFilename, NULL);
+    // find format
+    g_pFormat = av_guess_format(pFormatName, NULL, NULL);
     if (!g_pFormat)
-        FatalError("guess_format");
+        FatalError("Format \"%s\" was not found", pFormatName);
 
     // allocate the output media context
     g_pContainer = avformat_alloc_context();
-    if (g_pContainer)
-    {
-        g_pContainer->oformat = g_pFormat;
-        snprintf(g_pContainer->filename, sizeof(g_pContainer->filename), "%s", pFilename);
-    }
-#endif
     if (!g_pContainer)
         FatalError("Could not allocate output context");
 
-    g_pFormat = g_pContainer->oformat;
+    g_pContainer->oformat = g_pFormat;
 
-    enum CodecID VideoCodecID = g_pFormat->video_codec;//CODEC_ID_H264;
-    enum CodecID AudioCodecID = g_pFormat->audio_codec;
+    // append extesnion to filename
+    snprintf(g_pContainer->filename, sizeof(g_pContainer->filename),
+             "%s.%*s",
+             pFilename,
+             strcspn(g_pFormat->extensions, ","), g_pFormat->extensions);
 
+    // find codecs
+    g_pVCodec = avcodec_find_encoder_by_name(pVCodecName);
+    g_pACodec = avcodec_find_encoder_by_name(pACodecName);
+
+    // add audio and video stream to container
     g_pVStream = NULL;
     g_pAStream = NULL;
-    if (VideoCodecID != CODEC_ID_NONE)
-    {
-        g_pVCodec = avcodec_find_encoder(VideoCodecID);
-        if (!g_pVCodec)
-            FatalError("Video codec not found");
-        AddVideoStream(VideoCodecID);
-    }
+
+    if (g_pVCodec)
+        AddVideoStream();
+    else
+        Log("Video codec \"%s\" was not found; video will be ignored.\n", pVCodecName);
 
-    if (AudioCodecID != CODEC_ID_NONE)
-    {
-        g_pACodec = avcodec_find_encoder(AudioCodecID);
-        if (!g_pACodec)
-            FatalError("Audio codec not found");
-        AddAudioStream(AudioCodecID);
-    }
+    if (g_pACodec)
+        AddAudioStream();
+    else
+        Log("Audio codec \"%s\" was not found; audio will be ignored.\n", pACodecName);
+
+    if (!g_pAStream && !g_pVStream)
+        FatalError("No video, no audio, aborting...");
 
     if (g_pAStream)
     {
@@ -414,17 +394,18 @@
     }
 
     // write format info to log
-    av_dump_format(g_pContainer, 0, pFilename, 1);
+    av_dump_format(g_pContainer, 0, g_pContainer->filename, 1);
 
     // open the output file, if needed
     if (!(g_pFormat->flags & AVFMT_NOFILE))
     {
-        if (avio_open(&g_pContainer->pb, pFilename, AVIO_FLAG_WRITE) < 0)
+        if (avio_open(&g_pContainer->pb, g_pContainer->filename, AVIO_FLAG_WRITE) < 0)
             FatalError("Could not open output file (%s)", pFilename);
     }
 
     // write the stream header, if any
     avformat_write_header(g_pContainer, NULL);
+
     g_pVFrame->pts = -1;
 }
 
@@ -439,32 +420,27 @@
     // write the trailer, if any.
     av_write_trailer(g_pContainer);
 
-    // close each codec
-    if( g_pVStream )
+    // close the output file
+    if (!(g_pFormat->flags & AVFMT_NOFILE))
+        avio_close(g_pContainer->pb);
+
+    // free everything
+    if (g_pVStream)
     {
-        avcodec_close(g_pVStream->codec);
+        avcodec_close(g_pVideo);
+        av_free(g_pVideo);
+        av_free(g_pVStream);
         av_free(g_pVFrame);
     }
-    if( g_pAStream )
+    if (g_pAStream)
     {
-        avcodec_close(g_pAStream->codec);
+        avcodec_close(g_pAudio);
+        av_free(g_pAudio);
+        av_free(g_pAStream);
         av_free(g_pAFrame);
         av_free(g_pSamples);
         fclose(g_pSoundFile);
     }
 
-    // free the streams
-    int i;
-    for (i = 0; i < g_pContainer->nb_streams; i++)
-    {
-        av_freep(&g_pContainer->streams[i]->codec);
-        av_freep(&g_pContainer->streams[i]);
-    }
-
-    // close the output file
-    if (!(g_pFormat->flags & AVFMT_NOFILE))
-        avio_close(g_pContainer->pb);
-
-    // free the stream 
     av_free(g_pContainer);
 }