[Libav-user] ASS subtitle header missing while encoding

Taha Ansari mtaha.ansari at gmail.com
Mon Dec 9 14:49:27 CET 2013


On Mon, Dec 9, 2013 at 2:55 PM, Taha Ansari <mtaha.ansari at gmail.com> wrote:

> Hi all!
>
> I am writing a small test program that will convert .SRT subtitle file to
> .ASS format.
>
> original SRT file (subtitle.srt):
>
> 1
> 00:00:02,000 --> 00:00:06,000
> Hello
>
> 2
> 00:00:15,000 --> 00:00:20,000
> World
>
> 3
> 00:01:00,000 --> 00:01:10,000
> !!Again!!
>
> Demo code:
>
> #include "stdafx.h"
>
> //using namespace std;
>
> #include <iostream>
> #include <fstream>
>
> #include <string>
> #include <vector>
> #include <map>
> //#include <DShow.h>
> #include <conio.h>
>
> #include <deque>
> #include <queue>
>
> #include <math.h>
> #include <stdlib.h>
> #include <stdio.h>
> #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
> extern "C"
> {
> #include "libavcodec/avcodec.h"
> #include "libavformat/avformat.h"
> #include "libavdevice/avdevice.h"
> #include "libswscale/swscale.h"
> #include "libavutil/dict.h"
> #include "libavutil/error.h"
> #include "libavutil/opt.h"
> #include <libavutil/fifo.h>
> #include <libavutil/imgutils.h>
> #include <libavutil/samplefmt.h>
> #include <libavutil/time.h>
> #include <libavformat/avio.h>
> #include <libavutil/avstring.h>
> #include <libavutil/mathematics.h>
> #include "libavfilter/avfiltergraph.h"
> #include "libavfilter/avcodec.h"
> #include "libavfilter/buffersink.h"
> #include "libavfilter/buffersrc.h"
>
> #include <libswresample/swresample.h>
> }
>
> //++decoder++//
> AVFormatContext    *pFormatCtx = 0;
> int                subtitleIndex = -1;
> AVPacket        packet;
> AVCodecContext    *pCodecCtxSubtitle = 0;
> AVCodec            *pCodecSubtitle = 0;
> AVSubtitle        subtitle;
> //--decoder--
>
> //++encoder++
> AVFormatContext    *pOutputFormatCtx=0;
> AVStream        *subtitle_st=0;
> AVOutputFormat    *outputFmt=0;
> AVCodec            *subtitle_codec=0;
> /* Add an output stream. */
> AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,
>     enum AVCodecID codec_id)
> {
>     AVCodecContext *c;
>     AVStream *st;
>
>     /* find the encoder */
>     *codec = avcodec_find_encoder(codec_id);
>     if (!(*codec)) {
>         fprintf(stderr, "Could not find encoder for '%s'\n",
>             avcodec_get_name(codec_id));
>         exit(1);
>     }
>
>     st = avformat_new_stream(oc, *codec);
>     if (!st) {
>         fprintf(stderr, "Could not allocate stream\n");
>         exit(1);
>     }
>     st->id = oc->nb_streams-1;
>     c = st->codec;
>
>     avcodec_get_context_defaults3( c, *codec);
>
>     switch ((*codec)->type)
>     {
>     case AVMEDIA_TYPE_SUBTITLE:
>         break;
>     default:
>         break;
>     }
>
>     /* Some formats want stream headers to be separate. */
>     if (oc->oformat->flags & AVFMT_GLOBALHEADER)
>         c->flags |= CODEC_FLAG_GLOBAL_HEADER;
>
>     return st;
> }
>
> void open_subtitle(AVFormatContext *oc, AVCodec *codec, AVStream *st)
> {
>     int ret;
>     AVCodecContext *c = st->codec;
>
>     /* open the codec */
>     ret = avcodec_open2(c, codec, NULL);
>     if (ret < 0)
>     {
>         exit(1);
>     }
> }
> //--encoder--
>
> int main (int argc, char **argv)
> {
>     av_register_all();
>     avcodec_register_all();
>     avfilter_register_all();
>
>     char inputFilename[90]="subtitle.srt";
>     char outputFilename[90]="temp.ass";
>     int rv = 0;
>
>     // ++decoder++
>     int got_subtitle = 0;
>     pFormatCtx = avformat_alloc_context();
>
>     rv = avformat_open_input(&pFormatCtx, inputFilename, NULL, NULL);
>
>     for(int i=0; i < pFormatCtx->nb_streams; i++)
>     {
>
> if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_SUBTITLE)
>         {
>             subtitleIndex = i;
>         }
>     }
>     if ( subtitleIndex >= 0 )
>     {
>         pCodecCtxSubtitle=pFormatCtx->streams[subtitleIndex]->codec;
>
>         pCodecSubtitle = avcodec_find_decoder(pCodecCtxSubtitle->codec_id);
>         if(pCodecSubtitle==NULL)
>         {
>             return -1; // Codec not found
>         }
>         // Open codec
>         AVDictionary *codecDictOptions = NULL;
>         if(avcodec_open2(pCodecCtxSubtitle, pCodecSubtitle,
> &codecDictOptions) < 0 )//error
>         {
>         }
>     }
>     //--decoder--
>
>     //++encoder++
>     int ret;
>     avformat_alloc_output_context2(&pOutputFormatCtx, NULL, NULL,
> outputFilename);
>     if (!pOutputFormatCtx)
>     {
>         rv = 1;
>     }
>     //outputFmt = pOutputFormatCtx->oformat;
>     outputFmt = av_guess_format( NULL, outputFilename, NULL );
>     if (outputFmt->subtitle_codec != AV_CODEC_ID_NONE)
>     {
>         subtitle_st = add_stream(pOutputFormatCtx, &subtitle_codec,
> outputFmt->subtitle_codec);
>     }
>
>     if (subtitle_st)
>         open_subtitle(pOutputFormatCtx, subtitle_codec, subtitle_st);
>
>     av_dump_format(pOutputFormatCtx, 0, outputFilename, 1);
>     /* open the output file, if needed */
>     if (!(outputFmt->flags & AVFMT_NOFILE))
>     {
>         if (avio_open(&pOutputFormatCtx->pb, outputFilename,
> AVIO_FLAG_WRITE) < 0)
>         {
>             rv = 1;
>         }
>         else
>         {
>             /* Write the stream header, if any. */
>             if (avformat_write_header(pOutputFormatCtx, NULL) < 0)
>             {
>                 rv = 1;
>             }
>         }
>     }
>
>     AVCodecContext *c = subtitle_st->codec;
>     //--encoder--
>
>     while( av_read_frame( pFormatCtx, &packet ) >= 0 )
>     {
>         if(packet.stream_index != subtitle_st->index)
>             continue;
>         int gotSubtitle = 0;
>         rv = avcodec_decode_subtitle2( pCodecCtxSubtitle, &subtitle,
> &gotSubtitle, &packet );
>         uint64_t bufferSize = 1024 * 1024 ;
>         uint8_t *buffer = new uint8_t[bufferSize];
>         memset(buffer, 0, bufferSize);
>         if( rv >= 0 )
>         {
>             rv = avcodec_encode_subtitle( subtitle_st->codec, buffer,
> bufferSize, &subtitle );
>             if ( rv >= 0 )
>             {
>                 AVPacket outPacket;
>                 av_init_packet(&outPacket);
>                 outPacket.data = buffer;
>                 outPacket.size = strlen((const char*)buffer);
>                 rv = av_interleaved_write_frame(pOutputFormatCtx,
> &outPacket);
>                 av_free_packet(&outPacket);
>             }
>         }
>         delete [] buffer;
>     }
>
>     if ( pCodecCtxSubtitle)
>     {
>         avcodec_close(pCodecCtxSubtitle);
>         pCodecCtxSubtitle= NULL;
>     }
>     if ( pCodecSubtitle )
>     {
>         av_free(pCodecSubtitle);
>         pCodecSubtitle= NULL;
>     }
>     if ( pFormatCtx )
>     {
>         avformat_close_input(&pFormatCtx);
>         avformat_free_context(pFormatCtx);
>         pFormatCtx = NULL;
>     }
>     avcodec_close(subtitle_st->codec);
>     return rv;
> }
>
> PTS values are not calculated still, it is work in progress; but the above
> code gives me this output:
>
> output temp.ass file:
>
> Dialogue: 0,0:00:00.00,0:00:00.00,Default,Hello
> Dialogue: 0,0:00:00.00,0:00:00.00,Default,World
> Dialogue: 0,0:00:00.00,0:00:00.00,Default,!!Again!!
>
> It is clearly missing all header information.
>
> When i run ffmpeg command line to convert orignal SRT file using:
>
> ffmpeg -i subtitle.srt temp.ass
>
> I get:
>
> [Script Info]
> ScriptType: v4.00+
>
> [V4+ Styles]
> Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,
> OutlineColour, BackColour, Bold, Italic, Underline, BorderStyle, Outline,
> Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding
> Style:
> Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,1,1,0,2,10,10,10,0,0
>
> [Events]
> Format: Layer, Start, End, Style, Text
> Dialogue: 0,0:00:02.00,0:00:06.00,Default,Hello
> Dialogue: 0,0:00:15.00,0:00:20.00,Default,World
> Dialogue: 0,0:01:00.00,0:01:10.00,Default,!!Again!!
>
> so my question is, even though I am calling avformat_write_header()
> function, why isn't this header information being written through my code?
>
> Thanks in advance...
>

Perhaps some code was missing in my original email, which I have added:

    if (pCodecCtxSubtitle && pCodecCtxSubtitle->subtitle_header)
    {
        /* ASS code assumes this buffer is null terminated so add extra
byte. */
        subtitle_st->codec->subtitle_header = (uint8_t*)
av_mallocz(pCodecCtxSubtitle->subtitle_header_size + 1);
        if (!subtitle_st->codec->subtitle_header)
        {
            ret = AVERROR(ENOMEM);
            //goto dump_format;
        }
        memcpy(subtitle_st->codec->subtitle_header,
pCodecCtxSubtitle->subtitle_header,
pCodecCtxSubtitle->subtitle_header_size);
        subtitle_st->codec->subtitle_header_size =
pCodecCtxSubtitle->subtitle_header_size;
    }

So, final code now looks like this:


#include "stdafx.h"

//using namespace std;

#include <iostream>
#include <fstream>

#include <string>
#include <vector>
#include <map>
//#include <DShow.h>
#include <conio.h>

#include <deque>
#include <queue>

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libavutil/dict.h"
#include "libavutil/error.h"
#include "libavutil/opt.h"
#include <libavutil/fifo.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/time.h>
#include <libavformat/avio.h>
#include <libavutil/avstring.h>
#include <libavutil/mathematics.h>
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/avcodec.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"

#include <libswresample/swresample.h>
}

//++decoder++//
AVFormatContext    *pFormatCtx = 0;
int                subtitleIndex = -1;
AVPacket        packet;
AVCodecContext    *pCodecCtxSubtitle = 0;
AVCodec            *pCodecSubtitle = 0;
AVSubtitle        subtitle;
//--decoder--

//++encoder++
AVFormatContext    *pOutputFormatCtx=0;
AVStream        *subtitle_st=0;
AVOutputFormat    *outputFmt=0;
AVCodec            *subtitle_codec=0;
/* Add an output stream. */
AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,
    enum AVCodecID codec_id)
{
    AVCodecContext *c;
    AVStream *st;

    /* find the encoder */
    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec)) {
        fprintf(stderr, "Could not find encoder for '%s'\n",
            avcodec_get_name(codec_id));
        exit(1);
    }

    st = avformat_new_stream(oc, *codec);
    if (!st) {
        fprintf(stderr, "Could not allocate stream\n");
        exit(1);
    }
    st->id = oc->nb_streams-1;
    c = st->codec;

    avcodec_get_context_defaults3( c, *codec);

    switch ((*codec)->type)
    {
    case AVMEDIA_TYPE_SUBTITLE:
        break;
    default:
        break;
    }

    /* Some formats want stream headers to be separate. */
    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        c->flags |= CODEC_FLAG_GLOBAL_HEADER;

    return st;
}

void open_subtitle(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
    int ret;
    AVCodecContext *c = st->codec;

    /* open the codec */
    ret = avcodec_open2(c, codec, NULL);
    if (ret < 0)
    {
        exit(1);
    }
}
//--encoder--

int main (int argc, char **argv)
{
    av_register_all();
    avcodec_register_all();
    avfilter_register_all();

    char inputFilename[90]="subtitle.srt";
    char outputFilename[90]="temp.ass";
    int rv = 0;

    // ++decoder++
    int got_subtitle = 0;
    pFormatCtx = avformat_alloc_context();

    rv = avformat_open_input(&pFormatCtx, inputFilename, NULL, NULL);

    for(int i=0; i < pFormatCtx->nb_streams; i++)
    {

if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_SUBTITLE)
        {
            subtitleIndex = i;
        }
    }
    if ( subtitleIndex >= 0 )
    {
        pCodecCtxSubtitle=pFormatCtx->streams[subtitleIndex]->codec;

        pCodecSubtitle = avcodec_find_decoder(pCodecCtxSubtitle->codec_id);
        if(pCodecSubtitle==NULL)
        {
            return -1; // Codec not found
        }
        // Open codec
        AVDictionary *codecDictOptions = NULL;
        if(avcodec_open2(pCodecCtxSubtitle, pCodecSubtitle,
&codecDictOptions) < 0 )//error
        {
        }
    }
    //--decoder--

    //++encoder++
    int ret;
    avformat_alloc_output_context2(&pOutputFormatCtx, NULL, NULL,
outputFilename);
    if (!pOutputFormatCtx)
    {
        rv = 1;
    }
    //outputFmt = pOutputFormatCtx->oformat;
    outputFmt = av_guess_format( NULL, outputFilename, NULL );
    if (outputFmt->subtitle_codec != AV_CODEC_ID_NONE)
    {
        subtitle_st = add_stream(pOutputFormatCtx, &subtitle_codec,
outputFmt->subtitle_codec);
    }

    if (subtitle_st)
        open_subtitle(pOutputFormatCtx, subtitle_codec, subtitle_st);
    if (pCodecCtxSubtitle && pCodecCtxSubtitle->subtitle_header)
    {
        /* ASS code assumes this buffer is null terminated so add extra
byte. */
        subtitle_st->codec->subtitle_header = (uint8_t*)
av_mallocz(pCodecCtxSubtitle->subtitle_header_size + 1);
        if (!subtitle_st->codec->subtitle_header)
        {
            ret = AVERROR(ENOMEM);
            //goto dump_format;
        }
        memcpy(subtitle_st->codec->subtitle_header,
pCodecCtxSubtitle->subtitle_header,
pCodecCtxSubtitle->subtitle_header_size);
        subtitle_st->codec->subtitle_header_size =
pCodecCtxSubtitle->subtitle_header_size;
    }

    av_dump_format(pOutputFormatCtx, 0, outputFilename, 1);
    /* open the output file, if needed */
    if (!(outputFmt->flags & AVFMT_NOFILE))
    {
        if (avio_open(&pOutputFormatCtx->pb, outputFilename,
AVIO_FLAG_WRITE) < 0)
        {
            rv = 1;
        }
        else
        {
            /* Write the stream header, if any. */
            if (avformat_write_header(pOutputFormatCtx, NULL) < 0)
            {
                rv = 1;
            }
        }
    }

    AVCodecContext *c = subtitle_st->codec;
    //--encoder--

    while( av_read_frame( pFormatCtx, &packet ) >= 0 )
    {
        if(packet.stream_index != subtitle_st->index)
            continue;
        int gotSubtitle = 0;
        rv = avcodec_decode_subtitle2( pCodecCtxSubtitle, &subtitle,
&gotSubtitle, &packet );
        uint64_t bufferSize = 1024 * 1024 ;
        uint8_t *buffer = new uint8_t[bufferSize];
        memset(buffer, 0, bufferSize);
        if( rv >= 0 )
        {
            rv = avcodec_encode_subtitle( subtitle_st->codec, buffer,
bufferSize, &subtitle );
            if ( rv >= 0 )
            {
                AVPacket outPacket;
                av_init_packet(&outPacket);
                outPacket.data = buffer;
                outPacket.size = strlen((const char*)buffer);
                rv = av_interleaved_write_frame(pOutputFormatCtx,
&outPacket);
                av_free_packet(&outPacket);
            }
        }
        delete [] buffer;
    }

    if ( pCodecCtxSubtitle)
    {
        avcodec_close(pCodecCtxSubtitle);
        pCodecCtxSubtitle= NULL;
    }
    if ( pCodecSubtitle )
    {
        av_free(pCodecSubtitle);
        pCodecSubtitle= NULL;
    }
    if ( pFormatCtx )
    {
        avformat_close_input(&pFormatCtx);
        avformat_free_context(pFormatCtx);
        pFormatCtx = NULL;
    }
    avcodec_close(subtitle_st->codec);
    return rv;
}

Still, based on scenario posted with 1st email, it is not working. Hope to
get some answers soon...

Thanks...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ffmpeg.org/pipermail/libav-user/attachments/20131209/d6d96309/attachment.html>


More information about the Libav-user mailing list