1

I have been trying to transcode subtitles from format SubRip (.srt) to MPEG4 Timed Text to mux them into a MP4 container, where there is already audio and video. Doing so from command line with ffmpeg is trivial:

ffmpeg -i subtitles.srt -i video.mp4 -c:v copy -c:a copy -c:s mov_text videoWithSubtitles.mp4

However, using avcodec, I am able to open both files and read from them, but when I try to open the AVCodecContext for the (encoder) codec AV_CODEC_ID_MOV_TEXT, I receive the following message:

Error code: -1094995529
Error occurred: Invalid data found when processing input

The code that produces the error is the following:

AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MOV_TEXT);
if (!codec) {
    NSLog(@"Error finding encoder");
    exit(-1);
}

AVCodecContext *codecContext = avcodec_alloc_context3(codec);
if (!codecContext) {
    NSLog(@"Error allocating context");
    exit(-1);
}

if (avcodec_is_open(codecContext) == 0) {
    NSLog(@"output codec context is closed, opening");
    ret = avcodec_open2(codecContext, codec, NULL);
    if (ret < 0) {
        NSLog(@"Error code: %i",ret);
        NSLog(@"Error occurred: %s", av_err2str(ret));
        NSLog(@"Error opening encoder");
        exit(-1);
    }
}

The code works for all decoders (the same codec working in decode mode) and also with other encoders, mostly video or audio, but it also does not work for AV_CODEC_ID_SUBRIP or AV_CODEC_ID_SRT (set encoder).

marsop
  • 323
  • 4
  • 19
  • 1
    I have identified the problem, the **AVCodecContext** needs to have the Subtitles Header set. I am not sure what should be inside, but copying it from a codec context for **SRT** seems to do the trick – marsop Oct 08 '14 at 12:36

1 Answers1

0

Error codes from ffmpeg (error.h from avutil) : http://ffmpeg.org/doxygen/trunk/error_8h_source.html

It turns out the value you specified is :

#define AVERROR_INVALIDDATA        FFERRTAG( 'I','N','D','A')

The -1094995529 becomes -0x41444E49 and when you look at those letters, in ACSII, 0x41 = 'A', 0x44 = 'D', 0x4E = 'N, and 0x49 = 'I'. Due to the macro/etc things are reversed, so ADNI becomes INDA, which you can see from the #define snippet, is the AVERROR_INVALIDDATA defined FFERRTAG( 'I','N','D','A').

The rest of the error codes are in that file and I've pasted them below here :

#define AVERROR_BSF_NOT_FOUND      FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found
#define AVERROR_BUG                FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2
#define AVERROR_BUFFER_TOO_SMALL   FFERRTAG( 'B','U','F','S') ///< Buffer too small
#define AVERROR_DECODER_NOT_FOUND  FFERRTAG(0xF8,'D','E','C') ///< Decoder not found
#define AVERROR_DEMUXER_NOT_FOUND  FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found
#define AVERROR_ENCODER_NOT_FOUND  FFERRTAG(0xF8,'E','N','C') ///< Encoder not found
#define AVERROR_EOF                FFERRTAG( 'E','O','F',' ') ///< End of file
#define AVERROR_EXIT               FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted
#define AVERROR_EXTERNAL           FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library
#define AVERROR_FILTER_NOT_FOUND   FFERRTAG(0xF8,'F','I','L') ///< Filter not found
#define AVERROR_INVALIDDATA        FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input
#define AVERROR_MUXER_NOT_FOUND    FFERRTAG(0xF8,'M','U','X') ///< Muxer not found
#define AVERROR_OPTION_NOT_FOUND   FFERRTAG(0xF8,'O','P','T') ///< Option not found
#define AVERROR_PATCHWELCOME       FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome
#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found
#define AVERROR_STREAM_NOT_FOUND   FFERRTAG(0xF8,'S','T','R') ///< Stream not found
#define AVERROR_BUG2               FFERRTAG( 'B','U','G',' ')
#define AVERROR_UNKNOWN            FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library
#define AVERROR_EXPERIMENTAL       (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
#define AVERROR_INPUT_CHANGED      (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED)
#define AVERROR_OUTPUT_CHANGED     (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED)
#define AVERROR_HTTP_BAD_REQUEST   FFERRTAG(0xF8,'4','0','0')
#define AVERROR_HTTP_UNAUTHORIZED  FFERRTAG(0xF8,'4','0','1')
#define AVERROR_HTTP_FORBIDDEN     FFERRTAG(0xF8,'4','0','3')
#define AVERROR_HTTP_NOT_FOUND     FFERRTAG(0xF8,'4','0','4')
#define AVERROR_HTTP_OTHER_4XX     FFERRTAG(0xF8,'4','X','X')
#define AVERROR_HTTP_SERVER_ERROR  FFERRTAG(0xF8,'5','X','X')

The AVFormatContext is tricky. I eventually got mine to work, but I'm sure I do a ton of extra copies and crazy stuff.

av_register_all();
avformat_network_init();

//Allocate space and setup the the object to be used for storing 
// all info needed for this connection
context = avformat_alloc_context();

AVDictionary *opts = 0;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);

//open rtsp
if (avformat_open_input(&context, URL, NULL, &opts) != 0){
    return 1; //Error 1
}

//maximum time in microseconds during which the input should be analyzed
context->max_analyze_duration = 10000000;

if (avformat_find_stream_info(context,NULL) < 0){
    return 2; //Error 2
}

//search video stream
for(int i=0; i<(int)context->nb_streams; i++){
    if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        video_stream_index = i;
}

oc = avformat_alloc_context();

//start reading packets from stream and write them to file
av_read_play(context);  //start the stream

if(curVidFormat == MJPEG) {
    codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
}
else {
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
}

if (!codec) return 3; //Error 3
ccontext = avcodec_alloc_context3(codec);    

avcodec_get_context_defaults3(ccontext, codec);
avcodec_copy_context(ccontext,context->streams[video_stream_index]->codec);

int errorCode = avcodec_open2(ccontext, codec, NULL);
if(errorCode<0)
{
    printf("avcodec_open2 failed with errorCode:%d !",errorCode);
    exit(1);
}

//create frame to be used for storing frames coming out 
// of avcodec_decode_video2
int curAVFramesize = avpicture_get_size(PIX_FMT_YUV420P, 
     ccontext->width, ccontext->height);
curAVFramePicBuffer = (uint8_t*)(av_malloc(curAVFramesize));
curAVFrame=avcodec_alloc_frame();
avpicture_fill((AVPicture *)curAVFrame,curAVFramePicBuffer,
     PIX_FMT_YUV420P,ccontext->width, ccontext->height);
returnAVFramesize = avpicture_get_size(PIX_FMT_YUV420P, ccontext->width, ccontext->height);

//allocate and create frame to be used for resizing of frames
int resizedFramesize = avpicture_get_size(PIX_FMT_YUV420P, outputWidth,
    outputHeight);
resizedFramePicBuffer = (uint8_t*)(av_malloc(resizedFramesize));
resizedFrame=avcodec_alloc_frame();
avpicture_fill((AVPicture *)resizedFrame,resizedFramePicBuffer,
    PIX_FMT_YUV420P,outputWidth, outputHeight);

//Could be used to convert between formats
//In this case just scaling the image
img_convert_ctx = sws_getContext(ccontext->width, ccontext->height,
     ccontext->pix_fmt, width, height, PIX_FMT_YUV420P, 
     SWS_BICUBIC, NULL, NULL, NULL);

Obviously I do not do subtitles here, but if you are trying to load the subtitles to write them in as timed text, it makes sense that you should load them into the format context.

LawfulEvil
  • 2,267
  • 24
  • 46