Как установить формат декодирования пикселей в libavcodec? - PullRequest
11 голосов
/ 11 марта 2012

Я декодирую видео через libavcodec, используя следующий код:

//Open input file
if(avformat_open_input(&ctx, filename, NULL, NULL)!=0)
    return FALSE; // Couldn't open file
if(avformat_find_stream_info(ctx, NULL)<0)
    return FALSE; // Couldn't find stream information
videoStream = -1;
//find video stream
for(i=0; i<ctx->nb_streams; i++)
{       
    if((ctx->streams[i])->codec->codec_type==AVMEDIA_TYPE_VIDEO)
    {
        videoStream=i;
        break;
    }
}
if (videoStream == -1)
    return FALSE; // Didn't find a video stream
video_codec_ctx=ctx->streams[videoStream]->codec;
//find decoder
video_codec=avcodec_find_decoder(video_codec_ctx->codec_id);
if(video_codec==NULL)
    return FALSE; // Codec not found
if(avcodec_open(video_codec_ctx, video_codec)<0)
    return -1; // Could not open codec
video_frame=avcodec_alloc_frame();
scaled_frame=avcodec_alloc_frame();
static struct SwsContext *img_convert_ctx; 
if(img_convert_ctx == NULL) 
{
      int w = video_codec_ctx->width;
      int h = video_codec_ctx->height;
      img_convert_ctx = sws_getContext(w, h, 
                        video_codec_ctx->pix_fmt, 
                        w, h, dst_pix_fmt, SWS_BICUBIC, 
                        NULL, NULL, NULL);
      if(img_convert_ctx == NULL) {
        fprintf(stderr, "Cannot initialize the conversion context!\n");
        return FALSE;
      }
}
while(b_play) 
{
    if (av_read_frame(ctx, &packet) < 0)
    {
        break;
    }
    if(packet.stream_index==videoStream) {
    // Decode video frame   
        avcodec_decode_video2(video_codec_ctx, video_frame, &frameFinished,
                         &packet);
        // Did we get a video frame?
        if(frameFinished) 
        {
            if (video_codec_ctx->pix_fmt != dst_pix_fmt)
            {                       
                if (video_codec_ctx->pix_fmt != dst_pix_fmt)            
                     sws_scale(img_convert_ctx, video_frame->data, 
                              video_frame->linesize, 0, 
                              video_codec_ctx->height, 
                              scaled_frame->data, scaled_frame->linesize);              
            }           
        }
}
av_free_packet(&packet);
}

Код работает правильно, но необходимо преобразовать каждый кадр в требуемый формат.Можно ли установить формат пикселя для декодирования, чтобы получить правильный формат без sws_scale?

Большое спасибо за ваши ответы.

1 Ответ

18 голосов
/ 11 марта 2012

ffmpeg экземпляров AVCodec (статические объекты "фабричного" декодера) каждый определяет массив поддерживаемых ими форматов пикселей, оканчивающихся значением -1.

Объекты AVCodecContext (экземпляр декодера) имеют указатель функции обратного вызова, называемый get_format: это указатель функции в этой структуре.

Эта функция обратного вызова вызывается в некоторый момент инициализации кодека с помощью массива поддерживаемых форматов объекта фабрики AVCodec, и предполагается, что обратный вызов выбирает один из форматов из этого массива (вроде как "выберите карта, любая карта ") и вернуть это значение. Реализация по умолчанию этого обратного вызова get_format представляет собой функцию с именем avcodec_default_get_format. (Это установлено avcodec_get_context_defaults2). Эта функция по умолчанию реализует логику «выбора формата» довольно просто: она выбирает первый элемент массива, который не является аппаратно-приемлемым форматом пикселей.

Если вы хотите, чтобы кодек работал с другим форматом пикселей, вы можете установить собственный обратный вызов get_format в объект контекста. Однако обратный вызов должен возвращать одно из значений в массиве (например, выбор из меню). Он не может вернуть произвольное значение. Кодек будет поддерживать только те форматы, которые он указывает в массиве.

Пройдите по множеству доступных форматов и выберите лучший. Если вам повезет, это именно то, что вам действительно нужно, и функция sws_scale не будет выполнять преобразование формата пикселей. (Если, кроме того, вы не запрашиваете масштабировать или обрезать изображение, sws_scale должен признать, что преобразование является пустяком.)

...