2010-02-18 13 views
16

मैं ffmpeg के साथ एक एन्कोडर लिखना चाहता हूं जो IFrames (keyframes) को उन स्थितियों पर रख सकता है जिन्हें मैं चाहता हूं। इसके लिए मुझे ट्यूटोरियल या संदर्भ सामग्री कहां मिल सकती है?ffmpeg के साथ एक वीडियो एन्कोडर कैसे लिखें जो स्पष्ट रूप से कीफ्रेम की स्थिति को नियंत्रित करता है?

पीएस
क्या यह मेनकोडर या किसी ओपनसोर्स एन्कोडर के साथ ऐसा करना संभव है। मैं H263 फ़ाइल एन्कोड करना चाहता हूँ। मैं linux के लिए & के तहत लिख रहा हूँ।

+1

.. क्या भाषा? – Shoban

+0

मैं सी, सी ++ में लिख सकता हूं लेकिन ट्यूटोरियल या संदर्भ किसी भी भाषा में हो सकता है। मैं सिर्फ ffmpeg api को जानना/समझना चाहता हूं। – SunnyShah

उत्तर

18

आपको libavcodec दस्तावेज़ीकरण - विशेष रूप से, avcodec_encode_video() पर देखना होगा। मैंने पाया कि सबसे अच्छा उपलब्ध दस्तावेज ffmpeg हेडर फाइलों और एपीआई नमूना स्रोत कोड में है जो ffmpeg स्रोत के साथ प्रदान किया गया है। विशेष रूप से, libavcodec/api-example.c या यहां तक ​​कि ffmpeg.c देखें।

एक फ्रेम को मजबूर करने के लिए, आपको उस चित्र के चित्र_ टाइप सदस्य को सेट करना होगा जिसे आप एन्कोडिंग कर रहे हैं 1: 1 एक I फ्रेम है, 2 एक पी फ्रेम है, और मुझे याद नहीं है कि कोड क्या है मेरे सिर के शीर्ष पर एक बी फ्रेम के लिए ... इसके अलावा, key_frame सदस्य को 0

कुछ प्रारंभिक सामग्री here और here उपलब्ध है, लेकिन मुझे वास्तव में यह नहीं पता कि यह कितना अच्छा है।

आपको सावधान रहना होगा कि आप एपीआई कॉल की आवश्यकता वाले फ्रेम ऑब्जेक्ट्स को आवंटित करते हैं। api-example.c मेरी राय में, जहां तक ​​जाता है, आपकी सबसे अच्छी शर्त है। फ़ंक्शन video_encode_example() की तलाश करें - यह उन सभी महत्वपूर्ण चीजों को संक्षेप में दिखाता है जो आपको चिंता करने की ज़रूरत है - दूसरी कॉल पर विशेष ध्यान दें avcodec_encode_video() जो एक पूर्ण चित्र तर्क पास करता है - इसे वीडियो के अंतिम फ्रेम प्राप्त करने की आवश्यकता होती है एमपीईजी वीडियो अनुक्रम से बाहर एन्कोड किया गया है और आप कुछ फ्रेम की देरी के साथ समाप्त हो सकता है।

+2

बीटीडब्ल्यू, तस्वीर के 'pict_type' सदस्य के मान' AV_PICTURE_TYPE_I', 'AV_PICTURE_TYPE_P',' AV_PICTURE_TYPE_B', और इतने पर हैं ... –

3

GStreamerdecent documentation है, (हालांकि देशी एपीआई सी है) एक number of languages के लिए बाइंडिंग है, और किसी भी वीडियो प्रारूप आप के लिए प्लग-इन, gstreamer-ffmpeg के माध्यम से एच .263 सहित पा सकते हैं समर्थन करता है।

1

आपको libavcodec लाइब्रेरी की आवश्यकता होगी, पहले चरण के लिए मुझे लगता है कि आप ffmplayg स्रोत कोड में ffplay.c फ़ाइल में इसके उपयोग के बारे में जान सकते हैं। यह आपको बहुत कुछ बताएगा। आप rtstegvideo.sourceforge.net पर वीडियो के बारे में भी मेरी परियोजना की जांच कर सकते हैं।

इस सहायता की आशा करें।

1

यदि आप जावा प्रोग्रामर हैं तो Xuggler का उपयोग करें।

4

api-example.c का एक अप-टू-डेट संस्करण http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html

यह एक और अपेक्षाकृत कम समारोह में पूरे वीडियो एन्कोडिंग करता है में पाया जा सकता। तो यह शायद शुरू करने के लिए एक अच्छी जगह है। संकलित करें और इसे चलाएं। और उसके बाद इसे संशोधित करना शुरू करें जब तक कि आप जो चाहते हैं वह करता है।

इसमें ऑडियो एन्कोडिंग और ऑडियो & वीडियो डिकोडिंग उदाहरण भी हैं।

+0

अद्यतित संस्करण के लिए आधिकारिक दस्तावेज़ीकरण देखना बेहतर है : http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html –

0

FFmpeg 2,7

Ori Pessach's जवाब के आधार पर पर मिनिमल runnable उदाहरण के लिए, नीचे एक न्यूनतम उदाहरण है कि फार्म के तख्ते उत्पन्न करता है।

  • मैं
  • पी
  • बी
  • पी
  • ...

कि फ्रेम प्रकार को नियंत्रित कोड के मुख्य भागों हैं:

c = avcodec_alloc_context3(codec); 
/* Minimal distance of I-frames. This is the maximum value allowed, 
or else we get a warning at runtime. */ 
c->keyint_min = 600; 
/* Or else it defaults to 0 b-frames are not allowed. */ 
c->max_b_frames = 1; 

और:

frame->key_frame = 0; 
switch (frame->pts % 4) { 
    case 0: 
     frame->key_frame = 1; 
     frame->pict_type = AV_PICTURE_TYPE_I; 
    break; 
    case 1: 
    case 3: 
     frame->pict_type = AV_PICTURE_TYPE_P; 
    break; 
    case 2: 
     frame->pict_type = AV_PICTURE_TYPE_B; 
    break; 
} 

हम तो साथ फ्रेम प्रकार सत्यापित कर सकते हैं:

ffprobe -select_streams v \ 
    -show_frames \ 
    -show_entries frame=pict_type \ 
    -of csv \ 
    tmp.h264 

में उल्लेख किया: https://superuser.com/questions/885452/extracting-the-index-of-key-frames-from-a-video-using-ffmpeg

कुछ नियम FFmpeg द्वारा लागू किया गया है, भले ही मैं उन्हें दूर करने के लिए प्रयास करें:

  • पहला फ्रेम एक आई-फ्रेम
  • प्लेक नहीं कर सकता ई-फ्रेम से पहले एक बी 0 फ्रेम (TODO क्यों?)

Preview of generated output

#include <libavcodec/avcodec.h> 
#include <libavutil/imgutils.h> 
#include <libavutil/opt.h> 
#include <libswscale/swscale.h> 

static AVCodecContext *c = NULL; 
static AVFrame *frame; 
static AVPacket pkt; 
static FILE *file; 
struct SwsContext *sws_context = NULL; 

/* 
Convert RGB24 array to YUV. Save directly to the `frame`, 
modifying its `data` and `linesize` fields 
*/ 
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { 
    const int in_linesize[1] = { 3 * c->width }; 
    sws_context = sws_getCachedContext(sws_context, 
      c->width, c->height, AV_PIX_FMT_RGB24, 
      c->width, c->height, AV_PIX_FMT_YUV420P, 
      0, 0, 0, 0); 
    sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0, 
      c->height, frame->data, frame->linesize); 
} 

/* 
Generate 2 different images with four colored rectangles, each 25 frames long: 

Image 1: 

    black | red 
    ------+----- 
    green | blue 

Image 2: 

    yellow | red 
    -------+----- 
    green | white 
*/ 
uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) { 
    int x, y, cur; 
    rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width); 
    for (y = 0; y < height; y++) { 
     for (x = 0; x < width; x++) { 
      cur = 3 * (y * width + x); 
      rgb[cur + 0] = 0; 
      rgb[cur + 1] = 0; 
      rgb[cur + 2] = 0; 
      if ((frame->pts/25) % 2 == 0) { 
       if (y < height/2) { 
        if (x < width/2) { 
         /* Black. */ 
        } else { 
         rgb[cur + 0] = 255; 
        } 
       } else { 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
        } else { 
         rgb[cur + 2] = 255; 
        } 
       } 
      } else { 
       if (y < height/2) { 
        rgb[cur + 0] = 255; 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
        } else { 
         rgb[cur + 2] = 255; 
        } 
       } else { 
        if (x < width/2) { 
         rgb[cur + 1] = 255; 
         rgb[cur + 2] = 255; 
        } else { 
         rgb[cur + 0] = 255; 
         rgb[cur + 1] = 255; 
         rgb[cur + 2] = 255; 
        } 
       } 
      } 
     } 
    } 
    return rgb; 
} 

/* Allocate resources and write header data to the output file. */ 
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) { 
    AVCodec *codec; 
    int ret; 
    codec = avcodec_find_encoder(codec_id); 
    if (!codec) { 
     fprintf(stderr, "Codec not found\n"); 
     exit(1); 
    } 
    c = avcodec_alloc_context3(codec); 
    if (!c) { 
     fprintf(stderr, "Could not allocate video codec context\n"); 
     exit(1); 
    } 
    c->bit_rate = 400000; 
    c->width = width; 
    c->height = height; 
    c->time_base.num = 1; 
    c->time_base.den = fps; 
    /* I, P, B frame placement parameters. */ 
    c->gop_size = 600; 
    c->max_b_frames = 1; 
    c->keyint_min = 600; 
    c->pix_fmt = AV_PIX_FMT_YUV420P; 
    if (codec_id == AV_CODEC_ID_H264) 
     av_opt_set(c->priv_data, "preset", "slow", 0); 
    if (avcodec_open2(c, codec, NULL) < 0) { 
     fprintf(stderr, "Could not open codec\n"); 
     exit(1); 
    } 
    file = fopen(filename, "wb"); 
    if (!file) { 
     fprintf(stderr, "Could not open %s\n", filename); 
     exit(1); 
    } 
    frame = av_frame_alloc(); 
    if (!frame) { 
     fprintf(stderr, "Could not allocate video frame\n"); 
     exit(1); 
    } 
    frame->format = c->pix_fmt; 
    frame->width = c->width; 
    frame->height = c->height; 
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32); 
    if (ret < 0) { 
     fprintf(stderr, "Could not allocate raw picture buffer\n"); 
     exit(1); 
    } 
} 

/* 
Write trailing data to the output file 
and free resources allocated by ffmpeg_encoder_start. 
*/ 
void ffmpeg_encoder_finish(void) { 
    uint8_t endcode[] = { 0, 0, 1, 0xb7 }; 
    int got_output, ret; 
    do { 
     fflush(stdout); 
     ret = avcodec_encode_video2(c, &pkt, NULL, &got_output); 
     if (ret < 0) { 
      fprintf(stderr, "Error encoding frame\n"); 
      exit(1); 
     } 
     if (got_output) { 
      fwrite(pkt.data, 1, pkt.size, file); 
      av_packet_unref(&pkt); 
     } 
    } while (got_output); 
    fwrite(endcode, 1, sizeof(endcode), file); 
    fclose(file); 
    avcodec_close(c); 
    av_free(c); 
    av_freep(&frame->data[0]); 
    av_frame_free(&frame); 
} 

/* 
Encode one frame from an RGB24 input and save it to the output file. 
Must be called after ffmpeg_encoder_start, and ffmpeg_encoder_finish 
must be called after the last call to this function. 
*/ 
void ffmpeg_encoder_encode_frame(uint8_t *rgb) { 
    int ret, got_output; 
    ffmpeg_encoder_set_frame_yuv_from_rgb(rgb); 
    av_init_packet(&pkt); 
    pkt.data = NULL; 
    pkt.size = 0; 
    switch (frame->pts % 4) { 
     case 0: 
      frame->key_frame = 1; 
      frame->pict_type = AV_PICTURE_TYPE_I; 
     break; 
     case 1: 
     case 3: 
      frame->key_frame = 0; 
      frame->pict_type = AV_PICTURE_TYPE_P; 
     break; 
     case 2: 
      frame->key_frame = 0; 
      frame->pict_type = AV_PICTURE_TYPE_B; 
     break; 
    } 
    ret = avcodec_encode_video2(c, &pkt, frame, &got_output); 
    if (ret < 0) { 
     fprintf(stderr, "Error encoding frame\n"); 
     exit(1); 
    } 
    if (got_output) { 
     fwrite(pkt.data, 1, pkt.size, file); 
     av_packet_unref(&pkt); 
    } 
} 

/* Represents the main loop of an application which generates one frame per loop. */ 
static void encode_example(const char *filename, int codec_id) { 
    int pts; 
    int width = 320; 
    int height = 240; 
    uint8_t *rgb = NULL; 
    ffmpeg_encoder_start(filename, codec_id, 25, width, height); 
    for (pts = 0; pts < 100; pts++) { 
     frame->pts = pts; 
     rgb = generate_rgb(width, height, pts, rgb); 
     ffmpeg_encoder_encode_frame(rgb); 
    } 
    ffmpeg_encoder_finish(); 
} 

int main(void) { 
    avcodec_register_all(); 
    encode_example("tmp.h264", AV_CODEC_ID_H264); 
    encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO); 
    /* TODO: is this encoded correctly? Possible to view it without container? */ 
    /*encode_example("tmp.vp8", AV_CODEC_ID_VP8);*/ 
    return 0; 
} 

उबंटू 15.10 पर परीक्षण किया गया। GitHub upstream

क्या आप वास्तव में ऐसा करना चाहते हैं?

ज्यादातर मामलों में, आप AVCodecContext के वैश्विक मानकों को नियंत्रित करने से बेहतर हैं।

एफएफएमपीजी स्मार्ट चीजों को एक कीफ्रेम का उपयोग करने की तरह करता है यदि नया फ्रेम पिछले एक से बिल्कुल अलग है, और अंतर एन्कोडिंग से ज्यादा नहीं प्राप्त किया जाएगा।

उदाहरण के लिए, अगर हम सिर्फ सेट:

c->keyint_min = 600; 

तो हम वास्तव में 4 ऊपर के उदाहरण है, जो तार्किक है के बाद से वहाँ 4 उत्पन्न वीडियो पर अचानक फ्रेम परिवर्तन कर रहे हैं पर कुंजी-फ्रेम मिलता है।